コード例 #1
0
        internal RevisionInternal GetParentRevision(RevisionInternal rev)
        {
            // First get the parent's sequence:
            var seq = rev.GetSequence();
            if (seq > 0)
            {
                seq = LongForQuery("SELECT parent FROM revs WHERE sequence=?", new [] { Convert.ToString(seq) });
            }
            else
            {
                var docNumericID = GetDocNumericID(rev.GetDocId());
                if (docNumericID <= 0)
                {
                    return null;
                }
                var args = new [] { Convert.ToString(docNumericID), rev.GetRevId() };
                seq = LongForQuery("SELECT parent FROM revs WHERE doc_id=? and revid=?", args);
            }
            if (seq == 0)
            {
                return null;
            }

            // Now get its revID and deletion status:
            RevisionInternal result = null;
            var queryArgs = new [] { Convert.ToString(seq) };
            var queryString = "SELECT revid, deleted FROM revs WHERE sequence=?";

            Cursor cursor = null;
            try
            {
                cursor = StorageEngine.RawQuery(queryString, queryArgs);
                if (cursor.MoveToNext())
                {
                    string revId = cursor.GetString(0);
                    bool deleted = (cursor.GetInt(1) > 0);
                    result = new RevisionInternal(rev.GetDocId(), revId, deleted, this);
                    result.SetSequence(seq);
                }
            }
            finally
            {
                cursor.Close();
            }
            return result;
        }
コード例 #2
0
        /// <summary>Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev.
        ///     </summary>
        /// <remarks>
        /// Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev.
        /// Rev must already have its revID and sequence properties set.
        /// </remarks>
        internal IDictionary<String, Object> ExtraPropertiesForRevision(RevisionInternal rev, DocumentContentOptions contentOptions)
        {
            var docId = rev.GetDocId();
            var revId = rev.GetRevId();

            var sequenceNumber = rev.GetSequence();

            Debug.Assert((revId != null));
            Debug.Assert((sequenceNumber > 0));

            // Get attachment metadata, and optionally the contents:
            IDictionary<string, object> attachmentsDict = null;

            if (!contentOptions.HasFlag(DocumentContentOptions.NoAttachments))
            {
                attachmentsDict = GetAttachmentsDictForSequenceWithContent (sequenceNumber, contentOptions);
            }
            // Get more optional stuff to put in the properties:
            //OPT: This probably ends up making redundant SQL queries if multiple options are enabled.
            var localSeq = -1L;
            if (contentOptions.HasFlag(DocumentContentOptions.IncludeLocalSeq))
            {
                localSeq = sequenceNumber;
            }
            IDictionary<string, object> revHistory = null;
            if (contentOptions.HasFlag(DocumentContentOptions.IncludeRevs))
            {
                revHistory = GetRevisionHistoryDict(rev);
            }
            IList<object> revsInfo = null;
            if (contentOptions.HasFlag(DocumentContentOptions.IncludeRevsInfo))
            {
                revsInfo = new AList<object>();
                var revHistoryFull = GetRevisionHistory(rev);
                foreach (RevisionInternal historicalRev in revHistoryFull)
                {
                    var revHistoryItem = new Dictionary<string, object>();
                    var status = "available";
                    if (historicalRev.IsDeleted())
                    {
                        status = "deleted";
                    }

                    if (historicalRev.IsMissing())
                    {
                        status = "missing";
                    }
                    revHistoryItem.Put("rev", historicalRev.GetRevId());
                    revHistoryItem["status"] = status;
                    revsInfo.AddItem(revHistoryItem);
                }
            }
            IList<string> conflicts = null;
            if (contentOptions.HasFlag(DocumentContentOptions.IncludeConflicts))
            {
                var revs = GetAllRevisionsOfDocumentID(docId, true);
                if (revs.Count > 1)
                {
                    conflicts = new AList<string>();
                    foreach (RevisionInternal savedRev in revs)
                    {
                        if (!(savedRev.Equals(rev) || savedRev.IsDeleted()))
                        {
                            conflicts.AddItem(savedRev.GetRevId());
                        }
                    }
                }
            }

            var result = new Dictionary<string, object>();
            result["_id"] = docId;
            result["_rev"] = revId;

            if (rev.IsDeleted())
            {
                result["_deleted"] = true;
            }
            if (attachmentsDict != null)
            {
                result["_attachments"] = attachmentsDict;
            }
            if (localSeq > -1)
            {
                result["_local_seq"] = localSeq;
            }
            if (revHistory != null)
            {
                result["_revisions"] = revHistory;
            }
            if (revsInfo != null)
            {
                result["_revs_info"] = revsInfo;
            }
            if (conflicts != null)
            {
                result["_conflicts"] = conflicts;
            }
            return result;
        }
