Example #1
0
        public void UpdateIndex()
        {
            Log.V(Log.TagView, "Re-indexing view: %s", name);
            System.Diagnostics.Debug.Assert((mapBlock != null));
            if (GetViewId() <= 0)
            {
                string msg = string.Format("getViewId() < 0");
                throw new CouchbaseLiteException(msg, new Status(Status.NotFound));
            }
            database.BeginTransaction();
            Status result = new Status(Status.InternalServerError);
            Cursor cursor = null;

            try
            {
                long lastSequence  = GetLastSequenceIndexed();
                long dbMaxSequence = database.GetLastSequenceNumber();
                if (lastSequence == dbMaxSequence)
                {
                    // nothing to do (eg,  kCBLStatusNotModified)
                    Log.V(Log.TagView, "lastSequence (%s) == dbMaxSequence (%s), nothing to do", lastSequence
                          , dbMaxSequence);
                    result.SetCode(Status.NotModified);
                    return;
                }
                // First remove obsolete emitted results from the 'maps' table:
                long sequence = lastSequence;
                if (lastSequence < 0)
                {
                    string msg = string.Format("lastSequence < 0 (%s)", lastSequence);
                    throw new CouchbaseLiteException(msg, new Status(Status.InternalServerError));
                }
                if (lastSequence == 0)
                {
                    // If the lastSequence has been reset to 0, make sure to remove
                    // any leftover rows:
                    string[] whereArgs = new string[] { Sharpen.Extensions.ToString(GetViewId()) };
                    database.GetDatabase().Delete("maps", "view_id=?", whereArgs);
                }
                else
                {
                    // Delete all obsolete map results (ones from since-replaced
                    // revisions):
                    string[] args = new string[] { Sharpen.Extensions.ToString(GetViewId()), System.Convert.ToString
                                                       (lastSequence), System.Convert.ToString(lastSequence) };
                    database.GetDatabase().ExecSQL("DELETE FROM maps WHERE view_id=? AND sequence IN ("
                                                   + "SELECT parent FROM revs WHERE sequence>? " + "AND parent>0 AND parent<=?)",
                                                   args);
                }
                int deleted = 0;
                cursor = database.GetDatabase().RawQuery("SELECT changes()", null);
                cursor.MoveToNext();
                deleted = cursor.GetInt(0);
                cursor.Close();
                // This is the emit() block, which gets called from within the
                // user-defined map() block
                // that's called down below.
                AbstractTouchMapEmitBlock emitBlock = new _AbstractTouchMapEmitBlock_428(this);
                //Log.v(Log.TAG_VIEW, "    emit(" + keyJson + ", "
                //        + valueJson + ")");
                // find a better way to propagate this back
                // Now scan every revision added since the last time the view was
                // indexed:
                string[] selectArgs = new string[] { System.Convert.ToString(lastSequence) };
                cursor = database.GetDatabase().RawQuery("SELECT revs.doc_id, sequence, docid, revid, json, no_attachments FROM revs, docs "
                                                         + "WHERE sequence>? AND current!=0 AND deleted=0 " + "AND revs.doc_id = docs.doc_id "
                                                         + "ORDER BY revs.doc_id, revid DESC", selectArgs);
                long lastDocID = 0;
                bool keepGoing = cursor.MoveToNext();
                while (keepGoing)
                {
                    long docID = cursor.GetLong(0);
                    if (docID != lastDocID)
                    {
                        // Only look at the first-iterated revision of any document,
                        // because this is the
                        // one with the highest revid, hence the "winning" revision
                        // of a conflict.
                        lastDocID = docID;
                        // Reconstitute the document as a dictionary:
                        sequence = cursor.GetLong(1);
                        string docId = cursor.GetString(2);
                        if (docId.StartsWith("_design/"))
                        {
                            // design docs don't get indexed!
                            keepGoing = cursor.MoveToNext();
                            continue;
                        }
                        string revId         = cursor.GetString(3);
                        byte[] json          = cursor.GetBlob(4);
                        bool   noAttachments = cursor.GetInt(5) > 0;
                        while ((keepGoing = cursor.MoveToNext()) && cursor.GetLong(0) == docID)
                        {
                        }
                        // Skip rows with the same doc_id -- these are losing conflicts.
                        if (lastSequence > 0)
                        {
                            // Find conflicts with documents from previous indexings.
                            string[] selectArgs2 = new string[] { System.Convert.ToString(docID), System.Convert.ToString
                                                                      (lastSequence) };
                            Cursor cursor2 = database.GetDatabase().RawQuery("SELECT revid, sequence FROM revs "
                                                                             + "WHERE doc_id=? AND sequence<=? AND current!=0 AND deleted=0 " + "ORDER BY revID DESC "
                                                                             + "LIMIT 1", selectArgs2);
                            if (cursor2.MoveToNext())
                            {
                                string oldRevId = cursor2.GetString(0);
                                // This is the revision that used to be the 'winner'.
                                // Remove its emitted rows:
                                long     oldSequence = cursor2.GetLong(1);
                                string[] args        = new string[] { Sharpen.Extensions.ToString(GetViewId()), System.Convert.ToString
                                                                          (oldSequence) };
                                database.GetDatabase().ExecSQL("DELETE FROM maps WHERE view_id=? AND sequence=?",
                                                               args);
                                if (RevisionInternal.CBLCompareRevIDs(oldRevId, revId) > 0)
                                {
                                    // It still 'wins' the conflict, so it's the one that
                                    // should be mapped [again], not the current revision!
                                    revId    = oldRevId;
                                    sequence = oldSequence;
                                    string[] selectArgs3 = new string[] { System.Convert.ToString(sequence) };
                                    json = Utils.ByteArrayResultForQuery(database.GetDatabase(), "SELECT json FROM revs WHERE sequence=?"
                                                                         , selectArgs3);
                                }
                            }
                        }
                        // Get the document properties, to pass to the map function:
                        EnumSet <Database.TDContentOptions> contentOptions = EnumSet.NoneOf <Database.TDContentOptions
                                                                                             >();
                        if (noAttachments)
                        {
                            contentOptions.AddItem(Database.TDContentOptions.TDNoAttachments);
                        }
                        IDictionary <string, object> properties = database.DocumentPropertiesFromJSON(json
                                                                                                      , docId, revId, false, sequence, contentOptions);
                        if (properties != null)
                        {
                            // Call the user-defined map() to emit new key/value
                            // pairs from this revision:
                            emitBlock.SetSequence(sequence);
                            mapBlock.Map(properties, emitBlock);
                        }
                    }
                }
                // Finally, record the last revision sequence number that was
                // indexed:
                ContentValues updateValues = new ContentValues();
                updateValues.Put("lastSequence", dbMaxSequence);
                string[] whereArgs_1 = new string[] { Sharpen.Extensions.ToString(GetViewId()) };
                database.GetDatabase().Update("views", updateValues, "view_id=?", whereArgs_1);
                // FIXME actually count number added :)
                Log.V(Log.TagView, "Finished re-indexing view: %s " + " up to sequence %s" + " (deleted %s added ?)"
                      , name, dbMaxSequence, deleted);
                result.SetCode(Status.Ok);
            }
            catch (SQLException e)
            {
                throw new CouchbaseLiteException(e, new Status(Status.DbError));
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
                if (!result.IsSuccessful())
                {
                    Log.W(Log.TagView, "Failed to rebuild view %s.  Result code: %d", name, result.GetCode
                              ());
                }
                if (database != null)
                {
                    database.EndTransaction(result.IsSuccessful());
                }
            }
        }
Example #2
0
		public RevisionInternal LoadRevisionBody(RevisionInternal rev, EnumSet<Database.TDContentOptions
			> contentOptions)
		{
			if (rev.GetBody() != null && contentOptions == EnumSet.NoneOf<Database.TDContentOptions
				>() && rev.GetSequence() != 0)
			{
				return rev;
			}
			System.Diagnostics.Debug.Assert(((rev.GetDocId() != null) && (rev.GetRevId() != null
				)));
			Cursor cursor = null;
			Status result = new Status(Status.NotFound);
			try
			{
				// TODO: on ios this query is:
				// TODO: "SELECT sequence, json FROM revs WHERE doc_id=? AND revid=? LIMIT 1"
				string sql = "SELECT sequence, json FROM revs, docs WHERE revid=? AND docs.docid=? AND revs.doc_id=docs.doc_id LIMIT 1";
				string[] args = new string[] { rev.GetRevId(), rev.GetDocId() };
				cursor = database.RawQuery(sql, args);
				if (cursor.MoveToNext())
				{
					result.SetCode(Status.Ok);
					rev.SetSequence(cursor.GetLong(0));
					ExpandStoredJSONIntoRevisionWithAttachments(cursor.GetBlob(1), rev, contentOptions
						);
				}
			}
			catch (SQLException e)
			{
				Log.E(Database.Tag, "Error loading revision body", e);
				throw new CouchbaseLiteException(Status.InternalServerError);
			}
			finally
			{
				if (cursor != null)
				{
					cursor.Close();
				}
			}
			if (result.GetCode() == Status.NotFound)
			{
				throw new CouchbaseLiteException(result);
			}
			return rev;
		}
Example #3
0
        internal Replication ReplicationWithProperties(IDictionary <string, object> properties)
        {
            // Extract the parameters from the JSON request body:
            // http://wiki.apache.org/couchdb/Replication

            bool push, createTarget;
            var  results = new Dictionary <string, object>()
            {
                { "database", null },
                { "remote", null },
                { "headers", null },
                { "authorizer", null }
            };

            Status result = ParseReplicationProperties(properties, out push, out createTarget, results);

            if (result.IsError)
            {
                throw new CouchbaseLiteException(result.GetCode());
            }

            object continuousObj = properties.Get("continuous");
            bool   continuous    = false;

            if (continuousObj is bool)
            {
                continuous = (bool)continuousObj;
            }

            var         scheduler = new SingleTaskThreadpoolScheduler();
            Replication rep       = null;

            if (push)
            {
                rep = new Pusher((Database)results["database"], (Uri)results["remote"], continuous, new TaskFactory(scheduler));
            }
            else
            {
                rep = new Puller((Database)results["database"], (Uri)results["remote"], continuous, new TaskFactory(scheduler));
            }

            rep.Filter         = properties.Get("filter") as string;
            rep.FilterParams   = properties.Get("query_params") as IDictionary <string, object>;
            rep.DocIds         = properties.Get("doc_ids") as IEnumerable <string>;
            rep.RequestHeaders = results.Get("headers") as IDictionary <string, object>;
            rep.Authenticator  = results.Get("authorizer") as IAuthenticator;
            if (push)
            {
                ((Pusher)rep).CreateTarget = createTarget;
            }

            var db = (Database)results["database"];

            // If this is a duplicate, reuse an existing replicator:
            var existing = db.ActiveReplicators.FirstOrDefault(x => x.LocalDatabase == rep.LocalDatabase &&
                                                               x.RemoteUrl == rep.RemoteUrl && x.IsPull == rep.IsPull &&
                                                               x.RemoteCheckpointDocID().Equals(rep.RemoteCheckpointDocID()));


            return(existing ?? rep);
        }
		/// <exception cref="System.Exception"></exception>
		public virtual void TestPusherDeletedDoc()
		{
			CountDownLatch replicationDoneSignal = new CountDownLatch(1);
			Uri remote = GetReplicationURL();
			string docIdTimestamp = System.Convert.ToString(Runtime.CurrentTimeMillis());
			// Create some documents:
			IDictionary<string, object> documentProperties = new Dictionary<string, object>();
			string doc1Id = string.Format("doc1-%s", docIdTimestamp);
			documentProperties.Put("_id", doc1Id);
			documentProperties.Put("foo", 1);
			documentProperties.Put("bar", false);
			Body body = new Body(documentProperties);
			RevisionInternal rev1 = new RevisionInternal(body, database);
			Status status = new Status();
			rev1 = database.PutRevision(rev1, null, false, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			documentProperties.Put("_rev", rev1.GetRevId());
			documentProperties.Put("UPDATED", true);
			documentProperties.Put("_deleted", true);
			RevisionInternal rev2 = database.PutRevision(new RevisionInternal(documentProperties
				, database), rev1.GetRevId(), false, status);
			NUnit.Framework.Assert.IsTrue(status.GetCode() >= 200 && status.GetCode() < 300);
			Replication repl = database.CreatePushReplication(remote);
			((Pusher)repl).SetCreateTarget(true);
			RunReplication(repl);
			// make sure doc1 is deleted
			Uri replicationUrlTrailing = new Uri(string.Format("%s/", remote.ToExternalForm()
				));
			Uri pathToDoc = new Uri(replicationUrlTrailing, doc1Id);
			Log.D(Tag, "Send http request to " + pathToDoc);
			CountDownLatch httpRequestDoneSignal = new CountDownLatch(1);
			BackgroundTask getDocTask = new _BackgroundTask_216(pathToDoc, httpRequestDoneSignal
				);
			getDocTask.Execute();
			Log.D(Tag, "Waiting for http request to finish");
			try
			{
				httpRequestDoneSignal.Await(300, TimeUnit.Seconds);
				Log.D(Tag, "http request finished");
			}
			catch (Exception e)
			{
				Sharpen.Runtime.PrintStackTrace(e);
			}
			Log.D(Tag, "testPusherDeletedDoc() finished");
		}
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        internal RevisionInternal LoadRevisionBody(RevisionInternal rev, DocumentContentOptions contentOptions)
        {
            if (rev.GetBody() != null && contentOptions == DocumentContentOptions.None && rev.GetSequence() != 0)
            {
                return rev;
            }

            if ((rev.GetDocId() == null) || (rev.GetRevId() == null))
            {
                Log.E(Database.Tag, "Error loading revision body");
                throw new CouchbaseLiteException(StatusCode.PreconditionFailed);
            }
            Cursor cursor = null;
            var result = new Status(StatusCode.NotFound);
            try
            {
                // TODO: on ios this query is:
                // TODO: "SELECT sequence, json FROM revs WHERE doc_id=@ AND revid=@ LIMIT 1"
                var sql = "SELECT sequence, json FROM revs, docs WHERE revid=? AND docs.docid=? AND revs.doc_id=docs.doc_id LIMIT 1";
                var args = new [] { rev.GetRevId(), rev.GetDocId() };

                cursor = StorageEngine.RawQuery(sql, CommandBehavior.SequentialAccess, args);
                if (cursor.MoveToNext())
                {
                    result.SetCode(StatusCode.Ok);
                    rev.SetSequence(cursor.GetLong(0));
                    ExpandStoredJSONIntoRevisionWithAttachments(cursor.GetBlob(1), rev, contentOptions);
                }
            }
            catch (SQLException e)
            {
                Log.E(Tag, "Error loading revision body", e);
                throw new CouchbaseLiteException(StatusCode.InternalServerError);
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
            }
            if (result.GetCode() == StatusCode.NotFound)
            {
                throw new CouchbaseLiteException(result.GetCode());
            }
            return rev;
        }
		/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
		private string CreateDocumentsForPushReplication(string docIdTimestamp)
		{
			string doc1Id;
			string doc2Id;
			// Create some documents:
			IDictionary<string, object> documentProperties = new Dictionary<string, object>();
			doc1Id = string.Format("doc1-%s", docIdTimestamp);
			documentProperties.Put("_id", doc1Id);
			documentProperties.Put("foo", 1);
			documentProperties.Put("bar", false);
			Body body = new Body(documentProperties);
			RevisionInternal rev1 = new RevisionInternal(body, database);
			Status status = new Status();
			rev1 = database.PutRevision(rev1, null, false, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			documentProperties.Put("_rev", rev1.GetRevId());
			documentProperties.Put("UPDATED", true);
			RevisionInternal rev2 = database.PutRevision(new RevisionInternal(documentProperties
				, database), rev1.GetRevId(), false, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			documentProperties = new Dictionary<string, object>();
			doc2Id = string.Format("doc2-%s", docIdTimestamp);
			documentProperties.Put("_id", doc2Id);
			documentProperties.Put("baz", 666);
			documentProperties.Put("fnord", true);
			database.PutRevision(new RevisionInternal(documentProperties, database), null, false
				, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			return doc1Id;
		}
 /// <exception cref="System.Exception"></exception>
 public virtual void TestAttachments()
 {
     string testAttachmentName = "test_attachment";
     BlobStore attachments = database.GetAttachments();
     NUnit.Framework.Assert.AreEqual(0, attachments.Count());
     NUnit.Framework.Assert.AreEqual(new HashSet<object>(), attachments.AllKeys());
     Status status = new Status();
     IDictionary<string, object> rev1Properties = new Dictionary<string, object>();
     rev1Properties.Put("foo", 1);
     rev1Properties.Put("bar", false);
     RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties, 
         database), null, false, status);
     NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
     byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1");
     database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
         ), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
     NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
     //We must set the no_attachments column for the rev to false, as we are using an internal
     //private API call above (database.insertAttachmentForSequenceWithNameAndType) which does
     //not set the no_attachments column on revs table
     try
     {
         ContentValues args = new ContentValues();
         args.Put("no_attachments=", false);
         database.GetDatabase().Update("revs", args, "sequence=?", new string[] { rev1.GetSequence
             ().ToString() });
     }
     catch (SQLException e)
     {
         Log.E(Database.Tag, "Error setting rev1 no_attachments to false", e);
         throw new CouchbaseLiteException(Status.InternalServerError);
     }
     Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
         );
     NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
     byte[] data = IOUtils.ToByteArray(attachment.GetContent());
     NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
     IDictionary<string, object> innerDict = new Dictionary<string, object>();
     innerDict.Put("content_type", "text/plain");
     innerDict.Put("digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=");
     innerDict.Put("length", 27);
     innerDict.Put("stub", true);
     innerDict.Put("revpos", 1);
     IDictionary<string, object> attachmentDict = new Dictionary<string, object>();
     attachmentDict.Put(testAttachmentName, innerDict);
     IDictionary<string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
         (rev1.GetSequence(), EnumSet.NoneOf<Database.TDContentOptions>());
     NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
     RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1
         .GetRevId(), EnumSet.NoneOf<Database.TDContentOptions>());
     IDictionary<string, object> gotAttachmentDict = (IDictionary<string, object>)gotRev1
         .GetProperties().Get("_attachments");
     NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
     // Check the attachment dict, with attachments included:
     Sharpen.Collections.Remove(innerDict, "stub");
     innerDict.Put("data", Base64.EncodeBytes(attach1));
     attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1
         .GetSequence(), EnumSet.Of(Database.TDContentOptions.TDIncludeAttachments));
     NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
     gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), EnumSet
         .Of(Database.TDContentOptions.TDIncludeAttachments));
     gotAttachmentDict = (IDictionary<string, object>)gotRev1.GetProperties().Get("_attachments"
         );
     NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
     // Add a second revision that doesn't update the attachment:
     IDictionary<string, object> rev2Properties = new Dictionary<string, object>();
     rev2Properties.Put("_id", rev1.GetDocId());
     rev2Properties.Put("foo", 2);
     rev2Properties.Put("bazz", false);
     RevisionInternal rev2 = database.PutRevision(new RevisionInternal(rev2Properties, 
         database), rev1.GetRevId(), false, status);
     NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
     database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1.GetSequence
         (), rev2.GetSequence());
     // Add a third revision of the same document:
     IDictionary<string, object> rev3Properties = new Dictionary<string, object>();
     rev3Properties.Put("_id", rev2.GetDocId());
     rev3Properties.Put("foo", 2);
     rev3Properties.Put("bazz", false);
     RevisionInternal rev3 = database.PutRevision(new RevisionInternal(rev3Properties, 
         database), rev2.GetRevId(), false, status);
     NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
     byte[] attach2 = Sharpen.Runtime.GetBytesForString("<html>And this is attach2</html>"
         );
     database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach2
         ), rev3.GetSequence(), testAttachmentName, "text/html", rev2.GetGeneration());
     // Check the 2nd revision's attachment:
     Attachment attachment2 = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName
         );
     NUnit.Framework.Assert.AreEqual("text/plain", attachment2.GetContentType());
     data = IOUtils.ToByteArray(attachment2.GetContent());
     NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
     // Check the 3rd revision's attachment:
     Attachment attachment3 = database.GetAttachmentForSequence(rev3.GetSequence(), testAttachmentName
         );
     NUnit.Framework.Assert.AreEqual("text/html", attachment3.GetContentType());
     data = IOUtils.ToByteArray(attachment3.GetContent());
     NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach2, data));
     IDictionary<string, object> attachmentDictForRev3 = (IDictionary<string, object>)
         database.GetAttachmentsDictForSequenceWithContent(rev3.GetSequence(), EnumSet.NoneOf
         <Database.TDContentOptions>()).Get(testAttachmentName);
     if (attachmentDictForRev3.ContainsKey("follows"))
     {
         if (((bool)attachmentDictForRev3.Get("follows")) == true)
         {
             throw new RuntimeException("Did not expected attachment dict 'follows' key to be true"
                 );
         }
         else
         {
             throw new RuntimeException("Did not expected attachment dict to have 'follows' key"
                 );
         }
     }
     // Examine the attachment store:
     NUnit.Framework.Assert.AreEqual(2, attachments.Count());
     ICollection<BlobKey> expected = new HashSet<BlobKey>();
     expected.AddItem(BlobStore.KeyForBlob(attach1));
     expected.AddItem(BlobStore.KeyForBlob(attach2));
     NUnit.Framework.Assert.AreEqual(expected, attachments.AllKeys());
     database.Compact();
     // This clears the body of the first revision
     NUnit.Framework.Assert.AreEqual(1, attachments.Count());
     ICollection<BlobKey> expected2 = new HashSet<BlobKey>();
     expected2.AddItem(BlobStore.KeyForBlob(attach2));
     NUnit.Framework.Assert.AreEqual(expected2, attachments.AllKeys());
 }