コード例 #3
0
        //Doesn't handle CouchbaseLiteException
        internal RevisionInternal LoadRevisionBody(RevisionInternal rev)
        {
            if (!IsOpen) {
                Log.W(TAG, "LoadRevisionBody called on closed database");
                return null;
            }

            if (rev.GetSequence() > 0) {
                var props = rev.GetProperties();
                if (props != null && props.GetCast<string>("_rev") != null && props.GetCast<string>("_id") != null) {
                    return rev;
                }
            }



            Debug.Assert(rev.GetDocId() != null && rev.GetRevId() != null);
            Storage.LoadRevisionBody(rev);
            return rev;
        }
コード例 #4
0
        // Apply the options in the URL query to the specified revision and create a new revision object
        internal static RevisionInternal ApplyOptions(DocumentContentOptions options, RevisionInternal rev, ICouchbaseListenerContext context,
            Database db, Status outStatus)
        {
            if ((options & (DocumentContentOptions.IncludeRevs | DocumentContentOptions.IncludeRevsInfo | DocumentContentOptions.IncludeConflicts |
                DocumentContentOptions.IncludeAttachments | DocumentContentOptions.IncludeLocalSeq)) != 0) {
                var dst = rev.GetProperties(); 
                if (options.HasFlag(DocumentContentOptions.IncludeLocalSeq)) {
                    dst["_local_seq"] = rev.GetSequence();
                }

                if (options.HasFlag(DocumentContentOptions.IncludeRevs)) {
                    var revs = db.GetRevisionHistory(rev, null);
                    dst["_revisions"] = Database.MakeRevisionHistoryDict(revs);
                }

                if (options.HasFlag(DocumentContentOptions.IncludeRevsInfo)) {
                    dst["_revs_info"] = db.Storage.GetRevisionHistory(rev, null).Select(x =>
                    {
                        string status = "available";
                        if(x.IsDeleted()) {
                            status = "deleted";
                        } else if(x.IsMissing()) {
                            status = "missing";
                        }

                        return new Dictionary<string, object> {
                            { "rev", x.GetRevId() },
                            { "status", status }
                        };
                    });
                }

                if (options.HasFlag(DocumentContentOptions.IncludeConflicts)) {
                    RevisionList revs = db.Storage.GetAllDocumentRevisions(rev.GetDocId(), true);
                    if (revs.Count > 1) {
                        dst["_conflicts"] = revs.Select(x =>
                        {
                            return x.Equals(rev) || x.IsDeleted() ? null : x.GetRevId();
                        });
                    }
                }

                RevisionInternal nuRev = new RevisionInternal(dst);
                if (options.HasFlag(DocumentContentOptions.IncludeAttachments)) {
                    bool attEncodingInfo = context != null && context.GetQueryParam<bool>("att_encoding_info", bool.TryParse, false);
                    if(!db.ExpandAttachments(nuRev, 0, false, !attEncodingInfo, outStatus)) {
                        return null;
                    }
                }

                rev = nuRev;
            }

            return rev;
        }
コード例 #5
0
        internal RevisionInternal RevisionByLoadingBody(RevisionInternal rev, Status outStatus)
        {
            // First check for no-op -- if we just need the default properties and already have them:
            if (rev.GetSequence() != 0) {
                var props = rev.GetProperties();
                if (props != null && props.ContainsKey("_rev") && props.ContainsKey("_id")) {
                    if (outStatus != null) {
                        outStatus.Code = StatusCode.Ok;
                    }

                    return rev;
                }
            }

            RevisionInternal nuRev = rev.CopyWithDocID(rev.GetDocId(), rev.GetRevId());
            try {
                LoadRevisionBody(nuRev);
            } catch(CouchbaseLiteException e) {
                if (outStatus != null) {
                    outStatus.Code = e.CBLStatus.Code;
                }

                nuRev = null;
            }

            return nuRev;
        }
 private void RemovePending(RevisionInternal revisionInternal)
 {
     long seq = revisionInternal.GetSequence();
     if (pendingSequences == null || pendingSequences.Count == 0)
     {
         Log.W(Log.TagSync, "%s: removePending() called w/ rev: %s, but pendingSequences empty"
             , this, revisionInternal);
         return;
     }
     bool wasFirst = (seq == pendingSequences.First());
     if (!pendingSequences.Contains(seq))
     {
         Log.W(Log.TagSync, "%s: removePending: sequence %s not in set, for rev %s", this, 
             seq, revisionInternal);
     }
     pendingSequences.Remove(seq);
     if (wasFirst)
     {
         // If I removed the first pending sequence, can advance the checkpoint:
         long maxCompleted;
         if (pendingSequences.Count == 0)
         {
             maxCompleted = maxPendingSequence;
         }
         else
         {
             maxCompleted = pendingSequences.First();
             --maxCompleted;
         }
         SetLastSequence(System.Convert.ToString(maxCompleted));
     }
 }
コード例 #7
0
ファイル: Database.cs プロジェクト: Redth/couchbase-lite-net
		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;
		}
 /// <summary>
 /// Creates a dictionary of metadata for one specific revision
 /// </summary>
 /// <returns>The metadata dictionary</returns>
 /// <param name="rev">The revision to examine</param>
 /// <param name="responseState">The current response state</param>
 public static IDictionary<string, object> ChangesDictForRev(RevisionInternal rev, DBMonitorCouchbaseResponseState responseState)
 {
     if (responseState.ChangesIncludeDocs) {
         var status = new Status();
         var rev2 = DocumentMethods.ApplyOptions(responseState.ContentOptions, rev, responseState.Context, responseState.Db, status);
         if (rev2 != null) {
             rev2.SetSequence(rev.GetSequence());
             rev = rev2;
         }
     }
     return new NonNullDictionary<string, object> {
         { "seq", rev.GetSequence() },
         { "id", rev.GetDocId() },
         { "changes", new List<object> { 
                 new Dictionary<string, object> { 
                     { "rev", rev.GetRevId() } 
                 } 
             } 
         },
         { "deleted", rev.IsDeleted() ? (object)true : null },
         { "doc", responseState.ChangesIncludeDocs ? rev.GetProperties() : null }
     };
 }
コード例 #9
0
        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);
        }