Example #8
0
 public CouchbaseLiteException(Exception innerException, Status status) : this(innerException, status.GetCode())
 {
     Code = status.GetCode();
 }
 /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
 private string CreateDocumentsForPushReplication(string docIdTimestamp, string attachmentType
     )
 {
     string doc1Id;
     string doc2Id;
     // Create some documents:
     IDictionary<string, object> doc1Properties = new Dictionary<string, object>();
     doc1Id = string.Format("doc1-%s", docIdTimestamp);
     doc1Properties.Put("_id", doc1Id);
     doc1Properties.Put("foo", 1);
     doc1Properties.Put("bar", false);
     Body body = new Body(doc1Properties);
     RevisionInternal rev1 = new RevisionInternal(body, database);
     Status status = new Status();
     rev1 = database.PutRevision(rev1, null, false, status);
     NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
     doc1Properties.Put("_rev", rev1.GetRevId());
     doc1Properties.Put("UPDATED", true);
     RevisionInternal rev2 = database.PutRevision(new RevisionInternal(doc1Properties, 
         database), rev1.GetRevId(), false, status);
     NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
     IDictionary<string, object> doc2Properties = new Dictionary<string, object>();
     doc2Id = string.Format("doc2-%s", docIdTimestamp);
     doc2Properties.Put("_id", doc2Id);
     doc2Properties.Put("baz", 666);
     doc2Properties.Put("fnord", true);
     database.PutRevision(new RevisionInternal(doc2Properties, database), null, false, 
         status);
     NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
     Document doc2 = database.GetDocument(doc2Id);
     UnsavedRevision doc2UnsavedRev = doc2.CreateRevision();
     if (attachmentType.Equals("png"))
     {
         InputStream attachmentStream = GetAsset("attachment.png");
         doc2UnsavedRev.SetAttachment("attachment.png", "image/png", attachmentStream);
     }
     else
     {
         if (attachmentType.Equals("txt"))
         {
             StringBuilder sb = new StringBuilder();
             for (int i = 0; i < 1000; i++)
             {
                 sb.Append("This is a large attachemnt.");
             }
             ByteArrayInputStream attachmentStream = new ByteArrayInputStream(Sharpen.Runtime.GetBytesForString
                 (sb.ToString()));
             doc2UnsavedRev.SetAttachment("attachment.txt", "text/plain", attachmentStream);
         }
         else
         {
             throw new RuntimeException("invalid attachment type: " + attachmentType);
         }
     }
     SavedRevision doc2Rev = doc2UnsavedRev.Save();
     NUnit.Framework.Assert.IsNotNull(doc2Rev);
     return doc1Id;
 }
Example #10
0
		public void UpdateIndex()
		{
			Log.V(Database.Tag, "Re-indexing view " + name + " ...");
			System.Diagnostics.Debug.Assert((mapBlock != null));
			if (GetViewId() < 0)
			{
				string msg = string.Format("getViewId() < 0");
				throw new CouchbaseLiteException(msg, new Status(Status.NotFound));
			}
			database.BeginTransaction();
			Status result = new Status(Status.InternalServerError);
			Cursor cursor = null;
			try
			{
				long lastSequence = GetLastSequenceIndexed();
				long dbMaxSequence = database.GetLastSequenceNumber();
				if (lastSequence == dbMaxSequence)
				{
					// nothing to do (eg,  kCBLStatusNotModified)
					string msg = string.Format("lastSequence (%d) == dbMaxSequence (%d), nothing to do"
						, lastSequence, dbMaxSequence);
					Log.D(Database.Tag, msg);
					result.SetCode(Status.Ok);
					return;
				}
				// First remove obsolete emitted results from the 'maps' table:
				long sequence = lastSequence;
				if (lastSequence < 0)
				{
					string msg = string.Format("lastSequence < 0 (%s)", lastSequence);
					throw new CouchbaseLiteException(msg, new Status(Status.InternalServerError));
				}
				if (lastSequence == 0)
				{
					// If the lastSequence has been reset to 0, make sure to remove
					// any leftover rows:
					string[] whereArgs = new string[] { Sharpen.Extensions.ToString(GetViewId()) };
					database.GetDatabase().Delete("maps", "view_id=?", whereArgs);
				}
				else
				{
					// Delete all obsolete map results (ones from since-replaced
					// revisions):
					string[] args = new string[] { Sharpen.Extensions.ToString(GetViewId()), System.Convert.ToString
						(lastSequence), System.Convert.ToString(lastSequence) };
					database.GetDatabase().ExecSQL("DELETE FROM maps WHERE view_id=? AND sequence IN ("
						 + "SELECT parent FROM revs WHERE sequence>? " + "AND parent>0 AND parent<=?)", 
						args);
				}
				int deleted = 0;
				cursor = database.GetDatabase().RawQuery("SELECT changes()", null);
				cursor.MoveToNext();
				deleted = cursor.GetInt(0);
				cursor.Close();
				// This is the emit() block, which gets called from within the
				// user-defined map() block
				// that's called down below.
				AbstractTouchMapEmitBlock emitBlock = new _AbstractTouchMapEmitBlock_446(this);
				// find a better way to propagate this back
				// Now scan every revision added since the last time the view was
				// indexed:
				string[] selectArgs = new string[] { System.Convert.ToString(lastSequence) };
				cursor = database.GetDatabase().RawQuery("SELECT revs.doc_id, sequence, docid, revid, json FROM revs, docs "
					 + "WHERE sequence>? AND current!=0 AND deleted=0 " + "AND revs.doc_id = docs.doc_id "
					 + "ORDER BY revs.doc_id, revid DESC", selectArgs);
				cursor.MoveToNext();
				long lastDocID = 0;
				while (!cursor.IsAfterLast())
				{
					long docID = cursor.GetLong(0);
					if (docID != lastDocID)
					{
						// Only look at the first-iterated revision of any document,
						// because this is the
						// one with the highest revid, hence the "winning" revision
						// of a conflict.
						lastDocID = docID;
						// Reconstitute the document as a dictionary:
						sequence = cursor.GetLong(1);
						string docId = cursor.GetString(2);
						if (docId.StartsWith("_design/"))
						{
							// design docs don't get indexed!
							cursor.MoveToNext();
							continue;
						}
						string revId = cursor.GetString(3);
						byte[] json = cursor.GetBlob(4);
						IDictionary<string, object> properties = database.DocumentPropertiesFromJSON(json
							, docId, revId, false, sequence, EnumSet.NoneOf<Database.TDContentOptions>());
						if (properties != null)
						{
							// Call the user-defined map() to emit new key/value
							// pairs from this revision:
							Log.V(Database.Tag, "  call map for sequence=" + System.Convert.ToString(sequence
								));
							emitBlock.SetSequence(sequence);
							mapBlock.Map(properties, emitBlock);
						}
					}
					cursor.MoveToNext();
				}
				// Finally, record the last revision sequence number that was
				// indexed:
				ContentValues updateValues = new ContentValues();
				updateValues.Put("lastSequence", dbMaxSequence);
				string[] whereArgs_1 = new string[] { Sharpen.Extensions.ToString(GetViewId()) };
				database.GetDatabase().Update("views", updateValues, "view_id=?", whereArgs_1);
				// FIXME actually count number added :)
				Log.V(Database.Tag, "...Finished re-indexing view " + name + " up to sequence " +
					 System.Convert.ToString(dbMaxSequence) + " (deleted " + deleted + " added " + "?"
					 + ")");
				result.SetCode(Status.Ok);
			}
			catch (SQLException e)
			{
				throw new CouchbaseLiteException(e, new Status(Status.DbError));
			}
			finally
			{
				if (cursor != null)
				{
					cursor.Close();
				}
				if (!result.IsSuccessful())
				{
					Log.W(Database.Tag, "Failed to rebuild view " + name + ": " + result.GetCode());
				}
				if (database != null)
				{
					database.EndTransaction(result.IsSuccessful());
				}
			}
		}
Example #11
0
        public void UpdateIndex()
        {
            Log.V(Database.Tag, "Re-indexing view " + name + " ...");
            System.Diagnostics.Debug.Assert((mapBlock != null));
            if (GetViewId() < 0)
            {
                string msg = string.Format("getViewId() < 0");
                throw new CouchbaseLiteException(msg, new Status(Status.NotFound));
            }
            database.BeginTransaction();
            Status result = new Status(Status.InternalServerError);
            Cursor cursor = null;

            try
            {
                long lastSequence  = GetLastSequenceIndexed();
                long dbMaxSequence = database.GetLastSequenceNumber();
                if (lastSequence == dbMaxSequence)
                {
                    // nothing to do (eg,  kCBLStatusNotModified)
                    string msg = string.Format("lastSequence (%d) == dbMaxSequence (%d), nothing to do"
                                               , lastSequence, dbMaxSequence);
                    Log.D(Database.Tag, msg);
                    result.SetCode(Status.Ok);
                    return;
                }
                // First remove obsolete emitted results from the 'maps' table:
                long sequence = lastSequence;
                if (lastSequence < 0)
                {
                    string msg = string.Format("lastSequence < 0 (%s)", lastSequence);
                    throw new CouchbaseLiteException(msg, new Status(Status.InternalServerError));
                }
                if (lastSequence == 0)
                {
                    // If the lastSequence has been reset to 0, make sure to remove
                    // any leftover rows:
                    string[] whereArgs = new string[] { Sharpen.Extensions.ToString(GetViewId()) };
                    database.GetDatabase().Delete("maps", "view_id=?", whereArgs);
                }
                else
                {
                    // Delete all obsolete map results (ones from since-replaced
                    // revisions):
                    string[] args = new string[] { Sharpen.Extensions.ToString(GetViewId()), System.Convert.ToString
                                                       (lastSequence), System.Convert.ToString(lastSequence) };
                    database.GetDatabase().ExecSQL("DELETE FROM maps WHERE view_id=? AND sequence IN ("
                                                   + "SELECT parent FROM revs WHERE sequence>? " + "AND parent>0 AND parent<=?)",
                                                   args);
                }
                int deleted = 0;
                cursor = database.GetDatabase().RawQuery("SELECT changes()", null);
                cursor.MoveToNext();
                deleted = cursor.GetInt(0);
                cursor.Close();
                // This is the emit() block, which gets called from within the
                // user-defined map() block
                // that's called down below.
                AbstractTouchMapEmitBlock emitBlock = new _AbstractTouchMapEmitBlock_446(this);
                // find a better way to propagate this back
                // Now scan every revision added since the last time the view was
                // indexed:
                string[] selectArgs = new string[] { System.Convert.ToString(lastSequence) };
                cursor = database.GetDatabase().RawQuery("SELECT revs.doc_id, sequence, docid, revid, json FROM revs, docs "
                                                         + "WHERE sequence>? AND current!=0 AND deleted=0 " + "AND revs.doc_id = docs.doc_id "
                                                         + "ORDER BY revs.doc_id, revid DESC", selectArgs);
                cursor.MoveToNext();
                long lastDocID = 0;
                while (!cursor.IsAfterLast())
                {
                    long docID = cursor.GetLong(0);
                    if (docID != lastDocID)
                    {
                        // Only look at the first-iterated revision of any document,
                        // because this is the
                        // one with the highest revid, hence the "winning" revision
                        // of a conflict.
                        lastDocID = docID;
                        // Reconstitute the document as a dictionary:
                        sequence = cursor.GetLong(1);
                        string docId = cursor.GetString(2);
                        if (docId.StartsWith("_design/"))
                        {
                            // design docs don't get indexed!
                            cursor.MoveToNext();
                            continue;
                        }
                        string revId = cursor.GetString(3);
                        byte[] json  = cursor.GetBlob(4);
                        IDictionary <string, object> properties = database.DocumentPropertiesFromJSON(json
                                                                                                      , docId, revId, false, sequence, EnumSet.NoneOf <Database.TDContentOptions>());
                        if (properties != null)
                        {
                            // Call the user-defined map() to emit new key/value
                            // pairs from this revision:
                            Log.V(Database.Tag, "  call map for sequence=" + System.Convert.ToString(sequence
                                                                                                     ));
                            emitBlock.SetSequence(sequence);
                            mapBlock.Map(properties, emitBlock);
                        }
                    }
                    cursor.MoveToNext();
                }
                // Finally, record the last revision sequence number that was
                // indexed:
                ContentValues updateValues = new ContentValues();
                updateValues.Put("lastSequence", dbMaxSequence);
                string[] whereArgs_1 = new string[] { Sharpen.Extensions.ToString(GetViewId()) };
                database.GetDatabase().Update("views", updateValues, "view_id=?", whereArgs_1);
                // FIXME actually count number added :)
                Log.V(Database.Tag, "...Finished re-indexing view " + name + " up to sequence " +
                      System.Convert.ToString(dbMaxSequence) + " (deleted " + deleted + " added " + "?"
                      + ")");
                result.SetCode(Status.Ok);
            }
            catch (SQLException e)
            {
                throw new CouchbaseLiteException(e, new Status(Status.DbError));
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
                if (!result.IsSuccessful())
                {
                    Log.W(Database.Tag, "Failed to rebuild view " + name + ": " + result.GetCode());
                }
                if (database != null)
                {
                    database.EndTransaction(result.IsSuccessful());
                }
            }
        }