コード例 #10
0
ファイル: Database.cs プロジェクト: Redth/couchbase-lite-net
		public IDictionary<string, object> ExtraPropertiesForRevision(RevisionInternal rev
			, EnumSet<Database.TDContentOptions> contentOptions)
		{
			string docId = rev.GetDocId();
			string revId = rev.GetRevId();
			long sequenceNumber = rev.GetSequence();
			System.Diagnostics.Debug.Assert((revId != null));
			System.Diagnostics.Debug.Assert((sequenceNumber > 0));
			// Get attachment metadata, and optionally the contents:
			IDictionary<string, object> attachmentsDict = GetAttachmentsDictForSequenceWithContent
				(sequenceNumber, contentOptions);
			// Get more optional stuff to put in the properties:
			//OPT: This probably ends up making redundant SQL queries if multiple options are enabled.
			long localSeq = null;
			if (contentOptions.Contains(Database.TDContentOptions.TDIncludeLocalSeq))
			{
				localSeq = sequenceNumber;
			}
			IDictionary<string, object> revHistory = null;
			if (contentOptions.Contains(Database.TDContentOptions.TDIncludeRevs))
			{
				revHistory = GetRevisionHistoryDict(rev);
			}
			IList<object> revsInfo = null;
			if (contentOptions.Contains(Database.TDContentOptions.TDIncludeRevsInfo))
			{
				revsInfo = new AList<object>();
				IList<RevisionInternal> revHistoryFull = GetRevisionHistory(rev);
				foreach (RevisionInternal historicalRev in revHistoryFull)
				{
					IDictionary<string, object> revHistoryItem = new Dictionary<string, object>();
					string status = "available";
					if (historicalRev.IsDeleted())
					{
						status = "deleted";
					}
					if (historicalRev.IsMissing())
					{
						status = "missing";
					}
					revHistoryItem.Put("rev", historicalRev.GetRevId());
					revHistoryItem.Put("status", status);
					revsInfo.AddItem(revHistoryItem);
				}
			}
			IList<string> conflicts = null;
			if (contentOptions.Contains(Database.TDContentOptions.TDIncludeConflicts))
			{
				RevisionList revs = GetAllRevisionsOfDocumentID(docId, true);
				if (revs.Count > 1)
				{
					conflicts = new AList<string>();
					foreach (RevisionInternal historicalRev in revs)
					{
						if (!historicalRev.Equals(rev))
						{
							conflicts.AddItem(historicalRev.GetRevId());
						}
					}
				}
			}
			IDictionary<string, object> result = new Dictionary<string, object>();
			result.Put("_id", docId);
			result.Put("_rev", revId);
			if (rev.IsDeleted())
			{
				result.Put("_deleted", true);
			}
			if (attachmentsDict != null)
			{
				result.Put("_attachments", attachmentsDict);
			}
			if (localSeq != null)
			{
				result.Put("_local_seq", localSeq);
			}
			if (revHistory != null)
			{
				result.Put("_revisions", revHistory);
			}
			if (revsInfo != null)
			{
				result.Put("_revs_info", revsInfo);
			}
			if (conflicts != null)
			{
				result.Put("_conflicts", conflicts);
			}
			return result;
		}
コード例 #11
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);
		}
コード例 #12
0
        private void RemovePending(RevisionInternal revisionInternal)
        {
            lock (pendingSequences)
            {
                var seq = revisionInternal.GetSequence();
                var wasFirst = (seq == pendingSequences.FirstOrDefault());
                if (!pendingSequences.Contains(seq))
                {
                    Log.W(Tag, "Remove Pending: Sequence " + seq + " not in set, for rev " + revisionInternal);
                }

                pendingSequences.Remove(seq);
                if (wasFirst)
                {
                    // If removing the first pending sequence, can advance the checkpoint:
                    long maxCompleted;
                    if (pendingSequences.Count == 0)
                    {
                        maxCompleted = maxPendingSequence;
                    }
                    else
                    {
                        maxCompleted = pendingSequences.First();
                        --maxCompleted;
                    }
                    LastSequence = maxCompleted.ToString();
                }
            }
        }
コード例 #13
0
 private void AddPending(RevisionInternal revisionInternal)
 {
     lock(pendingSequences)
     {
         var seq = revisionInternal.GetSequence();
         pendingSequences.Add(seq);
         if (seq > maxPendingSequence)
         {
             maxPendingSequence = seq;
         }
     }
 }
コード例 #14
0
        /// <summary>
        /// Given a newly-added revision, adds the necessary attachment rows to the sqliteDb and
        /// stores inline attachments into the blob store.
        /// </summary>
        /// <remarks>
        /// Given a newly-added revision, adds the necessary attachment rows to the sqliteDb and
        /// stores inline attachments into the blob store.
        /// </remarks>
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        internal void ProcessAttachmentsForRevision(IDictionary<string, AttachmentInternal> attachments, RevisionInternal rev, long parentSequence)
        {
            Debug.Assert((rev != null));
            var newSequence = rev.GetSequence();
            Debug.Assert((newSequence > parentSequence));
            var generation = rev.GetGeneration();
            Debug.Assert((generation > 0));

            // If there are no attachments in the new rev, there's nothing to do:
            IDictionary<string, object> revAttachments = null;
            var properties = rev.GetProperties ();
            if (properties != null)
            {
                revAttachments = properties.Get("_attachments").AsDictionary<string, object>();
            }

            if (revAttachments == null || revAttachments.Count == 0 || rev.IsDeleted())
            {
                return;
            }

            foreach (string name in revAttachments.Keys)
            {
                var attachment = attachments.Get(name);
                if (attachment != null)
                {
                    // Determine the revpos, i.e. generation # this was added in. Usually this is
                    // implicit, but a rev being pulled in replication will have it set already.
                    if (attachment.GetRevpos() == 0)
                    {
                        attachment.SetRevpos(generation);
                    }
                    else
                    {
                        if (attachment.GetRevpos() > generation)
                        {
                            Log.W(Tag, string.Format("Attachment {0} {1} has unexpected revpos {2}, setting to {3}", rev, name, attachment.GetRevpos(), generation));
                            attachment.SetRevpos(generation);
                        }
                    }
                    // Finally insert the attachment:
                    InsertAttachmentForSequence(attachment, newSequence);
                }
                else
                {
                    // It's just a stub, so copy the previous revision's attachment entry:
                    //? Should I enforce that the type and digest (if any) match?
                    CopyAttachmentNamedFromSequenceToSequence(name, parentSequence, newSequence);
                }
            }
        }
コード例 #15
0
ファイル: Database.cs プロジェクト: Redth/couchbase-lite-net
		public RevisionInternal UpdateAttachment(string filename, InputStream contentStream
			, string contentType, string docID, string oldRevID)
		{
			bool isSuccessful = false;
			if (filename == null || filename.Length == 0 || (contentStream != null && contentType
				 == null) || (oldRevID != null && docID == null) || (contentStream != null && docID
				 == null))
			{
				throw new CouchbaseLiteException(Status.BadRequest);
			}
			BeginTransaction();
			try
			{
				RevisionInternal oldRev = new RevisionInternal(docID, oldRevID, false, this);
				if (oldRevID != null)
				{
					// Load existing revision if this is a replacement:
					try
					{
						LoadRevisionBody(oldRev, EnumSet.NoneOf<Database.TDContentOptions>());
					}
					catch (CouchbaseLiteException e)
					{
						if (e.GetCBLStatus().GetCode() == Status.NotFound && ExistsDocumentWithIDAndRev(docID
							, null))
						{
							throw new CouchbaseLiteException(Status.Conflict);
						}
					}
					IDictionary<string, object> oldRevProps = oldRev.GetProperties();
					IDictionary<string, object> attachments = null;
					if (oldRevProps != null)
					{
						attachments = (IDictionary<string, object>)oldRevProps.Get("_attachments");
					}
					if (contentStream == null && attachments != null && !attachments.ContainsKey(filename
						))
					{
						throw new CouchbaseLiteException(Status.NotFound);
					}
					// Remove the _attachments stubs so putRevision: doesn't copy the rows for me
					// OPT: Would be better if I could tell loadRevisionBody: not to add it
					if (attachments != null)
					{
						IDictionary<string, object> properties = new Dictionary<string, object>(oldRev.GetProperties
							());
						Sharpen.Collections.Remove(properties, "_attachments");
						oldRev.SetBody(new Body(properties));
					}
				}
				else
				{
					// If this creates a new doc, it needs a body:
					oldRev.SetBody(new Body(new Dictionary<string, object>()));
				}
				// Create a new revision:
				Status putStatus = new Status();
				RevisionInternal newRev = PutRevision(oldRev, oldRevID, false, putStatus);
				if (newRev == null)
				{
					return null;
				}
				if (oldRevID != null)
				{
					// Copy all attachment rows _except_ for the one being updated:
					string[] args = new string[] { System.Convert.ToString(newRev.GetSequence()), System.Convert.ToString
						(oldRev.GetSequence()), filename };
					database.ExecSQL("INSERT INTO attachments " + "(sequence, filename, key, type, length, revpos) "
						 + "SELECT ?, filename, key, type, length, revpos FROM attachments " + "WHERE sequence=? AND filename != ?"
						, args);
				}
				if (contentStream != null)
				{
					// If not deleting, add a new attachment entry:
					InsertAttachmentForSequenceWithNameAndType(contentStream, newRev.GetSequence(), filename
						, contentType, newRev.GetGeneration());
				}
				isSuccessful = true;
				return newRev;
			}
			catch (SQLException e)
			{
				Log.E(Tag, "Error updating attachment", e);
				throw new CouchbaseLiteException(new Status(Status.InternalServerError));
			}
			finally
			{
				EndTransaction(isSuccessful);
			}
		}