Example #12
0
        public void TestCRUDOperations()
        {
            database.Changed += (sender, e) => {
                var changes = e.Changes.ToList();
                foreach (DocumentChange change in changes)
                {
                    var rev = change.AddedRevision;
                    Assert.IsNotNull(rev);
                    Assert.IsNotNull(rev.GetDocId());
                    Assert.IsNotNull(rev.GetRevId());
                    Assert.AreEqual(rev.GetDocId(), rev.GetProperties()["_id"]);
                    Assert.AreEqual(rev.GetRevId(), rev.GetProperties()["_rev"]);
                }
            };

            var privateUUID = database.PrivateUUID();
            var publicUUID  = database.PublicUUID();

            Log.V(Tag, "DB private UUID = '" + privateUUID + "', public UUID = '" +
                  publicUUID + "'");
            Assert.IsTrue(privateUUID.Length >= 20);
            Assert.IsTrue(publicUUID.Length >= 20);

            //create a document
            var documentProperties = new Dictionary <string, object>();

            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;
            documentProperties["baz"] = "touch";

            var body = new Body(documentProperties);
            var rev1 = new RevisionInternal(body);

            var status = new Status();

            rev1 = database.PutRevision(rev1, null, false, status);
            Log.V(Tag, "Created " + rev1);
            Assert.IsTrue(rev1.GetDocId().Length >= 10);
            Assert.IsTrue(rev1.GetRevId().StartsWith("1-"));

            //read it back
            var readRev = database.GetDocumentWithIDAndRev(rev1.GetDocId(), null,
                                                           DocumentContentOptions.None);

            Assert.IsNotNull(readRev);

            var userReadRevProps = UserProperties(readRev.GetProperties());
            var userBodyProps    = UserProperties(body.GetProperties());

            Assert.AreEqual(userReadRevProps.Count, userBodyProps.Count);
            foreach (var key in userReadRevProps.Keys)
            {
                Assert.AreEqual(userReadRevProps[key], userBodyProps[key]);
            }

            //now update it
            documentProperties           = (Dictionary <string, object>)readRev.GetProperties();
            documentProperties["status"] = "updated!";
            body = new Body(documentProperties);
            var rev2      = new RevisionInternal(body);
            var rev2input = rev2;

            rev2 = database.PutRevision(rev2, rev1.GetRevId(), false, status);
            Log.V(Tag, "Updated " + rev1);
            Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId());
            Assert.IsTrue(rev2.GetRevId().StartsWith("2-"));

            //read it back
            readRev = database.GetDocumentWithIDAndRev(rev2.GetDocId(), null,
                                                       DocumentContentOptions.None);
            Assert.IsNotNull(readRev);
            Assert.AreEqual(UserProperties(readRev.GetProperties()), UserProperties
                                (body.GetProperties()));

            // Try to update the first rev, which should fail:
            var gotExpectedError = false;

            try
            {
                database.PutRevision(rev2input, rev1.GetRevId(), false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.Conflict;
            }
            Assert.IsTrue(gotExpectedError);

            // Check the changes feed, with and without filters:
            var changeRevisions = database.ChangesSince(0, null, null);

            Log.V(Tag, "Changes = " + changeRevisions);
            Assert.AreEqual(1, changeRevisions.Count);

            changeRevisions = database.ChangesSince(0, null,
                                                    (revision, items) => "updated!".Equals(revision.Properties.Get("status")));
            Assert.AreEqual(1, changeRevisions.Count);

            changeRevisions = database.ChangesSince(0, null,
                                                    (revision, items) => "not updated!".Equals(revision.Properties.Get("status")));
            Assert.AreEqual(0, changeRevisions.Count);

            // Delete it:
            var revD = new RevisionInternal(rev2.GetDocId(), null, true);
            RevisionInternal revResult = null;

            gotExpectedError = false;
            try
            {
                revResult = database.PutRevision(revD, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.Conflict;
            }
            Assert.IsTrue(gotExpectedError);
            Assert.IsNull(revResult);

            revD = database.PutRevision(revD, rev2.GetRevId(), false, status);
            Assert.AreEqual(StatusCode.Ok, status.GetCode());
            Assert.AreEqual(revD.GetDocId(), rev2.GetDocId());
            Assert.IsTrue(revD.GetRevId().StartsWith("3-"));

            // Delete nonexistent doc:
            var revFake = new RevisionInternal("fake", null, true);

            gotExpectedError = false;
            try
            {
                database.PutRevision(revFake, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.NotFound;
            }
            Assert.IsTrue(gotExpectedError);

            // Read it back (should fail):
            readRev = database.GetDocumentWithIDAndRev(revD.GetDocId(), null,
                                                       DocumentContentOptions.None);
            Assert.IsNull(readRev);

            // Get Changes feed:
            changeRevisions = database.ChangesSince(0, null, null);
            Assert.IsTrue(changeRevisions.Count == 1);

            // Get Revision History:
            IList <RevisionInternal> history = database.GetRevisionHistory(revD);

            Assert.AreEqual(revD, history[0]);
            Assert.AreEqual(rev2, history[1]);
            Assert.AreEqual(rev1, history[2]);
        }
        public void TestCRUDOperations()
        {
            database.Changed += (sender, e) => {
                var changes = e.Changes.ToList();
                foreach (DocumentChange change in changes)
                {
                    var rev = change.AddedRevision;
                    Assert.IsNotNull(rev);
                    Assert.IsNotNull(rev.GetDocId());
                    Assert.IsNotNull(rev.GetRevId());
                    Assert.AreEqual(rev.GetDocId(), rev.GetProperties()["_id"]);
                    Assert.AreEqual(rev.GetDocId(), rev.GetProperties()["_rev"]);
                }
            };

            var privateUUID = database.PrivateUUID();
            var publicUUID = database.PublicUUID();
            Log.V(Tag, "DB private UUID = '" + privateUUID + "', public UUID = '" + 
                publicUUID + "'");
            Assert.IsTrue(privateUUID.Length >= 20);
            Assert.IsTrue(publicUUID.Length >= 20);

            //create a document
            var documentProperties = new Dictionary<string, object>();
            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;
            documentProperties["baz"] = "touch";

            var body = new Body(documentProperties);
            var rev1 = new RevisionInternal(body, database);

            var status = new Status();
            rev1 = database.PutRevision(rev1, null, false, status);
            Log.V(Tag, "Created " + rev1);
            Assert.IsTrue(rev1.GetDocId().Length >= 10);
            Assert.IsTrue(rev1.GetRevId().StartsWith("1-"));

            //read it back
            var readRev = database.GetDocumentWithIDAndRev(rev1.GetDocId(), null, 
                DocumentContentOptions.None);
            Assert.IsNotNull(readRev);

            var userReadRevProps = UserProperties(readRev.GetProperties());
            var userBodyProps = UserProperties(body.GetProperties());
            Assert.AreEqual(userReadRevProps.Count, userBodyProps.Count);
            foreach(var key in userReadRevProps.Keys) 
            {
                Assert.AreEqual(userReadRevProps[key], userBodyProps[key]);
            }

            //now update it
            documentProperties = (Dictionary<string, object>)readRev.GetProperties();
            documentProperties["status"] = "updated!";
            body = new Body(documentProperties);
            var rev2 = new RevisionInternal(body, database);
            var rev2input = rev2;
            rev2 = database.PutRevision(rev2, rev1.GetRevId(), false, status);
            Log.V(Tag, "Updated " + rev1);
            Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId());
            Assert.IsTrue(rev2.GetRevId().StartsWith("2-"));

            //read it back
            readRev = database.GetDocumentWithIDAndRev(rev2.GetDocId(), null, 
                DocumentContentOptions.None);
            Assert.IsNotNull(readRev);
            Assert.AreEqual(UserProperties(readRev.GetProperties()), UserProperties
                (body.GetProperties()));

            // Try to update the first rev, which should fail:
            var gotExpectedError = false;
            try
            {
                database.PutRevision(rev2input, rev1.GetRevId(), false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.Conflict;
            }
            Assert.IsTrue(gotExpectedError);

            // Check the changes feed, with and without filters:
            var changeRevisions = database.ChangesSince(0, null, null);

            Log.V(Tag, "Changes = " + changeRevisions);
            Assert.AreEqual(1, changeRevisions.Count);

            changeRevisions = database.ChangesSince(0, null, 
                (revision, items) => "updated!".Equals (revision.Properties.Get("status")));
            Assert.AreEqual(1, changeRevisions.Count);

            changeRevisions = database.ChangesSince(0, null, 
                (revision, items) => "not updated!".Equals (revision.Properties.Get("status")));
            Assert.AreEqual(0, changeRevisions.Count);

            // Delete it:
            var revD = new RevisionInternal(rev2.GetDocId(), null, true, database);
            RevisionInternal revResult = null;
            gotExpectedError = false;
            try
            {
                revResult = database.PutRevision(revD, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.Conflict;
            }
            Assert.IsTrue(gotExpectedError);
            Assert.IsNull(revResult);

            revD = database.PutRevision(revD, rev2.GetRevId(), false, status);
            Assert.AreEqual(StatusCode.Ok, status.GetCode());
            Assert.AreEqual(revD.GetDocId(), rev2.GetDocId());
            Assert.IsTrue(revD.GetRevId().StartsWith("3-"));
            
            // Delete nonexistent doc:
            var revFake = new RevisionInternal("fake", null, true, database);
            gotExpectedError = false;
            try
            {
                database.PutRevision(revFake, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.NotFound;
            }
            Assert.IsTrue(gotExpectedError);

            // Read it back (should fail):
            readRev = database.GetDocumentWithIDAndRev(revD.GetDocId(), null, 
                DocumentContentOptions.None);
            Assert.IsNull(readRev);

            // Get Changes feed:
            changeRevisions = database.ChangesSince(0, null, null);
            Assert.IsTrue(changeRevisions.Count == 1);

            // Get Revision History:
            IList<RevisionInternal> history = database.GetRevisionHistory(revD);
            Assert.AreEqual(revD, history[0]);
            Assert.AreEqual(rev2, history[1]);
            Assert.AreEqual(rev1, history[2]);
        }
        public void TestValidations()
        {
            ValidateDelegate validator = (newRevision, context) =>
            {
                Assert.IsNotNull(newRevision);
                Assert.IsNotNull(context);
                Assert.IsTrue(newRevision.Properties != null || newRevision.IsDeletion);

                validationCalled = true;

                bool hoopy = newRevision.IsDeletion || (newRevision.Properties.Get("towel") != null);
                Log.V(ValidationsTest.Tag, string.Format("--- Validating {0} --> {1}", newRevision.Properties, hoopy));
                if (!hoopy)
                {
                    context.Reject("Where's your towel?");
                }
                return(hoopy);
            };

            database.SetValidation("hoopy", validator);

            // POST a valid new document:
            IDictionary <string, object> props = new Dictionary <string, object>();

            props["name"]  = "Zaphod Beeblebrox";
            props["towel"] = "velvet";
            RevisionInternal rev    = new RevisionInternal(props, database);
            Status           status = new Status();

            validationCalled = false;
            rev = database.PutRevision(rev, null, false, status);
            Assert.IsTrue(validationCalled);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            // PUT a valid update:
            props["head_count"] = 3;
            rev.SetProperties(props);
            validationCalled = false;
            rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            Assert.IsTrue(validationCalled);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            // PUT an invalid update:
            Sharpen.Collections.Remove(props, "towel");
            rev.SetProperties(props);
            validationCalled = false;
            bool gotExpectedError = false;

            try
            {
                rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
            }
            Assert.IsTrue(validationCalled);
            Assert.IsTrue(gotExpectedError);

            // POST an invalid new document:
            props            = new Dictionary <string, object>();
            props["name"]    = "Vogon";
            props["poetry"]  = true;
            rev              = new RevisionInternal(props, database);
            validationCalled = false;
            gotExpectedError = false;
            try
            {
                rev = database.PutRevision(rev, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
            }
            Assert.IsTrue(validationCalled);
            Assert.IsTrue(gotExpectedError);

            // PUT a valid new document with an ID:
            props            = new Dictionary <string, object>();
            props["_id"]     = "ford";
            props["name"]    = "Ford Prefect";
            props["towel"]   = "terrycloth";
            rev              = new RevisionInternal(props, database);
            validationCalled = false;
            rev              = database.PutRevision(rev, null, false, status);
            Assert.IsTrue(validationCalled);
            Assert.AreEqual("ford", rev.GetDocId());

            // DELETE a document:
            rev = new RevisionInternal(rev.GetDocId(), rev.GetRevId(), true, database);
            Assert.IsTrue(rev.IsDeleted());
            validationCalled = false;
            rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            Assert.IsTrue(validationCalled);

            // PUT an invalid new document:
            props            = new Dictionary <string, object>();
            props["_id"]     = "petunias";
            props["name"]    = "Pot of Petunias";
            rev              = new RevisionInternal(props, database);
            validationCalled = false;
            gotExpectedError = false;
            try
            {
                rev = database.PutRevision(rev, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
            }
            Assert.IsTrue(validationCalled);
            Assert.IsTrue(gotExpectedError);
        }
Example #15
0
        internal void UpdateIndex()
        {
            Log.V(Database.Tag, "Re-indexing view " + Name + " ...");
            System.Diagnostics.Debug.Assert((Map != null));

            if (Id < 0)
            {
                var msg = string.Format("View.Id < 0");
                throw new CouchbaseLiteException(msg, new Status(StatusCode.NotFound));
            }

            Database.BeginTransaction();

            var    result = new Status(StatusCode.InternalServerError);
            Cursor cursor = null;

            try
            {
                var lastSequence  = LastSequenceIndexed;
                var dbMaxSequence = Database.LastSequenceNumber;

                if (lastSequence == dbMaxSequence)
                {
                    // nothing to do (eg,  kCBLStatusNotModified)
                    var msg = String.Format("lastSequence ({0}) == dbMaxSequence ({1}), nothing to do", lastSequence, dbMaxSequence);
                    Log.D(Database.Tag, msg);
                    result.SetCode(StatusCode.Ok);
                    return;
                }

                // First remove obsolete emitted results from the 'maps' table:
                var sequence = lastSequence;
                if (lastSequence < 0)
                {
                    var msg = string.Format("lastSequence < 0 ({0})", lastSequence);
                    throw new CouchbaseLiteException(msg, new Status(StatusCode.InternalServerError));
                }
                if (lastSequence == 0)
                {
                    // If the lastSequence has been reset to 0, make sure to remove
                    // any leftover rows:
                    var whereArgs = new string[] { Sharpen.Extensions.ToString(Id) };
                    Database.StorageEngine.Delete("maps", "view_id=@", whereArgs);
                }
                else
                {
                    // Delete all obsolete map results (ones from since-replaced
                    // revisions):
                    var args = new [] {
                        Id.ToString(),
                        lastSequence.ToString(),
                        lastSequence.ToString()
                    };

                    Database.StorageEngine.ExecSQL(
                        "DELETE FROM maps WHERE view_id=@ AND sequence IN ("
                        + "SELECT parent FROM revs WHERE sequence>@ " + "AND parent>0 AND parent<=@)",
                        args);
                }
                var deleted = 0;
                cursor = Database.StorageEngine.RawQuery("SELECT changes()", null); // TODO: Convert to ADO params.
                cursor.MoveToNext();
                deleted = cursor.GetInt(0);
                cursor.Close();

                // find a better way to propagate this back
                // Now scan every revision added since the last time the view was
                // indexed:
                var selectArgs = new[] { Convert.ToString(lastSequence) };
                cursor = Database.StorageEngine.RawQuery("SELECT revs.doc_id, sequence, docid, revid, json FROM revs, docs "
                                                         + "WHERE sequence>@ AND current!=0 AND deleted=0 " + "AND revs.doc_id = docs.doc_id "
                                                         + "ORDER BY revs.doc_id, revid DESC", CommandBehavior.SequentialAccess, selectArgs);
                cursor.MoveToNext();

                var lastDocID = 0L;
                while (!cursor.IsAfterLast())
                {
                    long docID = cursor.GetLong(0);
                    if (docID != lastDocID)
                    {
                        // Only look at the first-iterated revision of any document,
                        // because this is the
                        // one with the highest revid, hence the "winning" revision
                        // of a conflict.
                        lastDocID = docID;
                        // Reconstitute the document as a dictionary:
                        sequence = cursor.GetLong(1);
                        string docId = cursor.GetString(2);
                        if (docId.StartsWith("_design/", StringComparison.InvariantCultureIgnoreCase))
                        {
                            // design docs don't get indexed!
                            cursor.MoveToNext();
                            continue;
                        }
                        var revId      = cursor.GetString(3);
                        var json       = cursor.GetBlob(4);
                        var properties = Database.DocumentPropertiesFromJSON(
                            json, docId, revId, false, sequence, EnumSet.NoneOf <TDContentOptions>()
                            );
                        if (properties != null)
                        {
                            // Call the user-defined map() to emit new key/value
                            // pairs from this revision:
                            Log.V(Database.Tag, "  call map for sequence=" + System.Convert.ToString(sequence
                                                                                                     ));
                            // This is the emit() block, which gets called from within the
                            // user-defined map() block
                            // that's called down below.

                            var enclosingView = this;
                            var thisSequence  = sequence;
                            var map           = Map;

                            if (map == null)
                            {
                                throw new CouchbaseLiteException("Map function is missing.");
                            }

                            EmitDelegate emitBlock = (key, value) =>
                            {
                                // TODO: Do we need to do any null checks on key or value?
                                try
                                {
                                    var keyJson   = Manager.GetObjectMapper().WriteValueAsString(key);
                                    var valueJson = value == null ? null : Manager.GetObjectMapper().WriteValueAsString(value);
                                    Log.V(Database.Tag, String.Format("    emit({0}, {1})", keyJson, valueJson));

                                    var insertValues = new ContentValues();
                                    insertValues.Put("view_id", enclosingView.Id);
                                    insertValues["sequence"] = thisSequence;
                                    insertValues["key"]      = keyJson;
                                    insertValues["value"]    = valueJson;

                                    enclosingView.Database.StorageEngine.Insert("maps", null, insertValues);
                                }
                                catch (Exception e)
                                {
                                    Log.E(Database.Tag, "Error emitting", e);
                                }
                            };

                            map(properties, emitBlock);
                        }
                    }
                    cursor.MoveToNext();
                }
                // Finally, record the last revision sequence number that was
                // indexed:
                ContentValues updateValues = new ContentValues();
                updateValues["lastSequence"] = dbMaxSequence;
                var whereArgs_1 = new string[] { Sharpen.Extensions.ToString(Id) };
                Database.StorageEngine.Update("views", updateValues, "view_id=@", whereArgs_1);
                // FIXME actually count number added :)
                Log.V(Database.Tag, "...Finished re-indexing view " + Name + " up to sequence " +
                      System.Convert.ToString(dbMaxSequence) + " (deleted " + deleted + " added " + "?" + ")");
                result.SetCode(StatusCode.Ok);
            }
            catch (SQLException e)
            {
                throw new CouchbaseLiteException(e, new Status(StatusCode.DbError));
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
                if (!result.IsSuccessful())
                {
                    Log.W(Database.Tag, "Failed to rebuild view " + Name + ": " + result.GetCode());
                }
                if (Database != null)
                {
                    Database.EndTransaction(result.IsSuccessful());
                }
            }
        }
		/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
		public virtual void TestValidations()
		{
			Validator validator = new _Validator_19(this);
			database.SetValidation("hoopy", validator);
			// POST a valid new document:
			IDictionary<string, object> props = new Dictionary<string, object>();
			props.Put("name", "Zaphod Beeblebrox");
			props.Put("towel", "velvet");
			RevisionInternal rev = new RevisionInternal(props, database);
			Status status = new Status();
			validationCalled = false;
			rev = database.PutRevision(rev, null, false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			// PUT a valid update:
			props.Put("head_count", 3);
			rev.SetProperties(props);
			validationCalled = false;
			rev = database.PutRevision(rev, rev.GetRevId(), false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			// PUT an invalid update:
			Sharpen.Collections.Remove(props, "towel");
			rev.SetProperties(props);
			validationCalled = false;
			bool gotExpectedError = false;
			try
			{
				rev = database.PutRevision(rev, rev.GetRevId(), false, status);
			}
			catch (CouchbaseLiteException e)
			{
				gotExpectedError = (e.GetCBLStatus().GetCode() == Status.Forbidden);
			}
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
			// POST an invalid new document:
			props = new Dictionary<string, object>();
			props.Put("name", "Vogon");
			props.Put("poetry", true);
			rev = new RevisionInternal(props, database);
			validationCalled = false;
			gotExpectedError = false;
			try
			{
				rev = database.PutRevision(rev, null, false, status);
			}
			catch (CouchbaseLiteException e)
			{
				gotExpectedError = (e.GetCBLStatus().GetCode() == Status.Forbidden);
			}
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
			// PUT a valid new document with an ID:
			props = new Dictionary<string, object>();
			props.Put("_id", "ford");
			props.Put("name", "Ford Prefect");
			props.Put("towel", "terrycloth");
			rev = new RevisionInternal(props, database);
			validationCalled = false;
			rev = database.PutRevision(rev, null, false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.AreEqual("ford", rev.GetDocId());
			// DELETE a document:
			rev = new RevisionInternal(rev.GetDocId(), rev.GetRevId(), true, database);
			NUnit.Framework.Assert.IsTrue(rev.IsDeleted());
			validationCalled = false;
			rev = database.PutRevision(rev, rev.GetRevId(), false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
			// PUT an invalid new document:
			props = new Dictionary<string, object>();
			props.Put("_id", "petunias");
			props.Put("name", "Pot of Petunias");
			rev = new RevisionInternal(props, database);
			validationCalled = false;
			gotExpectedError = false;
			try
			{
				rev = database.PutRevision(rev, null, false, status);
			}
			catch (CouchbaseLiteException e)
			{
				gotExpectedError = (e.GetCBLStatus().GetCode() == Status.Forbidden);
			}
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
		}
Example #17
0
        /// <exception cref="System.Exception"></exception>
        public virtual void TestPutLargeAttachment()
        {
            string    testAttachmentName = "test_attachment";
            BlobStore attachments        = database.GetAttachments();

            attachments.DeleteBlobs();
            NUnit.Framework.Assert.AreEqual(0, attachments.Count());
            Status status = new Status();
            IDictionary <string, object> rev1Properties = new Dictionary <string, object>();

            rev1Properties.Put("foo", 1);
            rev1Properties.Put("bar", false);
            RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties,
                                                                              database), null, false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            StringBuilder largeAttachment = new StringBuilder();

            for (int i = 0; i < Database.kBigAttachmentLength; i++)
            {
                largeAttachment.Append("big attachment!");
            }
            byte[] attach1 = Sharpen.Runtime.GetBytesForString(largeAttachment.ToString());
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
                                                                                         ), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
            Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
                                                                      );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
            byte[] data = IOUtils.ToByteArray(attachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            EnumSet <Database.TDContentOptions> contentOptions = EnumSet.Of(Database.TDContentOptions
                                                                            .TDIncludeAttachments, Database.TDContentOptions.TDBigAttachmentsFollow);
            IDictionary <string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
                                                                         (rev1.GetSequence(), contentOptions);
            IDictionary <string, object> innerDict = (IDictionary <string, object>)attachmentDictForSequence
                                                     .Get(testAttachmentName);

            if (!innerDict.ContainsKey("stub"))
            {
                throw new RuntimeException("Expected attachment dict to have 'stub' key");
            }
            if (((bool)innerDict.Get("stub")) == false)
            {
                throw new RuntimeException("Expected attachment dict 'stub' key to be true");
            }
            if (!innerDict.ContainsKey("follows"))
            {
                throw new RuntimeException("Expected attachment dict to have 'follows' key");
            }
            RevisionInternal rev1WithAttachments = database.GetDocumentWithIDAndRev(rev1.GetDocId
                                                                                        (), rev1.GetRevId(), contentOptions);
            // Map<String,Object> rev1PropertiesPrime = rev1WithAttachments.getProperties();
            // rev1PropertiesPrime.put("foo", 2);
            IDictionary <string, object> rev1WithAttachmentsProperties = rev1WithAttachments.GetProperties
                                                                             ();
            IDictionary <string, object> rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1WithAttachmentsProperties.Get("_id"));
            rev2Properties.Put("foo", 2);
            RevisionInternal newRev = new RevisionInternal(rev2Properties, database);
            RevisionInternal rev2   = database.PutRevision(newRev, rev1WithAttachments.GetRevId
                                                               (), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1WithAttachments
                                                               .GetSequence(), rev2.GetSequence());
            // Check the 2nd revision's attachment:
            Attachment rev2FetchedAttachment = database.GetAttachmentForSequence(rev2.GetSequence
                                                                                     (), testAttachmentName);

            NUnit.Framework.Assert.AreEqual(attachment.GetLength(), rev2FetchedAttachment.GetLength
                                                ());
            NUnit.Framework.Assert.AreEqual(attachment.GetMetadata(), rev2FetchedAttachment.GetMetadata
                                                ());
            NUnit.Framework.Assert.AreEqual(attachment.GetContentType(), rev2FetchedAttachment
                                            .GetContentType());
            // Add a third revision of the same document:
            IDictionary <string, object> rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetProperties().Get("_id"));
            rev3Properties.Put("foo", 3);
            rev3Properties.Put("baz", false);
            RevisionInternal rev3 = new RevisionInternal(rev3Properties, database);

            rev3 = database.PutRevision(rev3, rev2.GetRevId(), false, status);
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach3 = Sharpen.Runtime.GetBytesForString("<html><blink>attach3</blink></html>"
                                                               );
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach3
                                                                                         ), rev3.GetSequence(), testAttachmentName, "text/html", rev3.GetGeneration());
            // Check the 3rd revision's attachment:
            Attachment rev3FetchedAttachment = database.GetAttachmentForSequence(rev3.GetSequence
                                                                                     (), testAttachmentName);

            data = IOUtils.ToByteArray(rev3FetchedAttachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach3, data));
            NUnit.Framework.Assert.AreEqual("text/html", rev3FetchedAttachment.GetContentType
                                                ());
            // TODO: why doesn't this work?
            // Assert.assertEquals(attach3.length, rev3FetchedAttachment.getLength());
            ICollection <BlobKey> blobKeys = database.GetAttachments().AllKeys();

            NUnit.Framework.Assert.AreEqual(2, blobKeys.Count);
            database.Compact();
            blobKeys = database.GetAttachments().AllKeys();
            NUnit.Framework.Assert.AreEqual(1, blobKeys.Count);
        }
        public void TestPusher()
        {
            var remote = GetReplicationURL();
            var docIdTimestamp = Convert.ToString(Runtime.CurrentTimeMillis());

            // Create some documents:
            var documentProperties = new Dictionary<string, object>();
            var doc1Id = string.Format("doc1-{0}", docIdTimestamp);
            documentProperties["_id"] = doc1Id;
            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;

            var body = new Body(documentProperties);
            var rev1 = new RevisionInternal(body, database);
            var status = new Status();
            rev1 = database.PutRevision(rev1, null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            documentProperties.Put("_rev", rev1.GetRevId());
            documentProperties["UPDATED"] = true;
            database.PutRevision(new RevisionInternal(documentProperties, database), rev1.GetRevId(), false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            documentProperties = new Dictionary<string, object>();
            var doc2Id = string.Format("doc2-{0}", docIdTimestamp);
            documentProperties["_id"] = doc2Id;
            documentProperties["baz"] = 666;
            documentProperties["fnord"] = true;

            database.PutRevision(new RevisionInternal(documentProperties, database), null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var doc2 = database.GetDocument(doc2Id);
            var doc2UnsavedRev = doc2.CreateRevision();
            var attachmentStream = GetAsset("attachment.png");
            doc2UnsavedRev.SetAttachment("attachment.png", "image/png", attachmentStream);
            var doc2Rev = doc2UnsavedRev.Save();

            Assert.IsNotNull(doc2Rev);

            const bool continuous = false;
            var repl = database.CreatePushReplication(remote);
            repl.Continuous = continuous;
            if (!IsSyncGateway(remote)) {
                repl.CreateTarget = true;
            }

            // Check the replication's properties:
            Assert.AreEqual(database, repl.LocalDatabase);
            Assert.AreEqual(remote, repl.RemoteUrl);
            Assert.IsFalse(repl.IsPull);
            Assert.IsFalse(repl.Continuous);
            Assert.IsNull(repl.Filter);
            Assert.IsNull(repl.FilterParams);
            Assert.IsNull(repl.DocIds);
            // TODO: CAssertNil(r1.headers); still not null!
            // Check that the replication hasn't started running:
            Assert.IsFalse(repl.IsRunning);
            Assert.AreEqual((int)repl.Status, (int)ReplicationStatus.Stopped);
            Assert.AreEqual(0, repl.CompletedChangesCount);
            Assert.AreEqual(0, repl.ChangesCount);
            Assert.IsNull(repl.LastError);

            RunReplication(repl);

            // TODO: Verify the foloowing 2 asserts. ChangesCount and CompletedChangesCount
            // should already be reset when the replicator stopped.
             Assert.IsTrue(repl.ChangesCount >= 2);
             Assert.IsTrue(repl.CompletedChangesCount >= 2);
            Assert.IsNull(repl.LastError);

            VerifyRemoteDocExists(remote, doc1Id);

            // Add doc3
            documentProperties = new Dictionary<string, object>();
            var doc3Id = string.Format("doc3-{0}", docIdTimestamp);
            var doc3 = database.GetDocument(doc3Id);
            documentProperties["bat"] = 677;
            doc3.PutProperties(documentProperties);

            // re-run push replication
            var repl2 = database.CreatePushReplication(remote);
            repl2.Continuous = continuous;
            if (!IsSyncGateway(remote))
            {
                repl2.CreateTarget = true;
            }

            var repl2CheckedpointId = repl2.RemoteCheckpointDocID();

            RunReplication(repl2);

            Assert.IsNull(repl2.LastError);

            // make sure trhe doc has been added
            VerifyRemoteDocExists(remote, doc3Id);

            Assert.AreEqual(repl2.LastSequence, database.LastSequenceWithCheckpointId(repl2CheckedpointId));

            System.Threading.Thread.Sleep(2000);
            var json = GetRemoteDoc(remote, repl2CheckedpointId);
            var remoteLastSequence = (string)json["lastSequence"];
            Assert.AreEqual(repl2.LastSequence, remoteLastSequence);

            Log.D(Tag, "testPusher() finished");
        }
Example #19
0
        /// <exception cref="System.Exception"></exception>
        public virtual void TestAttachments()
        {
            string    testAttachmentName = "test_attachment";
            BlobStore attachments        = database.GetAttachments();

            NUnit.Framework.Assert.AreEqual(0, attachments.Count());
            NUnit.Framework.Assert.AreEqual(new HashSet <object>(), attachments.AllKeys());
            Status status = new Status();
            IDictionary <string, object> rev1Properties = new Dictionary <string, object>();

            rev1Properties.Put("foo", 1);
            rev1Properties.Put("bar", false);
            RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties,
                                                                              database), null, false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1");
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
                                                                                         ), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
                                                                      );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
            byte[] data = IOUtils.ToByteArray(attachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            IDictionary <string, object> innerDict = new Dictionary <string, object>();

            innerDict.Put("content_type", "text/plain");
            innerDict.Put("digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=");
            innerDict.Put("length", 27);
            innerDict.Put("stub", true);
            innerDict.Put("revpos", 1);
            IDictionary <string, object> attachmentDict = new Dictionary <string, object>();

            attachmentDict.Put(testAttachmentName, innerDict);
            IDictionary <string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
                                                                         (rev1.GetSequence(), EnumSet.NoneOf <Database.TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1
                                                                        .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>());
            IDictionary <string, object> gotAttachmentDict = (IDictionary <string, object>)gotRev1
                                                             .GetProperties().Get("_attachments");

            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Check the attachment dict, with attachments included:
            Sharpen.Collections.Remove(innerDict, "stub");
            innerDict.Put("data", Base64.EncodeBytes(attach1));
            attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1
                                                                                          .GetSequence(), EnumSet.Of(Database.TDContentOptions.TDIncludeAttachments));
            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), EnumSet
                                                       .Of(Database.TDContentOptions.TDIncludeAttachments));
            gotAttachmentDict = (IDictionary <string, object>)gotRev1.GetProperties().Get("_attachments"
                                                                                          );
            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Add a second revision that doesn't update the attachment:
            IDictionary <string, object> rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1.GetDocId());
            rev2Properties.Put("foo", 2);
            rev2Properties.Put("bazz", false);
            RevisionInternal rev2 = database.PutRevision(new RevisionInternal(rev2Properties,
                                                                              database), rev1.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1.GetSequence
                                                                   (), rev2.GetSequence());
            // Add a third revision of the same document:
            IDictionary <string, object> rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetDocId());
            rev3Properties.Put("foo", 2);
            rev3Properties.Put("bazz", false);
            RevisionInternal rev3 = database.PutRevision(new RevisionInternal(rev3Properties,
                                                                              database), rev2.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach2 = Sharpen.Runtime.GetBytesForString("<html>And this is attach2</html>"
                                                               );
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach2
                                                                                         ), rev3.GetSequence(), testAttachmentName, "text/html", rev2.GetGeneration());
            // Check the 2nd revision's attachment:
            Attachment attachment2 = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment2.GetContentType());
            data = IOUtils.ToByteArray(attachment2.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            // Check the 3rd revision's attachment:
            Attachment attachment3 = database.GetAttachmentForSequence(rev3.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/html", attachment3.GetContentType());
            data = IOUtils.ToByteArray(attachment3.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach2, data));
            // Examine the attachment store:
            NUnit.Framework.Assert.AreEqual(2, attachments.Count());
            ICollection <BlobKey> expected = new HashSet <BlobKey>();

            expected.AddItem(BlobStore.KeyForBlob(attach1));
            expected.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected, attachments.AllKeys());
            database.Compact();
            // This clears the body of the first revision
            NUnit.Framework.Assert.AreEqual(1, attachments.Count());
            ICollection <BlobKey> expected2 = new HashSet <BlobKey>();

            expected2.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected2, attachments.AllKeys());
        }
 public CouchbaseLiteException (Exception innerException, Status status) : this(innerException, status.GetCode()) { Code = status.GetCode(); }
Example #21
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public virtual void TestCRUDOperations()
        {
            database.AddChangeListener(this);
            string privateUUID = database.PrivateUUID();
            string publicUUID  = database.PublicUUID();

            Log.V(Tag, "DB private UUID = '" + privateUUID + "', public UUID = '" + publicUUID
                  + "'");
            NUnit.Framework.Assert.IsTrue(privateUUID.Length >= 20);
            NUnit.Framework.Assert.IsTrue(publicUUID.Length >= 20);
            //create a document
            IDictionary <string, object> documentProperties = new Dictionary <string, object>();

            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;
            documentProperties["baz"] = "touch";
            Body             body   = new Body(documentProperties);
            RevisionInternal rev1   = new RevisionInternal(body, database);
            Status           status = new Status();

            rev1 = database.PutRevision(rev1, null, false, status);
            Log.V(Tag, "Created " + rev1);
            NUnit.Framework.Assert.IsTrue(rev1.GetDocId().Length >= 10);
            NUnit.Framework.Assert.IsTrue(rev1.GetRevId().StartsWith("1-"));
            //read it back
            RevisionInternal readRev = database.GetDocumentWithIDAndRev(rev1.GetDocId(), null
                                                                        , EnumSet.NoneOf <TDContentOptions>());

            NUnit.Framework.Assert.IsNotNull(readRev);
            IDictionary <string, object> readRevProps = readRev.Properties;

            NUnit.Framework.Assert.AreEqual(UserProperties(readRevProps), UserProperties(body
                                                                                         .Properties));
            //now update it
            documentProperties           = readRev.Properties;
            documentProperties["status"] = "updated!";
            body = new Body(documentProperties);
            RevisionInternal rev2      = new RevisionInternal(body, database);
            RevisionInternal rev2input = rev2;

            rev2 = database.PutRevision(rev2, rev1.GetRevId(), false, status);
            Log.V(Tag, "Updated " + rev1);
            NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId());
            NUnit.Framework.Assert.IsTrue(rev2.GetRevId().StartsWith("2-"));
            //read it back
            readRev = database.GetDocumentWithIDAndRev(rev2.GetDocId(), null, EnumSet.NoneOf <
                                                           TDContentOptions>());
            NUnit.Framework.Assert.IsNotNull(readRev);
            NUnit.Framework.Assert.AreEqual(UserProperties(readRev.Properties), UserProperties
                                                (body.Properties));
            // Try to update the first rev, which should fail:
            bool gotExpectedError = false;

            try
            {
                database.PutRevision(rev2input, rev1.GetRevId(), false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.Conflict;
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            // Check the changes feed, with and without filters:
            RevisionList changes = database.ChangesSince(0, null, null);

            Log.V(Tag, "Changes = " + changes);
            NUnit.Framework.Assert.AreEqual(1, changes.Count);
            changes = database.ChangesSince(0, null, (revision, items) => "updated!".Equals(revision.Properties.Get("status")));
            NUnit.Framework.Assert.AreEqual(1, changes.Count);
            changes = database.ChangesSince(0, null, (revision, items) => "not updated!".Equals(revision.Properties.Get("status")));
            NUnit.Framework.Assert.AreEqual(0, changes.Count);
            // Delete it:
            RevisionInternal revD = new RevisionInternal(rev2.GetDocId(), null, true, database
                                                         );
            RevisionInternal revResult = null;

            gotExpectedError = false;
            try
            {
                revResult = database.PutRevision(revD, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.Conflict;
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            NUnit.Framework.Assert.IsNull(revResult);
            revD = database.PutRevision(revD, rev2.GetRevId(), false, status);
            NUnit.Framework.Assert.AreEqual(StatusCode.Ok, status.GetCode());
            NUnit.Framework.Assert.AreEqual(revD.GetDocId(), rev2.GetDocId());
            NUnit.Framework.Assert.IsTrue(revD.GetRevId().StartsWith("3-"));
            // Delete nonexistent doc:
            RevisionInternal revFake = new RevisionInternal("fake", null, true, database);

            gotExpectedError = false;
            try
            {
                database.PutRevision(revFake, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.NotFound;
            }
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            // Read it back (should fail):
            readRev = database.GetDocumentWithIDAndRev(revD.GetDocId(), null, EnumSet.NoneOf <TDContentOptions>());
            NUnit.Framework.Assert.IsNull(readRev);
            // Get Changes feed
            changes = database.ChangesSince(0, null, null);
            NUnit.Framework.Assert.IsTrue(changes.Count == 1);
            // Get Revision History
            IList <RevisionInternal> history = database.GetRevisionHistory(revD);

            NUnit.Framework.Assert.AreEqual(revD, history[0]);
            NUnit.Framework.Assert.AreEqual(rev2, history[1]);
            NUnit.Framework.Assert.AreEqual(rev1, history[2]);
        }
        public void TestAttachments()
        {
            var testAttachmentName = "test_attachment";
            var attachments        = database.Attachments;

            Assert.AreEqual(0, attachments.Count());
            Assert.AreEqual(0, attachments.AllKeys().Count());

            var rev1Properties = new Dictionary <string, object>();

            rev1Properties["foo"] = 1;
            rev1Properties["bar"] = false;

            var status = new Status();
            var rev1   = database.PutRevision(
                new RevisionInternal(rev1Properties), null, false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var attach1 = Encoding.UTF8.GetBytes("This is the body of attach1");

            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach1),
                rev1.GetSequence(),
                testAttachmentName,
                "text/plain",
                rev1.GetGeneration());

            //We must set the no_attachments column for the rev to false, as we are using an internal
            //private API call above (database.insertAttachmentForSequenceWithNameAndType) which does
            //not set the no_attachments column on revs table
            try
            {
                var args = new ContentValues();
                args.Put("no_attachments", false);
                database.StorageEngine.Update(
                    "revs",
                    args,
                    "sequence=?",
                    new[] { rev1.GetSequence().ToString() }
                    );
            }
            catch (SQLException e)
            {
                Log.E(Tag, "Error setting rev1 no_attachments to false", e);
                throw new CouchbaseLiteException(StatusCode.InternalServerError);
            }

            var attachment = database.GetAttachmentForSequence(
                rev1.GetSequence(),
                testAttachmentName
                );

            Assert.AreEqual("text/plain", attachment.ContentType);
            var data = attachment.Content.ToArray();

            Assert.IsTrue(Arrays.Equals(attach1, data));

            attachment.Dispose();

            var innerDict = new Dictionary <string, object>();

            innerDict["content_type"] = "text/plain";
            innerDict["digest"]       = "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=";
            innerDict["length"]       = 27;
            innerDict["stub"]         = true;
            innerDict["revpos"]       = 1;

            var attachmentDict = new Dictionary <string, object>();

            attachmentDict[testAttachmentName] = innerDict;
            var attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1.GetSequence(), DocumentContentOptions.None);

            Assert.AreEqual(new SortedDictionary <string, object>(attachmentDict), new SortedDictionary <string, object>(attachmentDictForSequence));//Assert.AreEqual(1, attachmentDictForSequence.Count);
            var gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(),
                                                           rev1.GetRevId(), DocumentContentOptions.IncludeAttachments);
            var gotAttachmentDict = gotRev1.GetProperties()
                                    .Get("_attachments")
                                    .AsDictionary <string, object>();

            Assert.AreEqual(attachmentDict.Select(kvp => kvp.Key).OrderBy(k => k), gotAttachmentDict.Select(kvp => kvp.Key).OrderBy(k => k));

            // Check the attachment dict, with attachments included:
            innerDict.Remove("stub");
            innerDict.Put("data", Convert.ToBase64String(attach1));
            attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(
                rev1.GetSequence(), DocumentContentOptions.IncludeAttachments);
            Assert.AreEqual(new SortedDictionary <string, object>(attachmentDict[testAttachmentName].AsDictionary <string, object>()), new SortedDictionary <string, object>(attachmentDictForSequence[testAttachmentName].AsDictionary <string, object>()));

            gotRev1 = database.GetDocumentWithIDAndRev(
                rev1.GetDocId(), rev1.GetRevId(), DocumentContentOptions.IncludeAttachments);
            gotAttachmentDict = gotRev1.GetProperties()
                                .Get("_attachments")
                                .AsDictionary <string, object>()
                                .Get(testAttachmentName)
                                .AsDictionary <string, object>();
            Assert.AreEqual(innerDict.Select(kvp => kvp.Key).OrderBy(k => k), gotAttachmentDict.Select(kvp => kvp.Key).OrderBy(k => k));

            // Add a second revision that doesn't update the attachment:
            database.BeginTransaction();
            var rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1.GetDocId());
            rev2Properties["foo"]  = 2;
            rev2Properties["bazz"] = false;
            var rev2 = database.PutRevision(new RevisionInternal(rev2Properties), rev1.GetRevId(), false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            database.CopyAttachmentNamedFromSequenceToSequence(
                testAttachmentName, rev1.GetSequence(), rev2.GetSequence());
            database.EndTransaction(true);
            // Add a third revision of the same document:
            var rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetDocId());
            rev3Properties["foo"]  = 2;
            rev3Properties["bazz"] = false;
            database.BeginTransaction();
            var rev3 = database.PutRevision(new RevisionInternal(
                                                rev3Properties), rev2.GetRevId(), false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var attach2 = Encoding.UTF8.GetBytes("<html>And this is attach2</html>");

            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach2), rev3.GetSequence(),
                testAttachmentName, "text/html", rev2.GetGeneration());
            database.EndTransaction(true);
            // Check the 2nd revision's attachment:
            var attachment2 = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName);

            Assert.AreEqual("text/plain", attachment2.ContentType);

            data = attachment2.Content.ToArray();
            Assert.IsTrue(Arrays.Equals(attach1, data));

            attachment2.Dispose();

            // Check the 3rd revision's attachment:
            var attachment3 = database.GetAttachmentForSequence(rev3.GetSequence(), testAttachmentName);

            Assert.AreEqual("text/html", attachment3.ContentType);

            data = attachment3.Content.ToArray();
            Assert.IsTrue(Arrays.Equals(attach2, data));

            var attachmentDictForRev3 = database.GetAttachmentsDictForSequenceWithContent(rev3.GetSequence(), DocumentContentOptions.None)
                                        .Get(testAttachmentName)
                                        .AsDictionary <string, object>();

            if (attachmentDictForRev3.ContainsKey("follows"))
            {
                if (((bool)attachmentDictForRev3.Get("follows")) == true)
                {
                    throw new RuntimeException("Did not expected attachment dict 'follows' key to be true"
                                               );
                }
                else
                {
                    throw new RuntimeException("Did not expected attachment dict to have 'follows' key"
                                               );
                }
            }

            attachment3.Dispose();

            // Examine the attachment store:
            Assert.AreEqual(2, attachments.Count());

            var expected = new HashSet <BlobKey>();

            expected.AddItem(BlobStore.KeyForBlob(attach1));
            expected.AddItem(BlobStore.KeyForBlob(attach2));
            Assert.AreEqual(expected.Count, attachments.AllKeys().Count());

            foreach (var key in attachments.AllKeys())
            {
                Assert.IsTrue(expected.Contains(key));
            }

            database.Compact();

            // This clears the body of the first revision
            Assert.AreEqual(1, attachments.Count());

            var expected2 = new HashSet <BlobKey>();

            expected2.AddItem(BlobStore.KeyForBlob(attach2));
            Assert.AreEqual(expected2.Count, attachments.AllKeys().Count());

            foreach (var key in attachments.AllKeys())
            {
                Assert.IsTrue(expected2.Contains(key));
            }
        }
Example #23
0
 public void UpdateIndex()
 {
     Log.V(Log.TagView, "Re-indexing view: %s", name);
     System.Diagnostics.Debug.Assert((mapBlock != null));
     if (GetViewId() <= 0)
     {
         string msg = string.Format("getViewId() < 0");
         throw new CouchbaseLiteException(msg, new Status(Status.NotFound));
     }
     database.BeginTransaction();
     Status result = new Status(Status.InternalServerError);
     Cursor cursor = null;
     try
     {
         long lastSequence = GetLastSequenceIndexed();
         long dbMaxSequence = database.GetLastSequenceNumber();
         if (lastSequence == dbMaxSequence)
         {
             // nothing to do (eg,  kCBLStatusNotModified)
             Log.V(Log.TagView, "lastSequence (%s) == dbMaxSequence (%s), nothing to do", lastSequence
                 , dbMaxSequence);
             result.SetCode(Status.NotModified);
             return;
         }
         // First remove obsolete emitted results from the 'maps' table:
         long sequence = lastSequence;
         if (lastSequence < 0)
         {
             string msg = string.Format("lastSequence < 0 (%s)", lastSequence);
             throw new CouchbaseLiteException(msg, new Status(Status.InternalServerError));
         }
         if (lastSequence == 0)
         {
             // If the lastSequence has been reset to 0, make sure to remove
             // any leftover rows:
             string[] whereArgs = new string[] { Sharpen.Extensions.ToString(GetViewId()) };
             database.GetDatabase().Delete("maps", "view_id=?", whereArgs);
         }
         else
         {
             // Delete all obsolete map results (ones from since-replaced
             // revisions):
             string[] args = new string[] { Sharpen.Extensions.ToString(GetViewId()), System.Convert.ToString
                 (lastSequence), System.Convert.ToString(lastSequence) };
             database.GetDatabase().ExecSQL("DELETE FROM maps WHERE view_id=? AND sequence IN ("
                  + "SELECT parent FROM revs WHERE sequence>? " + "AND parent>0 AND parent<=?)", 
                 args);
         }
         int deleted = 0;
         cursor = database.GetDatabase().RawQuery("SELECT changes()", null);
         cursor.MoveToNext();
         deleted = cursor.GetInt(0);
         cursor.Close();
         // This is the emit() block, which gets called from within the
         // user-defined map() block
         // that's called down below.
         AbstractTouchMapEmitBlock emitBlock = new _AbstractTouchMapEmitBlock_428(this);
         //Log.v(Log.TAG_VIEW, "    emit(" + keyJson + ", "
         //        + valueJson + ")");
         // find a better way to propagate this back
         // Now scan every revision added since the last time the view was
         // indexed:
         string[] selectArgs = new string[] { System.Convert.ToString(lastSequence) };
         cursor = database.GetDatabase().RawQuery("SELECT revs.doc_id, sequence, docid, revid, json, no_attachments FROM revs, docs "
              + "WHERE sequence>? AND current!=0 AND deleted=0 " + "AND revs.doc_id = docs.doc_id "
              + "ORDER BY revs.doc_id, revid DESC", selectArgs);
         long lastDocID = 0;
         bool keepGoing = cursor.MoveToNext();
         while (keepGoing)
         {
             long docID = cursor.GetLong(0);
             if (docID != lastDocID)
             {
                 // Only look at the first-iterated revision of any document,
                 // because this is the
                 // one with the highest revid, hence the "winning" revision
                 // of a conflict.
                 lastDocID = docID;
                 // Reconstitute the document as a dictionary:
                 sequence = cursor.GetLong(1);
                 string docId = cursor.GetString(2);
                 if (docId.StartsWith("_design/"))
                 {
                     // design docs don't get indexed!
                     keepGoing = cursor.MoveToNext();
                     continue;
                 }
                 string revId = cursor.GetString(3);
                 byte[] json = cursor.GetBlob(4);
                 bool noAttachments = cursor.GetInt(5) > 0;
                 while ((keepGoing = cursor.MoveToNext()) && cursor.GetLong(0) == docID)
                 {
                 }
                 // Skip rows with the same doc_id -- these are losing conflicts.
                 if (lastSequence > 0)
                 {
                     // Find conflicts with documents from previous indexings.
                     string[] selectArgs2 = new string[] { System.Convert.ToString(docID), System.Convert.ToString
                         (lastSequence) };
                     Cursor cursor2 = database.GetDatabase().RawQuery("SELECT revid, sequence FROM revs "
                          + "WHERE doc_id=? AND sequence<=? AND current!=0 AND deleted=0 " + "ORDER BY revID DESC "
                          + "LIMIT 1", selectArgs2);
                     if (cursor2.MoveToNext())
                     {
                         string oldRevId = cursor2.GetString(0);
                         // This is the revision that used to be the 'winner'.
                         // Remove its emitted rows:
                         long oldSequence = cursor2.GetLong(1);
                         string[] args = new string[] { Sharpen.Extensions.ToString(GetViewId()), System.Convert.ToString
                             (oldSequence) };
                         database.GetDatabase().ExecSQL("DELETE FROM maps WHERE view_id=? AND sequence=?", 
                             args);
                         if (RevisionInternal.CBLCompareRevIDs(oldRevId, revId) > 0)
                         {
                             // It still 'wins' the conflict, so it's the one that
                             // should be mapped [again], not the current revision!
                             revId = oldRevId;
                             sequence = oldSequence;
                             string[] selectArgs3 = new string[] { System.Convert.ToString(sequence) };
                             json = Utils.ByteArrayResultForQuery(database.GetDatabase(), "SELECT json FROM revs WHERE sequence=?"
                                 , selectArgs3);
                         }
                     }
                 }
                 // Get the document properties, to pass to the map function:
                 EnumSet<Database.TDContentOptions> contentOptions = EnumSet.NoneOf<Database.TDContentOptions
                     >();
                 if (noAttachments)
                 {
                     contentOptions.AddItem(Database.TDContentOptions.TDNoAttachments);
                 }
                 IDictionary<string, object> properties = database.DocumentPropertiesFromJSON(json
                     , docId, revId, false, sequence, contentOptions);
                 if (properties != null)
                 {
                     // Call the user-defined map() to emit new key/value
                     // pairs from this revision:
                     emitBlock.SetSequence(sequence);
                     mapBlock.Map(properties, emitBlock);
                 }
             }
         }
         // Finally, record the last revision sequence number that was
         // indexed:
         ContentValues updateValues = new ContentValues();
         updateValues.Put("lastSequence", dbMaxSequence);
         string[] whereArgs_1 = new string[] { Sharpen.Extensions.ToString(GetViewId()) };
         database.GetDatabase().Update("views", updateValues, "view_id=?", whereArgs_1);
         // FIXME actually count number added :)
         Log.V(Log.TagView, "Finished re-indexing view: %s " + " up to sequence %s" + " (deleted %s added ?)"
             , name, dbMaxSequence, deleted);
         result.SetCode(Status.Ok);
     }
     catch (SQLException e)
     {
         throw new CouchbaseLiteException(e, new Status(Status.DbError));
     }
     finally
     {
         if (cursor != null)
         {
             cursor.Close();
         }
         if (!result.IsSuccessful())
         {
             Log.W(Log.TagView, "Failed to rebuild view %s.  Result code: %d", name, result.GetCode
                 ());
         }
         if (database != null)
         {
             database.EndTransaction(result.IsSuccessful());
         }
     }
 }