コード例 #16
0
        /// <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;
        }
        private void AddPending(RevisionInternal revisionInternal)
        {
            lock(_pendingSequences)
            {
                var seq = revisionInternal.GetSequence();
                if (!_pendingSequences.ContainsKey(seq)) {
                    _pendingSequences.Add(seq, 0);
                }

                if (seq > _maxPendingSequence)
                {
                    _maxPendingSequence = seq;
                }
            }
        }
コード例 #18
0
        /// <summary>
        /// Given a newly-added revision, adds the necessary attachment rows to the sqliteDb and
        /// stores inline attachments into the blob store.
        /// </summary>
        /// <remarks>
        /// Given a newly-added revision, adds the necessary attachment rows to the sqliteDb and
        /// stores inline attachments into the blob store.
        /// </remarks>
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        internal void ProcessAttachmentsForRevision(IDictionary<string, AttachmentInternal> attachments, RevisionInternal rev, long parentSequence)
        {
            Debug.Assert((rev != null));
            var newSequence = rev.GetSequence();
            Debug.Assert((newSequence > parentSequence));
            var generation = rev.GetGeneration();
            Debug.Assert((generation > 0));

            // If there are no attachments in the new rev, there's nothing to do:
            IDictionary<string, object> revAttachments = null;
            var properties = rev.GetProperties ();
            if (properties != null) {
                revAttachments = properties.Get("_attachments").AsDictionary<string, object>();
            }

            if (revAttachments == null || revAttachments.Count == 0 || rev.IsDeleted()) {
                return;
            }

            foreach (string name in revAttachments.Keys)
            {
                var attachment = attachments.Get(name);
                if (attachment != null)
                {
                    // Determine the revpos, i.e. generation # this was added in. Usually this is
                    // implicit, but a rev being pulled in replication will have it set already.
                    if (attachment.RevPos == 0)
                    {
                        attachment.RevPos = generation;
                    }
                    else
                    {
                        if (attachment.RevPos > generation)
                        {
                            Log.W(TAG, string.Format("Attachment {0} {1} has unexpected revpos {2}, setting to {3}", rev, name, attachment.RevPos, generation));
                            attachment.RevPos = generation;
                        }
                    }
                }
            }
        }
        private void RemovePending(RevisionInternal revisionInternal)
        {
            lock (_pendingSequences)
            {
                var seq = revisionInternal.GetSequence();
                var wasFirst = (_pendingSequences.Count > 0 && seq == _pendingSequences.ElementAt(0).Key);
                if (!_pendingSequences.ContainsKey(seq))
                {
                    Log.W(TAG, "Remove Pending: Sequence " + seq + " not in set, for rev " + revisionInternal);
                }

                _pendingSequences.Remove(seq);
                if (wasFirst)
                {
                    // If removing the first pending sequence, can advance the checkpoint:
                    long maxCompleted;
                    if (_pendingSequences.Count == 0)
                    {
                        maxCompleted = _maxPendingSequence;
                    }
                    else
                    {
                        maxCompleted = _pendingSequences.ElementAt(0).Key;
                        --maxCompleted;
                    }
                    LastSequence = maxCompleted.ToString();
                }

                if (_pendingSequences.Count == 0) {
                    FireTrigger(Continuous ? ReplicationTrigger.WaitingForChanges : ReplicationTrigger.StopGraceful);
                }
            }
        }
コード例 #20
0
        //Doesn't handle CouchbaseLiteException
        internal RevisionInternal LoadRevisionBody(RevisionInternal rev)
        {
            if (rev.GetSequence() > 0) {
                var props = rev.GetProperties();
                if (props != null && props.GetCast<string>("_rev") != null && props.GetCast<string>("_id") != null) {
                    return rev;
                }
            }

            Debug.Assert(rev.GetDocId() != null && rev.GetRevId() != null);
            Storage.LoadRevisionBody(rev);
            return rev;
        }
 private void AddPending(RevisionInternal revisionInternal)
 {
     long seq = revisionInternal.GetSequence();
     pendingSequences.AddItem(seq);
     if (seq > maxPendingSequence)
     {
         maxPendingSequence = seq;
     }
 }