Example #24
0
        internal void UpdateIndex()
        {
            Log.V(Database.Tag, "Re-indexing view {0} ...", Name);
            System.Diagnostics.Debug.Assert((Map != null));

            if (Id <= 0)
            {
                var msg = string.Format("View.Id <= 0");
                throw new CouchbaseLiteException(msg, new Status(StatusCode.NotFound));
            }

            Database.BeginTransaction();

            var result = new Status(StatusCode.InternalServerError);
            Cursor cursor = null;
            try
            {
                var lastSequence = LastSequenceIndexed;
                var dbMaxSequence = Database.LastSequenceNumber;

                if (lastSequence == dbMaxSequence)
                {
                    // nothing to do (eg,  kCBLStatusNotModified)
                    Log.V(Database.Tag, "lastSequence ({0}) == dbMaxSequence ({1}), nothing to do", lastSequence, dbMaxSequence);
                    result.SetCode(StatusCode.NotModified);
                    return;
                }

                // First remove obsolete emitted results from the 'maps' table:
                var sequence = lastSequence;
                if (lastSequence < 0)
                {
                    var msg = string.Format("lastSequence < 0 ({0})", lastSequence);
                    throw new CouchbaseLiteException(msg, new Status(StatusCode.InternalServerError));
                }
                if (lastSequence == 0)
                {
                    // If the lastSequence has been reset to 0, make sure to remove
                    // any leftover rows:
                    var whereArgs = new string[] { Id.ToString() };
                    Database.StorageEngine.Delete("maps", "view_id=?", whereArgs);
                }
                else
                {
                    // Delete all obsolete map results (ones from since-replaced
                    // revisions):
                    var args = new [] {
                        Id.ToString(),
                        lastSequence.ToString(),
                        lastSequence.ToString()
                    };

                    Database.StorageEngine.ExecSQL(
                        "DELETE FROM maps WHERE view_id=? AND sequence IN ("
                        + "SELECT parent FROM revs WHERE sequence>? " + "AND parent>0 AND parent<=?)", 
                            args);
                }

                var deleted = 0;
                cursor = Database.StorageEngine.RawQuery("SELECT changes()");
                cursor.MoveToNext();
                deleted = cursor.GetInt(0);
                cursor.Close();

                // Find a better way to propagate this back
                // Now scan every revision added since the last time the view was indexed:
                var selectArgs = new[] { lastSequence.ToString() };
                cursor = Database.StorageEngine.RawQuery("SELECT revs.doc_id, sequence, docid, revid, json, no_attachments FROM revs, docs "
                    + "WHERE sequence>? AND current!=0 AND deleted=0 " 
                    + "AND revs.doc_id = docs.doc_id "
                    + "ORDER BY revs.doc_id, revid DESC", CommandBehavior.SequentialAccess, selectArgs);

                var lastDocID = 0L;
                var keepGoing = cursor.MoveToNext();
                while (keepGoing)
                {
                    long docID = cursor.GetLong(0);
                    if (docID != lastDocID)
                    {
                        // Only look at the first-iterated revision of any document,
                        // because this is the
                        // one with the highest revid, hence the "winning" revision
                        // of a conflict.
                        lastDocID = docID;
                        // Reconstitute the document as a dictionary:
                        sequence = cursor.GetLong(1);
                        string docId = cursor.GetString(2);
                        if (docId.StartsWith("_design/", StringComparison.InvariantCultureIgnoreCase))
                        {
                            // design docs don't get indexed!
                            keepGoing = cursor.MoveToNext();
                            continue;
                        }
                        var revId = cursor.GetString(3);
                        var json = cursor.GetBlob(4);

                        var noAttachments = cursor.GetInt(5) > 0;

                        // Skip rows with the same doc_id -- these are losing conflicts.
                        while ((keepGoing = cursor.MoveToNext()) && cursor.GetLong(0) == docID) { }

                        if (lastSequence > 0)
                        {
                            // Find conflicts with documents from previous indexings.
                            var selectArgs2 = new[] { Convert.ToString(docID), Convert.ToString(lastSequence) };
                            var cursor2 = Database.StorageEngine.RawQuery("SELECT revid, sequence FROM revs "
                                + "WHERE doc_id=? AND sequence<=? AND current!=0 AND deleted=0 " + "ORDER BY revID DESC "
                                + "LIMIT 1", selectArgs2);
                            if (cursor2.MoveToNext())
                            {
                                var oldRevId = cursor2.GetString(0);

                                // This is the revision that used to be the 'winner'.
                                // Remove its emitted rows:
                                var oldSequence = cursor2.GetLong(1);
                                var args = new[] { Sharpen.Extensions.ToString(Id), Convert.ToString(oldSequence) };
                                Database.StorageEngine.ExecSQL("DELETE FROM maps WHERE view_id=? AND sequence=?", args);

                                if (RevisionInternal.CBLCompareRevIDs(oldRevId, revId) > 0)
                                {
                                    // It still 'wins' the conflict, so it's the one that
                                    // should be mapped [again], not the current revision!
                                    revId = oldRevId;
                                    sequence = oldSequence;
                                    var selectArgs3 = new[] { Convert.ToString(sequence) };
                                    json = Misc.ByteArrayResultForQuery(
                                        Database.StorageEngine, 
                                        "SELECT json FROM revs WHERE sequence=?", 
                                        selectArgs3
                                    );
                                }
                            }
                        }
                        // Get the document properties, to pass to the map function:
                        var contentOptions = DocumentContentOptions.None;
                        if (noAttachments)
                        {
                            contentOptions |= DocumentContentOptions.NoAttachments;
                        }

                        var properties = Database.DocumentPropertiesFromJSON(
                            json, docId, revId, false, sequence, DocumentContentOptions.None
                        );
                        if (properties != null)
                        {
                            // Call the user-defined map() to emit new key/value
                            // pairs from this revision:

                            // This is the emit() block, which gets called from within the
                            // user-defined map() block
                            // that's called down below.

                            var enclosingView = this;
                            var thisSequence = sequence;
                            var map = Map;

                            if (map == null)
                                throw new CouchbaseLiteException("Map function is missing.");

                            EmitDelegate emitBlock = (key, value) =>
                            {
                                // TODO: Do we need to do any null checks on key or value?
                                try
                                {
                                    var keyJson = Manager.GetObjectMapper().WriteValueAsString(key);
                                    var valueJson = value == null ? null : Manager.GetObjectMapper().WriteValueAsString(value) ;

                                    var insertValues = new ContentValues();
                                    insertValues.Put("view_id", enclosingView.Id);
                                    insertValues["sequence"] = thisSequence;
                                    insertValues["key"] = keyJson;
                                    insertValues["value"] = valueJson;

                                    enclosingView.Database.StorageEngine.Insert("maps", null, insertValues);

                                    //
                                    // According to the issue #81, it is possible that there will be another
                                    // thread inserting a new revision to the database at the same time that 
                                    // the UpdateIndex operation is running. This event should be guarded by
                                    // the database transaction that the code begun but apparently it was not.
                                    // As a result, it is possible that dbMaxSequence will be out of date at 
                                    // this point and could cause the last indexed sequence to be out of track 
                                    // from the obsolete map entry cleanup operation, which eventually results 
                                    // to duplicated documents in the indexed map.
                                    //
                                    // To prevent the issue above, as a workaroubd, we need to make sure that 
                                    // we have the current max sequence of the indexed documents updated. 
                                    // This diverts from the CBL's Android code which doesn't have the same issue 
                                    // as the Android doesn't allow multiple thread to interact with the database 
                                    // at the same time.
                                    if (thisSequence > dbMaxSequence)
                                    {
                                        dbMaxSequence = thisSequence;
                                    }
                                }
                                catch (Exception e)
                                {
                                    Log.E(Database.Tag, "Error emitting", e);
                                }
                            };

                            map(properties, emitBlock);
                        }
                    }
                }

                // Finally, record the last revision sequence number that was 
                // indexed:
                var updateValues = new ContentValues();
                updateValues["lastSequence"] = dbMaxSequence;
                var whereArgs_1 = new string[] { Id.ToString() };
                Database.StorageEngine.Update("views", updateValues, "view_id=?", whereArgs_1);

                // FIXME actually count number added :)
                Log.V(Database.Tag, "...Finished re-indexing view {0} up to sequence {1} (deleted {2} added ?)", Name, Convert.ToString(dbMaxSequence), deleted);
                result.SetCode(StatusCode.Ok);
            }
            catch (Exception e)
            {
                throw new CouchbaseLiteException(e, new Status(StatusCode.DbError));
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
                if (!result.IsSuccessful)
                {
                    Log.W(Database.Tag, "Failed to rebuild view {0}:{1}", Name, result.GetCode());
                }
                if (Database != null)
                {
                    Database.EndTransaction(result.IsSuccessful);
                }
            }
        }
		public virtual void TestPusherDeletedDoc()
		{
            Assert.Fail(); // TODO.ZJG: Needs debugging, overflows stack.

			CountDownLatch replicationDoneSignal = new CountDownLatch(1);
			Uri remote = GetReplicationURL();
			string docIdTimestamp = System.Convert.ToString(Runtime.CurrentTimeMillis());
			// Create some documentsConvert
			IDictionary<string, object> documentProperties = new Dictionary<string, object>();
			string doc1Id = string.Format("doc1-{0}", docIdTimestamp);
			documentProperties["_id"] = doc1Id;
			documentProperties["foo"] = 1;
			documentProperties["bar"] = false;
			Body body = new Body(documentProperties);
			RevisionInternal rev1 = new RevisionInternal(body, database);
			Status status = new Status();
			rev1 = database.PutRevision(rev1, null, false, status);
            NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode());
            documentProperties["_rev"] = rev1.GetRevId();
			documentProperties["UPDATED"] = true;
			documentProperties["_deleted"] = true;
			RevisionInternal rev2 = database.PutRevision(new RevisionInternal(documentProperties
				, database), rev1.GetRevId(), false, status);
            NUnit.Framework.Assert.IsTrue((int)status.GetCode() >= 200 && (int)status.GetCode() < 300);
            var repl = database.CreatePushReplication(remote);
            ((Pusher)repl).CreateTarget = true;
			RunReplication(repl);
			// make sure doc1 is deleted
			Uri replicationUrlTrailing = new Uri(string.Format("{0}/", remote.ToString()
				));
			Uri pathToDoc = new Uri(replicationUrlTrailing, doc1Id);
			Log.D(Tag, "Send http request to " + pathToDoc);
			CountDownLatch httpRequestDoneSignal = new CountDownLatch(1);
            var getDocTask = Task.Factory.StartNew(()=>
                {
                    var httpclient = new HttpClient();
                    HttpResponseMessage response;
                    string responseString = null;
                    try
                    {
                        var responseTask = httpclient.GetAsync(pathToDoc.ToString());
                        responseTask.Wait();
                        response = responseTask.Result;
                        var statusLine = response.StatusCode;
                        Log.D(ReplicationTest.Tag, "statusLine " + statusLine);
                        Assert.AreEqual(HttpStatusCode.NotFound, statusLine.GetStatusCode());                        
                    }
                    catch (ProtocolViolationException e)
                    {
                        NUnit.Framework.Assert.IsNull(e, "Got ClientProtocolException: " + e.Message);
                    }
                    catch (IOException e)
                    {
                        NUnit.Framework.Assert.IsNull(e, "Got IOException: " + e.Message);
                    }
                    finally
                    {
                        httpRequestDoneSignal.CountDown();
                    }
                });
            getDocTask.Start();
			Log.D(Tag, "Waiting for http request to finish");
			try
			{
                httpRequestDoneSignal.Await(TimeSpan.FromSeconds(10));
				Log.D(Tag, "http request finished");
			}
			catch (Exception e)
			{
				Sharpen.Runtime.PrintStackTrace(e);
			}
			Log.D(Tag, "testPusherDeletedDoc() finished");
		}
		/// <exception cref="System.Exception"></exception>
		public virtual void TestPusher()
		{
			CountDownLatch replicationDoneSignal = new CountDownLatch(1);
			Uri remote = GetReplicationURL();
			string docIdTimestamp = System.Convert.ToString(Runtime.CurrentTimeMillis());
			// Create some documents:
			IDictionary<string, object> documentProperties = new Dictionary<string, object>();
			string doc1Id = string.Format("doc1-%s", docIdTimestamp);
			documentProperties.Put("_id", doc1Id);
			documentProperties.Put("foo", 1);
			documentProperties.Put("bar", false);
			Body body = new Body(documentProperties);
			RevisionInternal rev1 = new RevisionInternal(body, database);
			Status status = new Status();
			rev1 = database.PutRevision(rev1, null, false, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			documentProperties.Put("_rev", rev1.GetRevId());
			documentProperties.Put("UPDATED", true);
			RevisionInternal rev2 = database.PutRevision(new RevisionInternal(documentProperties
				, database), rev1.GetRevId(), false, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			documentProperties = new Dictionary<string, object>();
			string doc2Id = string.Format("doc2-%s", docIdTimestamp);
			documentProperties.Put("_id", doc2Id);
			documentProperties.Put("baz", 666);
			documentProperties.Put("fnord", true);
			database.PutRevision(new RevisionInternal(documentProperties, database), null, false
				, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			bool continuous = false;
			Replication repl = database.CreatePushReplication(remote);
			repl.SetContinuous(continuous);
			repl.SetCreateTarget(true);
			// Check the replication's properties:
			NUnit.Framework.Assert.AreEqual(database, repl.GetLocalDatabase());
			NUnit.Framework.Assert.AreEqual(remote, repl.GetRemoteUrl());
			NUnit.Framework.Assert.IsFalse(repl.IsPull());
			NUnit.Framework.Assert.IsFalse(repl.IsContinuous());
			NUnit.Framework.Assert.IsTrue(repl.ShouldCreateTarget());
			NUnit.Framework.Assert.IsNull(repl.GetFilter());
			NUnit.Framework.Assert.IsNull(repl.GetFilterParams());
			// TODO: CAssertNil(r1.doc_ids);
			// TODO: CAssertNil(r1.headers);
			// Check that the replication hasn't started running:
			NUnit.Framework.Assert.IsFalse(repl.IsRunning());
			NUnit.Framework.Assert.AreEqual(Replication.ReplicationStatus.ReplicationStopped, 
				repl.GetStatus());
			NUnit.Framework.Assert.AreEqual(0, repl.GetCompletedChangesCount());
			NUnit.Framework.Assert.AreEqual(0, repl.GetChangesCount());
			NUnit.Framework.Assert.IsNull(repl.GetLastError());
			RunReplication(repl);
			// make sure doc1 is there
			// TODO: make sure doc2 is there (refactoring needed)
			Uri replicationUrlTrailing = new Uri(string.Format("%s/", remote.ToExternalForm()
				));
			Uri pathToDoc = new Uri(replicationUrlTrailing, doc1Id);
			Log.D(Tag, "Send http request to " + pathToDoc);
			CountDownLatch httpRequestDoneSignal = new CountDownLatch(1);
			BackgroundTask getDocTask = new _BackgroundTask_122(pathToDoc, doc1Id, httpRequestDoneSignal
				);
			//Closes the connection.
			getDocTask.Execute();
			Log.D(Tag, "Waiting for http request to finish");
			try
			{
				httpRequestDoneSignal.Await(300, TimeUnit.Seconds);
				Log.D(Tag, "http request finished");
			}
			catch (Exception e)
			{
				Sharpen.Runtime.PrintStackTrace(e);
			}
			Log.D(Tag, "testPusher() finished");
		}
		public virtual void TestPusher()
		{
			var replicationDoneSignal = new CountDownLatch(1);
			var remote = GetReplicationURL();
			var docIdTimestamp = Convert.ToString(Runtime.CurrentTimeMillis());

			// Create some documents:
			var documentProperties = new Dictionary<string, object>();
			var doc1Id = string.Format("doc1-{0}", docIdTimestamp);
			documentProperties["_id"] = doc1Id;
			documentProperties["foo"] = 1;
			documentProperties["bar"] = false;

			var body = new Body(documentProperties);
			var rev1 = new RevisionInternal(body, database);
			var status = new Status();
			rev1 = database.PutRevision(rev1, null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

			documentProperties.Put("_rev", rev1.GetRevId());
			documentProperties["UPDATED"] = true;
			var rev2 = database.PutRevision(new RevisionInternal(documentProperties, database), rev1.GetRevId(), false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

			documentProperties = new Dictionary<string, object>();
			var doc2Id = string.Format("doc2-{0}", docIdTimestamp);
			documentProperties["_id"] = doc2Id;
			documentProperties["baz"] = 666;
			documentProperties["fnord"] = true;

			database.PutRevision(new RevisionInternal(documentProperties, database), null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

			var continuous = false;
			var repl = database.CreatePushReplication(remote);
            repl.Continuous = continuous;

            //repl.CreateTarget = false; 

			// Check the replication's properties:
			Assert.AreEqual(database, repl.LocalDatabase);
			Assert.AreEqual(remote, repl.RemoteUrl);
			Assert.IsFalse(repl.IsPull);
            Assert.IsFalse(repl.Continuous);
            //Assert.IsTrue(repl.CreateTarget);
			Assert.IsNull(repl.Filter);
			Assert.IsNull(repl.FilterParams);
			// TODO: CAssertNil(r1.doc_ids);
			// TODO: CAssertNil(r1.headers);
			// Check that the replication hasn't started running:
			Assert.IsFalse(repl.IsRunning);
            Assert.AreEqual((int)repl.Status, (int)ReplicationStatus.Stopped);
			Assert.AreEqual(0, repl.CompletedChangesCount);
			Assert.AreEqual(0, repl.ChangesCount);
			Assert.IsNull(repl.LastError);
            RunReplication(repl);
			// make sure doc1 is there
			// TODO: make sure doc2 is there (refactoring needed)
            var replicationUrlTrailing = new Uri(string.Format("{0}/", remote));
			var pathToDoc = new Uri(replicationUrlTrailing, doc1Id);
			Log.D(Tag, "Send http request to " + pathToDoc);
			var httpRequestDoneSignal = new CountDownLatch(1);
            var getDocTask = Task.Factory.StartNew(()=>
                {
                    var httpclient = new HttpClient();
                    HttpResponseMessage response;
                    string responseString = null;
                    try
                    {
                        var responseTask = httpclient.GetAsync(pathToDoc.ToString());
                        responseTask.Wait(TimeSpan.FromSeconds(10));
                        response = responseTask.Result;
                        var statusLine = response.StatusCode;
                        NUnit.Framework.Assert.IsTrue(statusLine == HttpStatusCode.OK);
                        if (statusLine == HttpStatusCode.OK)
                        {
                            var responseStringTask = response.Content.ReadAsStringAsync();
                            responseStringTask.Wait(TimeSpan.FromSeconds(10));
                            responseString = responseStringTask.Result;
                            NUnit.Framework.Assert.IsTrue(responseString.Contains(doc1Id));
                            Log.D(ReplicationTest.Tag, "result: " + responseString);
                        }
                        else
                        {
                            var statusReason = response.ReasonPhrase;
                            response.Dispose();
                            throw new IOException(statusReason);
                        }
                    }
                    catch (ProtocolViolationException e)
                    {
                        NUnit.Framework.Assert.IsNull(e, "Got ClientProtocolException: " + e.Message);
                    }
                    catch (IOException e)
                    {
                        NUnit.Framework.Assert.IsNull(e, "Got IOException: " + e.Message);
                    }
                    httpRequestDoneSignal.CountDown();
                });
			//Closes the connection.
			Log.D(Tag, "Waiting for http request to finish");
			try
			{
                var result = httpRequestDoneSignal.Await(TimeSpan.FromSeconds(10));
                Assert.IsTrue(result, "Could not retrieve the new doc from the sync gateway.");
				Log.D(Tag, "http request finished");
			}
			catch (Exception e)
			{
				Sharpen.Runtime.PrintStackTrace(e);
			}
			Log.D(Tag, "testPusher() finished");
		}
 public bool Run()
 {
     StringBuilder sb = new StringBuilder();
     for (int i = 0; i < this._enclosing.GetSizeOfAttachment(); i++)
     {
         sb.Append('1');
     }
     byte[] attach1 = Sharpen.Runtime.GetBytesForString(sb.ToString());
     try
     {
         Status status = new Status();
         for (int i_1 = 0; i_1 < this._enclosing.GetNumberOfDocuments(); i_1++)
         {
             IDictionary<string, object> rev1Properties = new Dictionary<string, object>();
             rev1Properties.Put("foo", 1);
             rev1Properties.Put("bar", false);
             RevisionInternal rev1 = this._enclosing.database.PutRevision(new RevisionInternal
                 (rev1Properties, this._enclosing.database), null, false, status);
             NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
             this._enclosing.database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream
                 (attach1), rev1.GetSequence(), Test3_CreateDocsWithAttachments._testAttachmentName
                 , "text/plain", rev1.GetGeneration());
             NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
         }
     }
     catch (Exception t)
     {
         Log.E(Test3_CreateDocsWithAttachments.Tag, "Document create with attachment failed"
             , t);
         return false;
     }
     return true;
 }
		/// <exception cref="System.Exception"></exception>
		public virtual void TestPutLargeAttachment()
		{
			string testAttachmentName = "test_attachment";
			BlobStore attachments = database.GetAttachments();
			attachments.DeleteBlobs();
			NUnit.Framework.Assert.AreEqual(0, attachments.Count());
			Status status = new Status();
			IDictionary<string, object> rev1Properties = new Dictionary<string, object>();
			rev1Properties.Put("foo", 1);
			rev1Properties.Put("bar", false);
			RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties, 
				database), null, false, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			StringBuilder largeAttachment = new StringBuilder();
			for (int i = 0; i < Database.kBigAttachmentLength; i++)
			{
				largeAttachment.Append("big attachment!");
			}
			byte[] attach1 = Sharpen.Runtime.GetBytesForString(largeAttachment.ToString());
			database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
				), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
			Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
				);
			NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
			byte[] data = IOUtils.ToByteArray(attachment.GetContent());
			NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
			EnumSet<Database.TDContentOptions> contentOptions = EnumSet.Of(Database.TDContentOptions
				.TDIncludeAttachments, Database.TDContentOptions.TDBigAttachmentsFollow);
			IDictionary<string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
				(rev1.GetSequence(), contentOptions);
			IDictionary<string, object> innerDict = (IDictionary<string, object>)attachmentDictForSequence
				.Get(testAttachmentName);
			if (!innerDict.ContainsKey("stub"))
			{
				throw new RuntimeException("Expected attachment dict to have 'stub' key");
			}
			if (((bool)innerDict.Get("stub")) == false)
			{
				throw new RuntimeException("Expected attachment dict 'stub' key to be true");
			}
			if (!innerDict.ContainsKey("follows"))
			{
				throw new RuntimeException("Expected attachment dict to have 'follows' key");
			}
			RevisionInternal rev1WithAttachments = database.GetDocumentWithIDAndRev(rev1.GetDocId
				(), rev1.GetRevId(), contentOptions);
			// Map<String,Object> rev1PropertiesPrime = rev1WithAttachments.getProperties();
			// rev1PropertiesPrime.put("foo", 2);
			IDictionary<string, object> rev1WithAttachmentsProperties = rev1WithAttachments.GetProperties
				();
			IDictionary<string, object> rev2Properties = new Dictionary<string, object>();
			rev2Properties.Put("_id", rev1WithAttachmentsProperties.Get("_id"));
			rev2Properties.Put("foo", 2);
			RevisionInternal newRev = new RevisionInternal(rev2Properties, database);
			RevisionInternal rev2 = database.PutRevision(newRev, rev1WithAttachments.GetRevId
				(), false, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1WithAttachments
				.GetSequence(), rev2.GetSequence());
			// Check the 2nd revision's attachment:
			Attachment rev2FetchedAttachment = database.GetAttachmentForSequence(rev2.GetSequence
				(), testAttachmentName);
			NUnit.Framework.Assert.AreEqual(attachment.GetLength(), rev2FetchedAttachment.GetLength
				());
			NUnit.Framework.Assert.AreEqual(attachment.GetMetadata(), rev2FetchedAttachment.GetMetadata
				());
			NUnit.Framework.Assert.AreEqual(attachment.GetContentType(), rev2FetchedAttachment
				.GetContentType());
			// Add a third revision of the same document:
			IDictionary<string, object> rev3Properties = new Dictionary<string, object>();
			rev3Properties.Put("_id", rev2.GetProperties().Get("_id"));
			rev3Properties.Put("foo", 3);
			rev3Properties.Put("baz", false);
			RevisionInternal rev3 = new RevisionInternal(rev3Properties, database);
			rev3 = database.PutRevision(rev3, rev2.GetRevId(), false, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			byte[] attach3 = Sharpen.Runtime.GetBytesForString("<html><blink>attach3</blink></html>"
				);
			database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach3
				), rev3.GetSequence(), testAttachmentName, "text/html", rev3.GetGeneration());
			// Check the 3rd revision's attachment:
			Attachment rev3FetchedAttachment = database.GetAttachmentForSequence(rev3.GetSequence
				(), testAttachmentName);
			data = IOUtils.ToByteArray(rev3FetchedAttachment.GetContent());
			NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach3, data));
			NUnit.Framework.Assert.AreEqual("text/html", rev3FetchedAttachment.GetContentType
				());
			// TODO: why doesn't this work?
			// Assert.assertEquals(attach3.length, rev3FetchedAttachment.getLength());
			ICollection<BlobKey> blobKeys = database.GetAttachments().AllKeys();
			NUnit.Framework.Assert.AreEqual(2, blobKeys.Count);
			database.Compact();
			blobKeys = database.GetAttachments().AllKeys();
			NUnit.Framework.Assert.AreEqual(1, blobKeys.Count);
		}
Example #30
0
        public void TestAttachments()
        {
            const string testAttachmentName = "test_attachment";
            var          attachments        = database.Attachments;

            Assert.AreEqual(0, attachments.Count());
            Assert.AreEqual(0, attachments.AllKeys().Count());

            var attach1 = Encoding.UTF8.GetBytes("This is the body of attach1");
            var props   = new Dictionary <string, object> {
                { "foo", 1 },
                { "bar", false },
                { "_attachments", CreateAttachmentsDict(attach1, testAttachmentName, "text/plain", false) }
            };

            Status           status = new Status();
            RevisionInternal rev1   = database.PutRevision(new RevisionInternal(props), null, false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var att = database.GetAttachmentForRevision(rev1, testAttachmentName, status);

            Assert.IsNotNull(att, "Couldn't get attachment:  Status {0}", status.GetCode());
            Assert.AreEqual(attach1, att.Content);
            Assert.AreEqual("text/plain", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            var itemDict = new Dictionary <string, object> {
                { "content_type", "text/plain" },
                { "digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=" },
                { "length", 27 },
                { "stub", true },
                { "revpos", 1 }
            };
            var attachmentDict = new Dictionary <string, object> {
                { testAttachmentName, itemDict }
            };
            var gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), DocumentContentOptions.None);

            AssertDictionariesAreEqual(attachmentDict, gotRev1.GetAttachments());

            itemDict.Remove("stub");
            itemDict["data"] = Convert.ToBase64String(attach1);
            gotRev1          = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), DocumentContentOptions.IncludeAttachments);
            var expandedRev = gotRev1.CopyWithDocID(rev1.GetDocId(), rev1.GetRevId());

            Assert.IsTrue(database.ExpandAttachments(expandedRev, 0, false, true, status));
            AssertDictionariesAreEqual(attachmentDict, expandedRev.GetAttachments());

            // Add a second revision that doesn't update the attachment:
            props = new Dictionary <string, object> {
                { "_id", rev1.GetDocId() },
                { "foo", 2 },
                { "bazz", false },
                { "_attachments", CreateAttachmentsStub(testAttachmentName) }
            };
            var rev2 = database.PutRevision(new RevisionInternal(props), rev1.GetRevId(), status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            // Add a third revision of the same document:
            var attach2 = Encoding.UTF8.GetBytes("<html>And this is attach2</html>");

            props = new Dictionary <string, object> {
                { "_id", rev2.GetDocId() },
                { "foo", 2 },
                { "bazz", false },
                { "_attachments", CreateAttachmentsDict(attach2, testAttachmentName, "text/html", false) }
            };
            var rev3 = database.PutRevision(new RevisionInternal(props), rev2.GetRevId(), status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());

            // Check the second revision's attachment
            att = database.GetAttachmentForRevision(rev2, testAttachmentName, status);
            Assert.IsNotNull(att, "Couldn't get attachment:  Status {0}", status.GetCode());
            Assert.AreEqual(attach1, att.Content);
            Assert.AreEqual("text/plain", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            expandedRev = rev2.CopyWithDocID(rev2.GetDocId(), rev2.GetRevId());
            Assert.IsTrue(database.ExpandAttachments(expandedRev, 2, false, true, status));
            AssertDictionariesAreEqual(new Dictionary <string, object> {
                { testAttachmentName, new Dictionary <string, object> {
                      { "stub", true },
                      { "revpos", 1 }
                  } }
            }, expandedRev.GetAttachments());

            // Check the 3rd revision's attachment:
            att = database.GetAttachmentForRevision(rev3, testAttachmentName, status);
            Assert.IsNotNull(att, "Couldn't get attachment:  Status {0}", status.GetCode());
            Assert.AreEqual(attach2, att.Content);
            Assert.AreEqual("text/html", att.ContentType);
            Assert.AreEqual(AttachmentEncoding.None, att.Encoding);

            expandedRev = rev3.CopyWithDocID(rev3.GetDocId(), rev3.GetRevId());
            Assert.IsTrue(database.ExpandAttachments(expandedRev, 2, false, true, status));
            attachmentDict = new Dictionary <string, object> {
                { testAttachmentName, new Dictionary <string, object> {
                      { "content_type", "text/html" },
                      { "data", "PGh0bWw+QW5kIHRoaXMgaXMgYXR0YWNoMjwvaHRtbD4=" },
                      { "digest", "sha1-s14XRTXlwvzYfjo1t1u0rjB+ZUA=" },
                      { "length", 32 },
                      { "revpos", 3 }
                  } }
            };
            AssertDictionariesAreEqual(attachmentDict, expandedRev.GetAttachments());

            // Examine the attachment store:
            Assert.AreEqual(2, attachments.Count());
            Assert.AreEqual(new HashSet <BlobKey> {
                BlobStore.KeyForBlob(attach1), BlobStore.KeyForBlob(attach2)
            }, attachments.AllKeys());
            database.Compact();
            Assert.AreEqual(1, attachments.Count());
            Assert.AreEqual(new HashSet <BlobKey> {
                BlobStore.KeyForBlob(attach2)
            }, attachments.AllKeys());
        }
		/// <exception cref="System.Exception"></exception>
		public virtual void TestAttachments()
		{
			string testAttachmentName = "test_attachment";
			BlobStore attachments = database.GetAttachments();
			NUnit.Framework.Assert.AreEqual(0, attachments.Count());
			NUnit.Framework.Assert.AreEqual(new HashSet<object>(), attachments.AllKeys());
			Status status = new Status();
			IDictionary<string, object> rev1Properties = new Dictionary<string, object>();
			rev1Properties.Put("foo", 1);
			rev1Properties.Put("bar", false);
			RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties, 
				database), null, false, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1");
			database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
				), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
				);
			NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
			byte[] data = IOUtils.ToByteArray(attachment.GetContent());
			NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
			IDictionary<string, object> innerDict = new Dictionary<string, object>();
			innerDict.Put("content_type", "text/plain");
			innerDict.Put("digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=");
			innerDict.Put("length", 27);
			innerDict.Put("stub", true);
			innerDict.Put("revpos", 1);
			IDictionary<string, object> attachmentDict = new Dictionary<string, object>();
			attachmentDict.Put(testAttachmentName, innerDict);
			IDictionary<string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
				(rev1.GetSequence(), EnumSet.NoneOf<Database.TDContentOptions>());
			NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
			RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1
				.GetRevId(), EnumSet.NoneOf<Database.TDContentOptions>());
			IDictionary<string, object> gotAttachmentDict = (IDictionary<string, object>)gotRev1
				.GetProperties().Get("_attachments");
			NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
			// Check the attachment dict, with attachments included:
			Sharpen.Collections.Remove(innerDict, "stub");
			innerDict.Put("data", Base64.EncodeBytes(attach1));
			attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1
				.GetSequence(), EnumSet.Of(Database.TDContentOptions.TDIncludeAttachments));
			NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
			gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), EnumSet
				.Of(Database.TDContentOptions.TDIncludeAttachments));
			gotAttachmentDict = (IDictionary<string, object>)gotRev1.GetProperties().Get("_attachments"
				);
			NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
			// Add a second revision that doesn't update the attachment:
			IDictionary<string, object> rev2Properties = new Dictionary<string, object>();
			rev2Properties.Put("_id", rev1.GetDocId());
			rev2Properties.Put("foo", 2);
			rev2Properties.Put("bazz", false);
			RevisionInternal rev2 = database.PutRevision(new RevisionInternal(rev2Properties, 
				database), rev1.GetRevId(), false, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1.GetSequence
				(), rev2.GetSequence());
			// Add a third revision of the same document:
			IDictionary<string, object> rev3Properties = new Dictionary<string, object>();
			rev3Properties.Put("_id", rev2.GetDocId());
			rev3Properties.Put("foo", 2);
			rev3Properties.Put("bazz", false);
			RevisionInternal rev3 = database.PutRevision(new RevisionInternal(rev3Properties, 
				database), rev2.GetRevId(), false, status);
			NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
			byte[] attach2 = Sharpen.Runtime.GetBytesForString("<html>And this is attach2</html>"
				);
			database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach2
				), rev3.GetSequence(), testAttachmentName, "text/html", rev2.GetGeneration());
			// Check the 2nd revision's attachment:
			Attachment attachment2 = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName
				);
			NUnit.Framework.Assert.AreEqual("text/plain", attachment2.GetContentType());
			data = IOUtils.ToByteArray(attachment2.GetContent());
			NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
			// Check the 3rd revision's attachment:
			Attachment attachment3 = database.GetAttachmentForSequence(rev3.GetSequence(), testAttachmentName
				);
			NUnit.Framework.Assert.AreEqual("text/html", attachment3.GetContentType());
			data = IOUtils.ToByteArray(attachment3.GetContent());
			NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach2, data));
			// Examine the attachment store:
			NUnit.Framework.Assert.AreEqual(2, attachments.Count());
			ICollection<BlobKey> expected = new HashSet<BlobKey>();
			expected.AddItem(BlobStore.KeyForBlob(attach1));
			expected.AddItem(BlobStore.KeyForBlob(attach2));
			NUnit.Framework.Assert.AreEqual(expected, attachments.AllKeys());
			database.Compact();
			// This clears the body of the first revision
			NUnit.Framework.Assert.AreEqual(1, attachments.Count());
			ICollection<BlobKey> expected2 = new HashSet<BlobKey>();
			expected2.AddItem(BlobStore.KeyForBlob(attach2));
			NUnit.Framework.Assert.AreEqual(expected2, attachments.AllKeys());
		}
Example #32
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public virtual void TestValidations()
        {
            Validator validator = new _Validator_19(this);

            database.SetValidation("hoopy", validator);
            // POST a valid new document:
            IDictionary <string, object> props = new Dictionary <string, object>();

            props.Put("name", "Zaphod Beeblebrox");
            props.Put("towel", "velvet");
            RevisionInternal rev    = new RevisionInternal(props, database);
            Status           status = new Status();

            validationCalled = false;
            rev = database.PutRevision(rev, null, false, status);
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            // PUT a valid update:
            props.Put("head_count", 3);
            rev.SetProperties(props);
            validationCalled = false;
            rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            // PUT an invalid update:
            Sharpen.Collections.Remove(props, "towel");
            rev.SetProperties(props);
            validationCalled = false;
            bool gotExpectedError = false;

            try
            {
                rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == Status.Forbidden);
            }
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            // POST an invalid new document:
            props = new Dictionary <string, object>();
            props.Put("name", "Vogon");
            props.Put("poetry", true);
            rev = new RevisionInternal(props, database);
            validationCalled = false;
            gotExpectedError = false;
            try
            {
                rev = database.PutRevision(rev, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == Status.Forbidden);
            }
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
            // PUT a valid new document with an ID:
            props = new Dictionary <string, object>();
            props.Put("_id", "ford");
            props.Put("name", "Ford Prefect");
            props.Put("towel", "terrycloth");
            rev = new RevisionInternal(props, database);
            validationCalled = false;
            rev = database.PutRevision(rev, null, false, status);
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.AreEqual("ford", rev.GetDocId());
            // DELETE a document:
            rev = new RevisionInternal(rev.GetDocId(), rev.GetRevId(), true, database);
            NUnit.Framework.Assert.IsTrue(rev.IsDeleted());
            validationCalled = false;
            rev = database.PutRevision(rev, rev.GetRevId(), false, status);
            NUnit.Framework.Assert.IsTrue(validationCalled);
            // PUT an invalid new document:
            props = new Dictionary <string, object>();
            props.Put("_id", "petunias");
            props.Put("name", "Pot of Petunias");
            rev = new RevisionInternal(props, database);
            validationCalled = false;
            gotExpectedError = false;
            try
            {
                rev = database.PutRevision(rev, null, false, status);
            }
            catch (CouchbaseLiteException e)
            {
                gotExpectedError = (e.GetCBLStatus().GetCode() == Status.Forbidden);
            }
            NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.IsTrue(gotExpectedError);
        }
        public void TestPutLargeAttachment()
        {
            var testAttachmentName = "test_attachment";
            var attachments = database.Attachments;
            attachments.DeleteBlobs();
            Assert.AreEqual(0, attachments.Count());

            var status = new Status();
            var rev1Properties = new Dictionary<string, object>();
            rev1Properties["foo"] = 1;
            rev1Properties["bar"] = false;
            var rev1 = database.PutRevision(new RevisionInternal(rev1Properties, 
                database), null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());
            var largeAttachment = new StringBuilder();
            for (int i = 0; i < Database.BigAttachmentLength; i++)
            {
                largeAttachment.Append("big attachment!");
            }

            var attach1 = Runtime.GetBytesForString(largeAttachment.ToString()).ToArray();
            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach1), rev1.GetSequence(), 
                testAttachmentName, "text/plain", rev1.GetGeneration());
            var attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName);
            Assert.AreEqual("text/plain", attachment.ContentType);
            var data = attachment.Content.ToArray();
            Assert.IsTrue(Arrays.Equals(attach1, data));

            const DocumentContentOptions contentOptions = DocumentContentOptions.IncludeAttachments | DocumentContentOptions.BigAttachmentsFollow;
            var attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1.GetSequence(), contentOptions);
            var innerDict = (IDictionary<string, object>)attachmentDictForSequence[testAttachmentName];
            if (innerDict.ContainsKey("stub"))
            {
                if (((bool)innerDict["stub"]))
                {
                    throw new RuntimeException("Expected attachment dict 'stub' key to be true");
                } else {
                    throw new RuntimeException("Expected attachment dict to have 'stub' key");
                }
            }
            if (!innerDict.ContainsKey("follows"))
            {
                throw new RuntimeException("Expected attachment dict to have 'follows' key");
            }

            // Workaround :
            // Not closing the content stream will cause Sharing Violation
            // Exception when trying to get the same attachment going forward.
            attachment.ContentStream.Close();

            var rev1WithAttachments = database.GetDocumentWithIDAndRev(
                rev1.GetDocId(), rev1.GetRevId(), contentOptions);
            
            var rev1WithAttachmentsProperties = rev1WithAttachments.GetProperties();
            var rev2Properties = new Dictionary<string, object>();
            rev2Properties.Put("_id", rev1WithAttachmentsProperties["_id"]);
            rev2Properties["foo"] = 2;
            var newRev = new RevisionInternal(rev2Properties, database);
            var rev2 = database.PutRevision(newRev, rev1WithAttachments.GetRevId(), false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            database.CopyAttachmentNamedFromSequenceToSequence(
                testAttachmentName, rev1WithAttachments.GetSequence(), rev2.GetSequence());

            // Check the 2nd revision's attachment:
            var rev2FetchedAttachment = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName);
            Assert.AreEqual(attachment.Length, rev2FetchedAttachment.Length);
            AssertPropertiesAreEqual(attachment.Metadata, rev2FetchedAttachment.Metadata);
            Assert.AreEqual(attachment.ContentType, rev2FetchedAttachment.ContentType);

            // Add a third revision of the same document:
            var rev3Properties = new Dictionary<string, object>();
            rev3Properties.Put("_id", rev2.GetProperties().Get("_id"));
            rev3Properties["foo"] = 3;
            rev3Properties["baz"] = false;
            var rev3 = new RevisionInternal(rev3Properties, database);
            rev3 = database.PutRevision(rev3, rev2.GetRevId(), false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());
            var attach3 = Runtime.GetBytesForString("<html><blink>attach3</blink></html>").ToArray();
            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach3), rev3.GetSequence(), 
                testAttachmentName, "text/html", rev3.GetGeneration());

            // Check the 3rd revision's attachment:
            var rev3FetchedAttachment = database.GetAttachmentForSequence(
                rev3.GetSequence(), testAttachmentName);
            data = rev3FetchedAttachment.Content.ToArray();
            Assert.IsTrue(Arrays.Equals(attach3, data));
            Assert.AreEqual("text/html", rev3FetchedAttachment.ContentType);

            // TODO: why doesn't this work?
            // Assert.assertEquals(attach3.length, rev3FetchedAttachment.getLength());
            ICollection<BlobKey> blobKeys = database.Attachments.AllKeys();
            Assert.AreEqual(2, blobKeys.Count);
            database.Compact();
            blobKeys = database.Attachments.AllKeys();
            Assert.AreEqual(1, blobKeys.Count);
        }
		/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
		public virtual void TestCRUDOperations()
		{
			database.AddChangeListener(this);
			string privateUUID = database.PrivateUUID();
			string publicUUID = database.PublicUUID();
			Log.V(Tag, "DB private UUID = '" + privateUUID + "', public UUID = '" + publicUUID
				 + "'");
			NUnit.Framework.Assert.IsTrue(privateUUID.Length >= 20);
			NUnit.Framework.Assert.IsTrue(publicUUID.Length >= 20);
			//create a document
			IDictionary<string, object> documentProperties = new Dictionary<string, object>();
			documentProperties["foo"] = 1;
			documentProperties["bar"] = false;
			documentProperties["baz"] = "touch";
			Body body = new Body(documentProperties);
			RevisionInternal rev1 = new RevisionInternal(body, database);
			Status status = new Status();
			rev1 = database.PutRevision(rev1, null, false, status);
			Log.V(Tag, "Created " + rev1);
			NUnit.Framework.Assert.IsTrue(rev1.GetDocId().Length >= 10);
			NUnit.Framework.Assert.IsTrue(rev1.GetRevId().StartsWith("1-"));
			//read it back
			RevisionInternal readRev = database.GetDocumentWithIDAndRev(rev1.GetDocId(), null
				, EnumSet.NoneOf<TDContentOptions>());
			NUnit.Framework.Assert.IsNotNull(readRev);
			IDictionary<string, object> readRevProps = readRev.Properties;
			NUnit.Framework.Assert.AreEqual(UserProperties(readRevProps), UserProperties(body
				.Properties));
			//now update it
			documentProperties = readRev.Properties;
			documentProperties["status"] = "updated!";
			body = new Body(documentProperties);
			RevisionInternal rev2 = new RevisionInternal(body, database);
			RevisionInternal rev2input = rev2;
			rev2 = database.PutRevision(rev2, rev1.GetRevId(), false, status);
			Log.V(Tag, "Updated " + rev1);
			NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rev2.GetDocId());
			NUnit.Framework.Assert.IsTrue(rev2.GetRevId().StartsWith("2-"));
			//read it back
			readRev = database.GetDocumentWithIDAndRev(rev2.GetDocId(), null, EnumSet.NoneOf<
				TDContentOptions>());
			NUnit.Framework.Assert.IsNotNull(readRev);
			NUnit.Framework.Assert.AreEqual(UserProperties(readRev.Properties), UserProperties
				(body.Properties));
			// Try to update the first rev, which should fail:
			bool gotExpectedError = false;
			try
			{
				database.PutRevision(rev2input, rev1.GetRevId(), false, status);
			}
			catch (CouchbaseLiteException e)
			{
				gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.Conflict;
			}
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
			// Check the changes feed, with and without filters:
			RevisionList changes = database.ChangesSince(0, null, null);
			Log.V(Tag, "Changes = " + changes);
			NUnit.Framework.Assert.AreEqual(1, changes.Count);
            changes = database.ChangesSince(0, null, (revision, items) => "updated!".Equals (revision.Properties.Get ("status")));
			NUnit.Framework.Assert.AreEqual(1, changes.Count);
            changes = database.ChangesSince(0, null, (revision, items) => "not updated!".Equals (revision.Properties.Get ("status")));
			NUnit.Framework.Assert.AreEqual(0, changes.Count);
			// Delete it:
			RevisionInternal revD = new RevisionInternal(rev2.GetDocId(), null, true, database
				);
			RevisionInternal revResult = null;
			gotExpectedError = false;
			try
			{
				revResult = database.PutRevision(revD, null, false, status);
			}
			catch (CouchbaseLiteException e)
			{
				gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.Conflict;
			}
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
			NUnit.Framework.Assert.IsNull(revResult);
			revD = database.PutRevision(revD, rev2.GetRevId(), false, status);
			NUnit.Framework.Assert.AreEqual(StatusCode.Ok, status.GetCode());
			NUnit.Framework.Assert.AreEqual(revD.GetDocId(), rev2.GetDocId());
			NUnit.Framework.Assert.IsTrue(revD.GetRevId().StartsWith("3-"));
			// Delete nonexistent doc:
			RevisionInternal revFake = new RevisionInternal("fake", null, true, database);
			gotExpectedError = false;
			try
			{
				database.PutRevision(revFake, null, false, status);
			}
			catch (CouchbaseLiteException e)
			{
				gotExpectedError = e.GetCBLStatus().GetCode() == StatusCode.NotFound;
			}
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
			// Read it back (should fail):
			readRev = database.GetDocumentWithIDAndRev(revD.GetDocId(), null, EnumSet.NoneOf<TDContentOptions>());
			NUnit.Framework.Assert.IsNull(readRev);
			// Get Changes feed
			changes = database.ChangesSince(0, null, null);
			NUnit.Framework.Assert.IsTrue(changes.Count == 1);
			// Get Revision History
			IList<RevisionInternal> history = database.GetRevisionHistory(revD);
			NUnit.Framework.Assert.AreEqual(revD, history[0]);
			NUnit.Framework.Assert.AreEqual(rev2, history[1]);
			NUnit.Framework.Assert.AreEqual(rev1, history[2]);
		}
        public void TestAttachments()
        {
            var testAttachmentName = "test_attachment";
            var attachments = database.Attachments;
            Assert.AreEqual(0, attachments.Count());
            Assert.AreEqual(0, attachments.AllKeys().Count());
            
            var rev1Properties = new Dictionary<string, object>();
            rev1Properties["foo"] = 1;
            rev1Properties["bar"] = false;

            var status = new Status();
            var rev1 = database.PutRevision(
                new RevisionInternal(rev1Properties, database), null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var attach1 = Runtime.GetBytesForString(
                "This is the body of attach1").ToArray();
            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach1), 
                rev1.GetSequence(), 
                testAttachmentName, 
                "text/plain", 
                rev1.GetGeneration());

            //We must set the no_attachments column for the rev to false, as we are using an internal
            //private API call above (database.insertAttachmentForSequenceWithNameAndType) which does
            //not set the no_attachments column on revs table
            try
            {
                var args = new ContentValues();
                args.Put("no_attachments", false);
                database.StorageEngine.Update(
                    "revs", 
                    args, 
                    "sequence=?", 
                    new[] { rev1.GetSequence().ToString() }
                );
            }
            catch (SQLException e)
            {
                Log.E(Tag, "Error setting rev1 no_attachments to false", e);
                throw new CouchbaseLiteException(StatusCode.InternalServerError);
            }

            var attachment = database.GetAttachmentForSequence(
                rev1.GetSequence(), 
                testAttachmentName
            );
            Assert.AreEqual("text/plain", attachment.ContentType);
            var data = attachment.Content.ToArray();
            Assert.IsTrue(Arrays.Equals(attach1, data));

            // Workaround :
            // Not closing the content stream will cause Sharing Violation
            // Exception when trying to get the same attachment going forward.
            attachment.ContentStream.Close();

            var innerDict = new Dictionary<string, object>();
            innerDict["content_type"] = "text/plain";
            innerDict["digest"] = "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=";
            innerDict["length"] = 27;
            innerDict["stub"] = true;
            innerDict["revpos"] = 1;

            var attachmentDict = new Dictionary<string, object>();
            attachmentDict[testAttachmentName] = innerDict;
            var attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1.GetSequence(), DocumentContentOptions.None);
            Assert.AreEqual(new SortedDictionary<string,object>(attachmentDict), new SortedDictionary<string,object>(attachmentDictForSequence));//Assert.AreEqual(1, attachmentDictForSequence.Count);
            var gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), 
                rev1.GetRevId(), DocumentContentOptions.IncludeAttachments);
            var gotAttachmentDict = gotRev1.GetProperties()
                .Get("_attachments")
                .AsDictionary<string,object>();
            Assert.AreEqual(attachmentDict.Select(kvp => kvp.Key).OrderBy(k => k), gotAttachmentDict.Select(kvp => kvp.Key).OrderBy(k => k));

            // Check the attachment dict, with attachments included:
            innerDict.Remove("stub");
            innerDict.Put("data", Convert.ToBase64String(attach1));
            attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(
                rev1.GetSequence(), DocumentContentOptions.IncludeAttachments);
            Assert.AreEqual(new SortedDictionary<string,object>(attachmentDict[testAttachmentName].AsDictionary<string,object>()), new SortedDictionary<string,object>(attachmentDictForSequence[testAttachmentName].AsDictionary<string,object>()));

            gotRev1 = database.GetDocumentWithIDAndRev(
                rev1.GetDocId(), rev1.GetRevId(), DocumentContentOptions.IncludeAttachments);
            gotAttachmentDict = gotRev1.GetProperties()
                .Get("_attachments")
                .AsDictionary<string, object>()
                .Get(testAttachmentName)
                .AsDictionary<string,object>();
            Assert.AreEqual(innerDict.Select(kvp => kvp.Key).OrderBy(k => k), gotAttachmentDict.Select(kvp => kvp.Key).OrderBy(k => k));

            // Add a second revision that doesn't update the attachment:
            var rev2Properties = new Dictionary<string, object>();
            rev2Properties.Put("_id", rev1.GetDocId());
            rev2Properties["foo"] = 2;
            rev2Properties["bazz"] = false;
            var rev2 = database.PutRevision(new RevisionInternal(rev2Properties, 
                database), rev1.GetRevId(), false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            database.CopyAttachmentNamedFromSequenceToSequence(
                testAttachmentName, rev1.GetSequence(), rev2.GetSequence());           
            // Add a third revision of the same document:
            var rev3Properties = new Dictionary<string, object>();
            rev3Properties.Put("_id", rev2.GetDocId());
            rev3Properties["foo"] = 2;
            rev3Properties["bazz"] = false;
            var rev3 = database.PutRevision(new RevisionInternal(
                rev3Properties, database), rev2.GetRevId(), false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var attach2 = Runtime.GetBytesForString("<html>And this is attach2</html>").ToArray();
            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach2), rev3.GetSequence(), 
                testAttachmentName, "text/html", rev2.GetGeneration());
            // Check the 2nd revision's attachment:
            var attachment2 = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName);
            Assert.AreEqual("text/plain", attachment2.ContentType);

            data = attachment2.Content.ToArray();
            Assert.IsTrue(Arrays.Equals(attach1, data));

            // Workaround :
            // Not closing the content stream will cause Sharing Violation
            // Exception when trying to get the same attachment going forward.
            attachment2.ContentStream.Close();

            // Check the 3rd revision's attachment:
            var attachment3 = database.GetAttachmentForSequence(rev3.GetSequence(), testAttachmentName);
            Assert.AreEqual("text/html", attachment3.ContentType);

            data = attachment3.Content.ToArray();
            Assert.IsTrue(Arrays.Equals(attach2, data));

            var attachmentDictForRev3 = database.GetAttachmentsDictForSequenceWithContent(rev3.GetSequence(), DocumentContentOptions.None)
                .Get(testAttachmentName)
                .AsDictionary<string,object>();
            if (attachmentDictForRev3.ContainsKey("follows"))
            {
                if (((bool)attachmentDictForRev3.Get("follows")) == true)
                {
                    throw new RuntimeException("Did not expected attachment dict 'follows' key to be true"
                    );
                }
                else
                {
                    throw new RuntimeException("Did not expected attachment dict to have 'follows' key"
                    );
                }
            }

            // Workaround :
            // Not closing the content stream will cause Sharing Violation
            // Exception when trying to get the same attachment going forward.
            attachment3.ContentStream.Close();

            // Examine the attachment store:
            Assert.AreEqual(2, attachments.Count());

            var expected = new HashSet<BlobKey>();
            expected.AddItem(BlobStore.KeyForBlob(attach1));
            expected.AddItem(BlobStore.KeyForBlob(attach2));
            Assert.AreEqual(expected.Count, attachments.AllKeys().Count());

            foreach(var key in attachments.AllKeys()) {
                Assert.IsTrue(expected.Contains(key));
            }

            database.Compact();

            // This clears the body of the first revision
            Assert.AreEqual(1, attachments.Count());

            var expected2 = new HashSet<BlobKey>();
            expected2.AddItem(BlobStore.KeyForBlob(attach2));
            Assert.AreEqual(expected2.Count, attachments.AllKeys().Count());

            foreach(var key in attachments.AllKeys()) {
                Assert.IsTrue(expected2.Contains(key));
            }
        }
Example #36
0
        internal void UpdateIndex()
        {
            Log.I(Database.Tag, "Re-indexing view {0} ...", Name);
            System.Diagnostics.Debug.Assert((Map != null));

            if (Id <= 0)
            {
                var msg = string.Format("View.Id <= 0");
                throw new CouchbaseLiteException(msg, new Status(StatusCode.NotFound));
            }

            var    result  = new Status(StatusCode.InternalServerError);
            Cursor cursor  = null;
            Cursor cursor2 = null;

            try
            {
                Database.RunInTransaction(() =>
                {
                    var lastSequence  = LastSequenceIndexed;
                    var dbMaxSequence = Database.LastSequenceNumber;

                    if (lastSequence >= dbMaxSequence)
                    {
                        // nothing to do (eg,  kCBLStatusNotModified)
                        Log.V(Database.Tag, "lastSequence ({0}) == dbMaxSequence ({1}), nothing to do", lastSequence, dbMaxSequence);
                        result.SetCode(StatusCode.NotModified);
                        return(false);
                    }

                    // First remove obsolete emitted results from the 'maps' table:
                    var sequence = lastSequence;
                    if (lastSequence < 0)
                    {
                        var msg = string.Format("lastSequence < 0 ({0})", lastSequence);
                        throw new CouchbaseLiteException(msg, new Status(StatusCode.InternalServerError));
                    }
                    if (lastSequence == 0)
                    {
                        // If the lastSequence has been reset to 0, make sure to remove
                        // any leftover rows:
                        var whereArgs = new string[] { Id.ToString() };
                        Database.StorageEngine.Delete("maps", "view_id=?", whereArgs);
                    }
                    else
                    {
                        Database.OptimizeSQLIndexes();
                        // Delete all obsolete map results (ones from since-replaced
                        // revisions):
                        var args = new [] {
                            Id.ToString(),
                            lastSequence.ToString(),
                            lastSequence.ToString()
                        };

                        Database.StorageEngine.ExecSQL(
                            "DELETE FROM maps WHERE view_id=? AND sequence IN ("
                            + "SELECT parent FROM revs WHERE sequence>? " + "AND +parent>0 AND +parent<=?)",
                            args);
                    }

                    var deleted = 0;
                    cursor      = Database.StorageEngine.IntransactionRawQuery("SELECT changes()");
                    cursor.MoveToNext();
                    deleted = cursor.GetInt(0);
                    cursor.Close();

                    // Find a better way to propagate this back
                    // Now scan every revision added since the last time the view was indexed:
                    var selectArgs = new[] { lastSequence.ToString(), dbMaxSequence.ToString() };
                    cursor         = Database.StorageEngine.IntransactionRawQuery("SELECT revs.doc_id, sequence, docid, revid, json, no_attachments FROM revs, docs "
                                                                                  + "WHERE sequence>? AND sequence<=? AND current!=0 AND deleted=0 "
                                                                                  + "AND revs.doc_id = docs.doc_id "
                                                                                  + "ORDER BY revs.doc_id, revid DESC", selectArgs);

                    var lastDocID = 0L;
                    var keepGoing = cursor.MoveToNext();
                    while (keepGoing)
                    {
                        long docID = cursor.GetLong(0);
                        if (docID != lastDocID)
                        {
                            // Only look at the first-iterated revision of any document,
                            // because this is the
                            // one with the highest revid, hence the "winning" revision
                            // of a conflict.
                            lastDocID = docID;
                            // Reconstitute the document as a dictionary:
                            sequence     = cursor.GetLong(1);
                            string docId = cursor.GetString(2);
                            if (docId.StartsWith("_design/", StringComparison.InvariantCultureIgnoreCase))
                            {
                                // design docs don't get indexed!
                                keepGoing = cursor.MoveToNext();
                                continue;
                            }
                            var revId = cursor.GetString(3);
                            var json  = cursor.GetBlob(4);

                            var noAttachments = cursor.GetInt(5) > 0;

                            // Skip rows with the same doc_id -- these are losing conflicts.
                            while ((keepGoing = cursor.MoveToNext()) && cursor.GetLong(0) == docID)
                            {
                            }

                            if (lastSequence > 0)
                            {
                                // Find conflicts with documents from previous indexings.
                                var selectArgs2 = new[] { Convert.ToString(docID), Convert.ToString(lastSequence) };
                                cursor2         = Database.StorageEngine.IntransactionRawQuery("SELECT revid, sequence FROM revs "
                                                                                               + "WHERE doc_id=? AND sequence<=? AND current!=0 AND deleted=0 " + "ORDER BY revID DESC "
                                                                                               + "LIMIT 1", selectArgs2);
                                if (cursor2.MoveToNext())
                                {
                                    var oldRevId = cursor2.GetString(0);

                                    // This is the revision that used to be the 'winner'.
                                    // Remove its emitted rows:
                                    var oldSequence = cursor2.GetLong(1);
                                    var args        = new[] { Sharpen.Extensions.ToString(Id), Convert.ToString(oldSequence) };
                                    Database.StorageEngine.ExecSQL("DELETE FROM maps WHERE view_id=? AND sequence=?", args);

                                    if (RevisionInternal.CBLCompareRevIDs(oldRevId, revId) > 0)
                                    {
                                        // It still 'wins' the conflict, so it's the one that
                                        // should be mapped [again], not the current revision!
                                        revId           = oldRevId;
                                        sequence        = oldSequence;
                                        var selectArgs3 = new[] { Convert.ToString(sequence) };
                                        json            = Misc.ByteArrayResultForQuery(
                                            Database.StorageEngine,
                                            "SELECT json FROM revs WHERE sequence=?",
                                            selectArgs3
                                            );
                                    }
                                }

                                cursor2.Close();
                                cursor2 = null;
                            }
                            // Get the document properties, to pass to the map function:
                            var contentOptions = DocumentContentOptions.None;
                            if (noAttachments)
                            {
                                contentOptions |= DocumentContentOptions.NoAttachments;
                            }

                            var properties = Database.DocumentPropertiesFromJSON(
                                json, docId, revId, false, sequence, DocumentContentOptions.None
                                );
                            if (properties != null)
                            {
                                // Call the user-defined map() to emit new key/value
                                // pairs from this revision:

                                // This is the emit() block, which gets called from within the
                                // user-defined map() block
                                // that's called down below.

                                var enclosingView = this;
                                var thisSequence  = sequence;
                                var map           = Map;

                                if (map == null)
                                {
                                    throw new CouchbaseLiteException("Map function is missing.");
                                }

                                EmitDelegate emitBlock = (key, value) =>
                                {
                                    // TODO: Do we need to do any null checks on key or value?
                                    try
                                    {
                                        var keyJson   = Manager.GetObjectMapper().WriteValueAsString(key);
                                        var valueJson = value == null ? null : Manager.GetObjectMapper().WriteValueAsString(value);

                                        var insertValues = new ContentValues();
                                        insertValues.Put("view_id", enclosingView.Id);
                                        insertValues["sequence"] = thisSequence;
                                        insertValues["key"]      = keyJson;
                                        insertValues["value"]    = valueJson;

                                        enclosingView.Database.StorageEngine.Insert("maps", null, insertValues);
                                    }
                                    catch (Exception e)
                                    {
                                        Log.E(Database.Tag, "Error emitting", e);
                                    }
                                };

                                map(properties, emitBlock);
                            }
                        }
                        else
                        {
                            keepGoing = cursor.MoveToNext();
                        }
                    }

                    // Finally, record the last revision sequence number that was
                    // indexed:
                    var updateValues             = new ContentValues();
                    updateValues["lastSequence"] = dbMaxSequence;
                    var whereArgs_1 = new string[] { Id.ToString() };
                    Database.StorageEngine.Update("views", updateValues, "view_id=?", whereArgs_1);

                    // FIXME actually count number added :)
                    Log.V(Database.Tag, "...Finished re-indexing view {0} up to sequence {1} (deleted {2} added ?)", Name, Convert.ToString(dbMaxSequence), deleted);
                    result.SetCode(StatusCode.Ok);

                    return(true);
                });
            }
            catch (Exception e)
            {
                throw new CouchbaseLiteException(e, new Status(StatusCode.DbError));
            }
            finally
            {
                if (cursor2 != null)
                {
                    cursor2.Close();
                }

                if (cursor != null)
                {
                    cursor.Close();
                }

                if (!result.IsSuccessful)
                {
                    Log.W(Database.Tag, "Failed to rebuild view {0}:{1}", Name, result.GetCode());
                }
            }
        }
Example #37
0
        public void TestPusher()
        {
            var remote         = GetReplicationURL();
            var docIdTimestamp = Convert.ToString(Runtime.CurrentTimeMillis());

            // Create some documents:
            var documentProperties = new Dictionary <string, object>();
            var doc1Id             = string.Format("doc1-{0}", docIdTimestamp);

            documentProperties["_id"] = doc1Id;
            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;

            var body   = new Body(documentProperties);
            var rev1   = new RevisionInternal(body, database);
            var status = new Status();

            rev1 = database.PutRevision(rev1, null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            documentProperties.Put("_rev", rev1.GetRevId());
            documentProperties["UPDATED"] = true;
            database.PutRevision(new RevisionInternal(documentProperties, database), rev1.GetRevId(), false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            documentProperties = new Dictionary <string, object>();
            var doc2Id = string.Format("doc2-{0}", docIdTimestamp);

            documentProperties["_id"]   = doc2Id;
            documentProperties["baz"]   = 666;
            documentProperties["fnord"] = true;

            database.PutRevision(new RevisionInternal(documentProperties, database), null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            var doc2             = database.GetDocument(doc2Id);
            var doc2UnsavedRev   = doc2.CreateRevision();
            var attachmentStream = GetAsset("attachment.png");

            doc2UnsavedRev.SetAttachment("attachment.png", "image/png", attachmentStream);
            var doc2Rev = doc2UnsavedRev.Save();

            Assert.IsNotNull(doc2Rev);

            const bool continuous = false;
            var        repl       = database.CreatePushReplication(remote);

            repl.Continuous = continuous;
            if (!IsSyncGateway(remote))
            {
                repl.CreateTarget = true;
            }

            // Check the replication's properties:
            Assert.AreEqual(database, repl.LocalDatabase);
            Assert.AreEqual(remote, repl.RemoteUrl);
            Assert.IsFalse(repl.IsPull);
            Assert.IsFalse(repl.Continuous);
            Assert.IsNull(repl.Filter);
            Assert.IsNull(repl.FilterParams);
            Assert.IsNull(repl.DocIds);
            // TODO: CAssertNil(r1.headers); still not null!
            // Check that the replication hasn't started running:
            Assert.IsFalse(repl.IsRunning);
            Assert.AreEqual((int)repl.Status, (int)ReplicationStatus.Stopped);
            Assert.AreEqual(0, repl.CompletedChangesCount);
            Assert.AreEqual(0, repl.ChangesCount);
            Assert.IsNull(repl.LastError);

            RunReplication(repl);

            // TODO: Verify the foloowing 2 asserts. ChangesCount and CompletedChangesCount
            // should already be reset when the replicator stopped.
            Assert.IsTrue(repl.ChangesCount >= 2);
            Assert.IsTrue(repl.CompletedChangesCount >= 2);
            Assert.IsNull(repl.LastError);

            VerifyRemoteDocExists(remote, doc1Id);

            // Add doc3
            documentProperties = new Dictionary <string, object>();
            var doc3Id = string.Format("doc3-{0}", docIdTimestamp);
            var doc3   = database.GetDocument(doc3Id);

            documentProperties["bat"] = 677;
            doc3.PutProperties(documentProperties);

            // re-run push replication
            var repl2 = database.CreatePushReplication(remote);

            repl2.Continuous = continuous;
            if (!IsSyncGateway(remote))
            {
                repl2.CreateTarget = true;
            }

            var repl2CheckedpointId = repl2.RemoteCheckpointDocID();

            RunReplication(repl2);

            Assert.IsNull(repl2.LastError);

            // make sure trhe doc has been added
            VerifyRemoteDocExists(remote, doc3Id);

            Assert.AreEqual(repl2.LastSequence, database.LastSequenceWithCheckpointId(repl2CheckedpointId));

            System.Threading.Thread.Sleep(2000);
            var json = GetRemoteDoc(remote, repl2CheckedpointId);
            var remoteLastSequence = (string)json["lastSequence"];

            Assert.AreEqual(repl2.LastSequence, remoteLastSequence);

            Log.D(Tag, "testPusher() finished");
        }
        public void TestPusherDeletedDoc()
        {
            var remote = GetReplicationURL();
            var docIdTimestamp = Convert.ToString(Runtime.CurrentTimeMillis());

            // Create some documentsConvert
            var documentProperties = new Dictionary<string, object>();
            var doc1Id = string.Format("doc1-{0}", docIdTimestamp);
            documentProperties["_id"] = doc1Id;
            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;

            var body = new Body(documentProperties);
            var rev1 = new RevisionInternal(body, database);
            var status = new Status();
            rev1 = database.PutRevision(rev1, null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            documentProperties["_rev"] = rev1.GetRevId();
            documentProperties["UPDATED"] = true;
            documentProperties["_deleted"] = true;
            database.PutRevision(new RevisionInternal(documentProperties, database), rev1.GetRevId(), false, status);
            Assert.IsTrue((int)status.GetCode() >= 200 && (int)status.GetCode() < 300);

            var repl = database.CreatePushReplication(remote);
            if (!IsSyncGateway(remote)) {
                ((Pusher)repl).CreateTarget = true;
            }

            RunReplication(repl);

            Assert.IsNull(repl.LastError);

            // make sure doc1 is deleted
            var replicationUrlTrailing = new Uri(string.Format ("{0}/", remote));
            var pathToDoc = new Uri(replicationUrlTrailing, doc1Id);
            Log.D(Tag, "Send http request to " + pathToDoc);
            var httpRequestDoneSignal = new CountDownLatch(1);
                var httpclient = new HttpClient();
                try
                {
                    var getDocResponse = httpclient.GetAsync(pathToDoc.ToString()).Result;
                    var statusLine = getDocResponse.StatusCode;
                    Log.D(ReplicationTest.Tag, "statusLine " + statusLine);
                    Assert.AreEqual(Couchbase.Lite.StatusCode.NotFound, statusLine.GetStatusCode());                        
                }
                catch (ProtocolViolationException e)
                {
                    Assert.IsNull(e, "Got ClientProtocolException: " + e.Message);
                }
                catch (IOException e)
                {
                    Assert.IsNull(e, "Got IOException: " + e.Message);
                }
                finally
                {
                    httpRequestDoneSignal.CountDown();
                }
            Log.D(Tag, "Waiting for http request to finish");
            try
            {
                httpRequestDoneSignal.Await(TimeSpan.FromSeconds(10));
                Log.D(Tag, "http request finished");
            }
            catch (Exception e)
            {
                Runtime.PrintStackTrace(e);
            }
            Log.D(Tag, "testPusherDeletedDoc() finished");
        }
Example #39
0
        public void TestPusherDeletedDoc()
        {
            var remote         = GetReplicationURL();
            var docIdTimestamp = Convert.ToString(Runtime.CurrentTimeMillis());

            // Create some documentsConvert
            var documentProperties = new Dictionary <string, object>();
            var doc1Id             = string.Format("doc1-{0}", docIdTimestamp);

            documentProperties["_id"] = doc1Id;
            documentProperties["foo"] = 1;
            documentProperties["bar"] = false;

            var body   = new Body(documentProperties);
            var rev1   = new RevisionInternal(body, database);
            var status = new Status();

            rev1 = database.PutRevision(rev1, null, false, status);
            Assert.AreEqual(StatusCode.Created, status.GetCode());

            documentProperties["_rev"]     = rev1.GetRevId();
            documentProperties["UPDATED"]  = true;
            documentProperties["_deleted"] = true;
            database.PutRevision(new RevisionInternal(documentProperties, database), rev1.GetRevId(), false, status);
            Assert.IsTrue((int)status.GetCode() >= 200 && (int)status.GetCode() < 300);

            var repl = database.CreatePushReplication(remote);

            if (!IsSyncGateway(remote))
            {
                ((Pusher)repl).CreateTarget = true;
            }

            RunReplication(repl);

            Assert.IsNull(repl.LastError);

            // make sure doc1 is deleted
            var replicationUrlTrailing = new Uri(string.Format("{0}/", remote));
            var pathToDoc = new Uri(replicationUrlTrailing, doc1Id);

            Log.D(Tag, "Send http request to " + pathToDoc);
            var httpRequestDoneSignal = new CountDownLatch(1);
            var httpclient            = new HttpClient();

            try
            {
                var getDocResponse = httpclient.GetAsync(pathToDoc.ToString()).Result;
                var statusLine     = getDocResponse.StatusCode;
                Log.D(ReplicationTest.Tag, "statusLine " + statusLine);
                Assert.AreEqual(Couchbase.Lite.StatusCode.NotFound, statusLine.GetStatusCode());
            }
            catch (ProtocolViolationException e)
            {
                Assert.IsNull(e, "Got ClientProtocolException: " + e.Message);
            }
            catch (IOException e)
            {
                Assert.IsNull(e, "Got IOException: " + e.Message);
            }
            finally
            {
                httpRequestDoneSignal.CountDown();
            }
            Log.D(Tag, "Waiting for http request to finish");
            try
            {
                httpRequestDoneSignal.Await(TimeSpan.FromSeconds(10));
                Log.D(Tag, "http request finished");
            }
            catch (Exception e)
            {
                Runtime.PrintStackTrace(e);
            }
            Log.D(Tag, "testPusherDeletedDoc() finished");
        }
Example #40
0
        internal void UpdateIndex()
        {
            Log.V(Database.Tag, "Re-indexing view " + Name + " ...");
            System.Diagnostics.Debug.Assert((Map != null));

            if (Id < 0)
            {
                var msg = string.Format("View.Id < 0");
                throw new CouchbaseLiteException(msg, new Status(StatusCode.NotFound));
            }

            Database.BeginTransaction();

            var result = new Status(StatusCode.InternalServerError);
            Cursor cursor = null;
            try
            {
                var lastSequence = LastSequenceIndexed;
                var dbMaxSequence = Database.LastSequenceNumber;

                if (lastSequence == dbMaxSequence)
                {
                    // nothing to do (eg,  kCBLStatusNotModified)
                    var msg = String.Format("lastSequence ({0}) == dbMaxSequence ({1}), nothing to do", lastSequence, dbMaxSequence);
                    Log.D(Database.Tag, msg);
                    result.SetCode(StatusCode.Ok);
                    return;
                }

                // First remove obsolete emitted results from the 'maps' table:
                var sequence = lastSequence;
                if (lastSequence < 0)
                {
                    var msg = string.Format("lastSequence < 0 ({0})", lastSequence);
                    throw new CouchbaseLiteException(msg, new Status(StatusCode.InternalServerError));
                }
                if (lastSequence == 0)
                {
                    // If the lastSequence has been reset to 0, make sure to remove
                    // any leftover rows:
                    var whereArgs = new string[] { Sharpen.Extensions.ToString(Id) };
                    Database.StorageEngine.Delete("maps", "view_id=@", whereArgs);
                }
                else
                {
                    // Delete all obsolete map results (ones from since-replaced
                    // revisions):
                    var args = new [] {
                        Id.ToString(),
                        lastSequence.ToString(),
                        lastSequence.ToString()
                    };

                    Database.StorageEngine.ExecSQL(
                        "DELETE FROM maps WHERE view_id=@ AND sequence IN ("
                        + "SELECT parent FROM revs WHERE sequence>@ " + "AND parent>0 AND parent<=@)", 
                            args);
                }
                var deleted = 0;
                cursor = Database.StorageEngine.RawQuery("SELECT changes()", null); // TODO: Convert to ADO params.
                cursor.MoveToNext();
                deleted = cursor.GetInt(0);
                cursor.Close();

                // find a better way to propagate this back
                // Now scan every revision added since the last time the view was
                // indexed:
                var selectArgs = new[] { Convert.ToString(lastSequence) };
                cursor = Database.StorageEngine.RawQuery("SELECT revs.doc_id, sequence, docid, revid, json FROM revs, docs "
                    + "WHERE sequence>@ AND current!=0 AND deleted=0 " + "AND revs.doc_id = docs.doc_id "
                    + "ORDER BY revs.doc_id, revid DESC", CommandBehavior.SequentialAccess, selectArgs);
                cursor.MoveToNext();

                var lastDocID = 0L;
                while (!cursor.IsAfterLast())
                {
                    long docID = cursor.GetLong(0);
                    if (docID != lastDocID)
                    {
                        // Only look at the first-iterated revision of any document,
                        // because this is the
                        // one with the highest revid, hence the "winning" revision
                        // of a conflict.
                        lastDocID = docID;
                        // Reconstitute the document as a dictionary:
                        sequence = cursor.GetLong(1);
                        string docId = cursor.GetString(2);
						if (docId.StartsWith("_design/", StringCompare.IgnoreCase))
                        {
                            // design docs don't get indexed!
                            cursor.MoveToNext();
                            continue;
                        }
                        var revId = cursor.GetString(3);
                        var json = cursor.GetBlob(4);
                        var properties = Database.DocumentPropertiesFromJSON(
                            json, docId, revId, false, sequence, EnumSet.NoneOf<TDContentOptions>()
                        );
                        if (properties != null)
                        {
                            // Call the user-defined map() to emit new key/value
                            // pairs from this revision:
                            Log.V(Database.Tag, "  call map for sequence=" + System.Convert.ToString(sequence
                            ));
                            // This is the emit() block, which gets called from within the
                            // user-defined map() block
                            // that's called down below.

                            var enclosingView = this;
                            var thisSequence = sequence;
                            var map = Map;

                            if (map == null)
                                throw new CouchbaseLiteException("Map function is missing.");

                            EmitDelegate emitBlock = (key, value) =>
                            {
                                // TODO: Do we need to do any null checks on key or value?
                                try
                                {
                                    var keyJson = Manager.GetObjectMapper().WriteValueAsString(key);
                                    var valueJson = value == null ? null : Manager.GetObjectMapper().WriteValueAsString(value) ;
                                    Log.V(Database.Tag, String.Format("    emit({0}, {1})", keyJson, valueJson));

                                    var insertValues = new ContentValues();
                                    insertValues.Put("view_id", enclosingView.Id);
                                    insertValues["sequence"] = thisSequence;
                                    insertValues["key"] = keyJson;
                                    insertValues["value"] = valueJson;

                                    enclosingView.Database.StorageEngine.Insert("maps", null, insertValues);
                                }
                                catch (Exception e)
                                {
                                    Log.E(Database.Tag, "Error emitting", e);
                                }
                            };

                            map(properties, emitBlock);
                        }
                    }
                    cursor.MoveToNext();
                }
                // Finally, record the last revision sequence number that was
                // indexed:
                ContentValues updateValues = new ContentValues();
                updateValues["lastSequence"] = dbMaxSequence;
                var whereArgs_1 = new string[] { Sharpen.Extensions.ToString(Id) };
                Database.StorageEngine.Update("views", updateValues, "view_id=@", whereArgs_1);
                // FIXME actually count number added :)
                        Log.V(Database.Tag, "...Finished re-indexing view " + Name + " up to sequence " +
                    System.Convert.ToString(dbMaxSequence) + " (deleted " + deleted + " added " + "?" + ")");
                        result.SetCode(StatusCode.Ok);
            }
            catch (SQLException e)
            {
                throw new CouchbaseLiteException(e, new Status(StatusCode.DbError));
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
                if (!result.IsSuccessful())
                {
                    Log.W(Database.Tag, "Failed to rebuild view " + Name + ": " + result.GetCode());
                }
                if (Database != null)
                {
                    Database.EndTransaction(result.IsSuccessful());
                }
            }
        }
        public void TestPutLargeAttachment()
        {
            var testAttachmentName = "test_attachment";
            var attachments        = database.Attachments;

            attachments.DeleteBlobs();
            Assert.AreEqual(0, attachments.Count());

            var status         = new Status();
            var rev1Properties = new Dictionary <string, object>();

            rev1Properties["foo"] = 1;
            rev1Properties["bar"] = false;
            database.BeginTransaction();
            var rev1 = database.PutRevision(new RevisionInternal(rev1Properties), null, false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());
            var largeAttachment = new StringBuilder();

            for (int i = 0; i < Database.BigAttachmentLength; i++)
            {
                largeAttachment.Append("big attachment!");
            }

            var attach1 = Encoding.UTF8.GetBytes(largeAttachment.ToString());

            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach1), rev1.GetSequence(),
                testAttachmentName, "text/plain", rev1.GetGeneration());
            database.EndTransaction(true);
            var attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName);

            Assert.AreEqual("text/plain", attachment.ContentType);
            var data = attachment.Content.ToArray();

            Assert.IsTrue(Arrays.Equals(attach1, data));
            attachment.Dispose();

            const DocumentContentOptions contentOptions = DocumentContentOptions.IncludeAttachments | DocumentContentOptions.BigAttachmentsFollow;
            var attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1.GetSequence(), contentOptions);
            var innerDict = (IDictionary <string, object>)attachmentDictForSequence[testAttachmentName];

            if (innerDict.ContainsKey("stub"))
            {
                if (((bool)innerDict["stub"]))
                {
                    throw new RuntimeException("Expected attachment dict 'stub' key to be true");
                }
                else
                {
                    throw new RuntimeException("Expected attachment dict to have 'stub' key");
                }
            }
            if (!innerDict.ContainsKey("follows"))
            {
                throw new RuntimeException("Expected attachment dict to have 'follows' key");
            }

            attachment.Dispose();

            var rev1WithAttachments = database.GetDocumentWithIDAndRev(
                rev1.GetDocId(), rev1.GetRevId(), contentOptions);

            var rev1WithAttachmentsProperties = rev1WithAttachments.GetProperties();
            var rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1WithAttachmentsProperties["_id"]);
            rev2Properties["foo"] = 2;
            database.BeginTransaction();
            var newRev = new RevisionInternal(rev2Properties);
            var rev2   = database.PutRevision(newRev, rev1WithAttachments.GetRevId(), false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());
            database.CopyAttachmentNamedFromSequenceToSequence(
                testAttachmentName, rev1WithAttachments.GetSequence(), rev2.GetSequence());
            database.EndTransaction(true);

            // Check the 2nd revision's attachment:
            var rev2FetchedAttachment = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName);

            Assert.AreEqual(attachment.Length, rev2FetchedAttachment.Length);
            AssertPropertiesAreEqual(attachment.Metadata, rev2FetchedAttachment.Metadata);
            Assert.AreEqual(attachment.ContentType, rev2FetchedAttachment.ContentType);
            rev2FetchedAttachment.Dispose();

            // Add a third revision of the same document:
            var rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetProperties().Get("_id"));
            rev3Properties["foo"] = 3;
            rev3Properties["baz"] = false;
            var rev3 = new RevisionInternal(rev3Properties);

            rev3 = database.PutRevision(rev3, rev2.GetRevId(), false, status);

            Assert.AreEqual(StatusCode.Created, status.GetCode());
            var attach3 = Encoding.UTF8.GetBytes("<html><blink>attach3</blink></html>");

            database.InsertAttachmentForSequenceWithNameAndType(
                new ByteArrayInputStream(attach3), rev3.GetSequence(),
                testAttachmentName, "text/html", rev3.GetGeneration());

            // Check the 3rd revision's attachment:
            var rev3FetchedAttachment = database.GetAttachmentForSequence(
                rev3.GetSequence(), testAttachmentName);

            data = rev3FetchedAttachment.Content.ToArray();
            Assert.IsTrue(Arrays.Equals(attach3, data));
            Assert.AreEqual("text/html", rev3FetchedAttachment.ContentType);
            rev3FetchedAttachment.Dispose();

            // TODO: why doesn't this work?
            // Assert.assertEquals(attach3.length, rev3FetchedAttachment.getLength());
            var blobKeys = database.Attachments.AllKeys();

            Assert.AreEqual(2, blobKeys.Count);
            database.Compact();
            blobKeys = database.Attachments.AllKeys();
            Assert.AreEqual(1, blobKeys.Count);
        }
		/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
		public virtual void TestValidations()
		{
            Database.ValidateDelegate validator = (Revision newRevision, ValidationContext context)=>
            {
                NUnit.Framework.Assert.IsNotNull(newRevision);
                NUnit.Framework.Assert.IsNotNull(context);
                NUnit.Framework.Assert.IsTrue(newRevision.Properties != null || newRevision.
                    IsDeletion);
                this._enclosing.validationCalled = true;
                bool hoopy = newRevision.IsDeletion || (newRevision.Properties.Get("towel"
                ) != null);
                Log.V(ValidationsTest.Tag, string.Format("--- Validating %s --> %b", newRevision.
                    Properties, hoopy));
                if (!hoopy)
                {
                    context.Reject("Where's your towel?");
                }
                return hoopy;
            };
			database.SetValidation("hoopy", validator);
			// POST a valid new document:
			IDictionary<string, object> props = new Dictionary<string, object>();
			props["name"] = "Zaphod Beeblebrox";
			props["towel"] = "velvet";
			RevisionInternal rev = new RevisionInternal(props, database);
			Status status = new Status();
			validationCalled = false;
			rev = database.PutRevision(rev, null, false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode());
			// PUT a valid update:
			props["head_count"] = 3;
			rev.SetProperties(props);
			validationCalled = false;
			rev = database.PutRevision(rev, rev.GetRevId(), false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
            NUnit.Framework.Assert.AreEqual(StatusCode.Created, status.GetCode());
			// PUT an invalid update:
			Sharpen.Collections.Remove(props, "towel");
			rev.SetProperties(props);
			validationCalled = false;
			bool gotExpectedError = false;
			try
			{
				rev = database.PutRevision(rev, rev.GetRevId(), false, status);
			}
			catch (CouchbaseLiteException e)
			{
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
			}
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
			// POST an invalid new document:
			props = new Dictionary<string, object>();
			props["name"] = "Vogon";
			props["poetry"] = true;
			rev = new RevisionInternal(props, database);
			validationCalled = false;
			gotExpectedError = false;
			try
			{
				rev = database.PutRevision(rev, null, false, status);
			}
			catch (CouchbaseLiteException e)
			{
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
			}
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
			// PUT a valid new document with an ID:
			props = new Dictionary<string, object>();
			props["_id"] = "ford";
			props["name"] = "Ford Prefect";
			props["towel"] = "terrycloth";
			rev = new RevisionInternal(props, database);
			validationCalled = false;
			rev = database.PutRevision(rev, null, false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.AreEqual("ford", rev.GetDocId());
			// DELETE a document:
			rev = new RevisionInternal(rev.GetDocId(), rev.GetRevId(), true, database);
			NUnit.Framework.Assert.IsTrue(rev.IsDeleted());
			validationCalled = false;
			rev = database.PutRevision(rev, rev.GetRevId(), false, status);
			NUnit.Framework.Assert.IsTrue(validationCalled);
			// PUT an invalid new document:
			props = new Dictionary<string, object>();
			props["_id"] = "petunias";
			props["name"] = "Pot of Petunias";
			rev = new RevisionInternal(props, database);
			validationCalled = false;
			gotExpectedError = false;
			try
			{
				rev = database.PutRevision(rev, null, false, status);
			}
			catch (CouchbaseLiteException e)
			{
                gotExpectedError = (e.GetCBLStatus().GetCode() == StatusCode.Forbidden);
			}
			NUnit.Framework.Assert.IsTrue(validationCalled);
			NUnit.Framework.Assert.IsTrue(gotExpectedError);
		}
        /// <exception cref="System.Exception"></exception>
        public virtual void TestAttachments()
        {
            string    testAttachmentName = "test_attachment";
            BlobStore attachments        = database.GetAttachments();

            NUnit.Framework.Assert.AreEqual(0, attachments.Count());
            NUnit.Framework.Assert.AreEqual(new HashSet <object>(), attachments.AllKeys());
            Status status = new Status();
            IDictionary <string, object> rev1Properties = new Dictionary <string, object>();

            rev1Properties.Put("foo", 1);
            rev1Properties.Put("bar", false);
            RevisionInternal rev1 = database.PutRevision(new RevisionInternal(rev1Properties,
                                                                              database), null, false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach1 = Sharpen.Runtime.GetBytesForString("This is the body of attach1");
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach1
                                                                                         ), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration());
            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            //We must set the no_attachments column for the rev to false, as we are using an internal
            //private API call above (database.insertAttachmentForSequenceWithNameAndType) which does
            //not set the no_attachments column on revs table
            try
            {
                ContentValues args = new ContentValues();
                args.Put("no_attachments=", false);
                database.GetDatabase().Update("revs", args, "sequence=?", new string[] { rev1.GetSequence
                                                                                             ().ToString() });
            }
            catch (SQLException e)
            {
                Log.E(Database.Tag, "Error setting rev1 no_attachments to false", e);
                throw new CouchbaseLiteException(Status.InternalServerError);
            }
            Attachment attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName
                                                                      );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment.GetContentType());
            byte[] data = IOUtils.ToByteArray(attachment.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            IDictionary <string, object> innerDict = new Dictionary <string, object>();

            innerDict.Put("content_type", "text/plain");
            innerDict.Put("digest", "sha1-gOHUOBmIMoDCrMuGyaLWzf1hQTE=");
            innerDict.Put("length", 27);
            innerDict.Put("stub", true);
            innerDict.Put("revpos", 1);
            IDictionary <string, object> attachmentDict = new Dictionary <string, object>();

            attachmentDict.Put(testAttachmentName, innerDict);
            IDictionary <string, object> attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent
                                                                         (rev1.GetSequence(), EnumSet.NoneOf <Database.TDContentOptions>());

            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            RevisionInternal gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1
                                                                        .GetRevId(), EnumSet.NoneOf <Database.TDContentOptions>());
            IDictionary <string, object> gotAttachmentDict = (IDictionary <string, object>)gotRev1
                                                             .GetProperties().Get("_attachments");

            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Check the attachment dict, with attachments included:
            Sharpen.Collections.Remove(innerDict, "stub");
            innerDict.Put("data", Base64.EncodeBytes(attach1));
            attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1
                                                                                          .GetSequence(), EnumSet.Of(Database.TDContentOptions.TDIncludeAttachments));
            NUnit.Framework.Assert.AreEqual(attachmentDict, attachmentDictForSequence);
            gotRev1 = database.GetDocumentWithIDAndRev(rev1.GetDocId(), rev1.GetRevId(), EnumSet
                                                       .Of(Database.TDContentOptions.TDIncludeAttachments));
            gotAttachmentDict = (IDictionary <string, object>)gotRev1.GetProperties().Get("_attachments"
                                                                                          );
            NUnit.Framework.Assert.AreEqual(attachmentDict, gotAttachmentDict);
            // Add a second revision that doesn't update the attachment:
            IDictionary <string, object> rev2Properties = new Dictionary <string, object>();

            rev2Properties.Put("_id", rev1.GetDocId());
            rev2Properties.Put("foo", 2);
            rev2Properties.Put("bazz", false);
            RevisionInternal rev2 = database.PutRevision(new RevisionInternal(rev2Properties,
                                                                              database), rev1.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            database.CopyAttachmentNamedFromSequenceToSequence(testAttachmentName, rev1.GetSequence
                                                                   (), rev2.GetSequence());
            // Add a third revision of the same document:
            IDictionary <string, object> rev3Properties = new Dictionary <string, object>();

            rev3Properties.Put("_id", rev2.GetDocId());
            rev3Properties.Put("foo", 2);
            rev3Properties.Put("bazz", false);
            RevisionInternal rev3 = database.PutRevision(new RevisionInternal(rev3Properties,
                                                                              database), rev2.GetRevId(), false, status);

            NUnit.Framework.Assert.AreEqual(Status.Created, status.GetCode());
            byte[] attach2 = Sharpen.Runtime.GetBytesForString("<html>And this is attach2</html>"
                                                               );
            database.InsertAttachmentForSequenceWithNameAndType(new ByteArrayInputStream(attach2
                                                                                         ), rev3.GetSequence(), testAttachmentName, "text/html", rev2.GetGeneration());
            // Check the 2nd revision's attachment:
            Attachment attachment2 = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/plain", attachment2.GetContentType());
            data = IOUtils.ToByteArray(attachment2.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach1, data));
            // Check the 3rd revision's attachment:
            Attachment attachment3 = database.GetAttachmentForSequence(rev3.GetSequence(), testAttachmentName
                                                                       );

            NUnit.Framework.Assert.AreEqual("text/html", attachment3.GetContentType());
            data = IOUtils.ToByteArray(attachment3.GetContent());
            NUnit.Framework.Assert.IsTrue(Arrays.Equals(attach2, data));
            IDictionary <string, object> attachmentDictForRev3 = (IDictionary <string, object>)
                                                                 database.GetAttachmentsDictForSequenceWithContent(rev3.GetSequence(), EnumSet.NoneOf
                                                                                                                   <Database.TDContentOptions>()).Get(testAttachmentName);

            if (attachmentDictForRev3.ContainsKey("follows"))
            {
                if (((bool)attachmentDictForRev3.Get("follows")) == true)
                {
                    throw new RuntimeException("Did not expected attachment dict 'follows' key to be true"
                                               );
                }
                else
                {
                    throw new RuntimeException("Did not expected attachment dict to have 'follows' key"
                                               );
                }
            }
            // Examine the attachment store:
            NUnit.Framework.Assert.AreEqual(2, attachments.Count());
            ICollection <BlobKey> expected = new HashSet <BlobKey>();

            expected.AddItem(BlobStore.KeyForBlob(attach1));
            expected.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected, attachments.AllKeys());
            database.Compact();
            // This clears the body of the first revision
            NUnit.Framework.Assert.AreEqual(1, attachments.Count());
            ICollection <BlobKey> expected2 = new HashSet <BlobKey>();

            expected2.AddItem(BlobStore.KeyForBlob(attach2));
            NUnit.Framework.Assert.AreEqual(expected2, attachments.AllKeys());
        }