private unsafe void SaveProperties(IDictionary <string, object> props, string docID)
        {
            Db.InBatch(() =>
            {
                var tricky =
                    (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db.c4db, docID, true, err));
                var put = new C4DocPutRequest {
                    docID        = tricky->docID,
                    history      = &tricky->revID,
                    historyCount = 1,
                    save         = true
                };

                var enc = Native.c4db_getSharedFleeceEncoder(Db.c4db);
                props.FLEncode(enc);
                var body = NativeRaw.FLEncoder_Finish(enc, null);
                put.body = (C4Slice)body;

                LiteCoreBridge.Check(err =>
                {
                    var localPut = put;
                    var retVal   = Native.c4doc_put(Db.c4db, &localPut, null, err);
                    Native.FLSliceResult_Free(body);
                    return(retVal);
                });
            });
        }
Exemple #2
0
        private void CreateDocs(uint n, bool verbose = false)
        {
            _R = new Random(42);
            using (var t = new TransactionHelper(_db)) {
                for (int i = 0; i < n; i++)
                {
                    var    docID = "doc-" + i.ToString();
                    double lat0  = RandomLatitude();
                    double lon0  = RandomLongitude();
                    double lat1  = Math.Min(lat0 + 0.5, 90.0);
                    double lon1  = Math.Min(lon0 + 0.5, 180.0);
                    var    body  = String.Format("({0}, {1}, {2}, {3})", lon0, lat0, lon1, lat1);

                    var rq = new C4DocPutRequest();
                    rq.docID = docID;
                    rq.body  = body;
                    rq.save  = true;
                    C4Error error;
                    var     doc = Native.c4doc_put(_db, rq, null, &error);
                    Assert.IsTrue(doc != null);
                    if (verbose)
                    {
                        Console.WriteLine("Added {0} -> {1}", docID, body);
                    }
                }
            }
        }
Exemple #3
0
        public void TestDeleteIndexedDoc()
        {
            RunTestVariants(() => {
                LiteCoreBridge.Check(err => Native.c4db_createIndex(Db, "test", Json5("[['length()', ['.name.first']]]"),
                                                                    C4IndexType.ValueIndex, null, err));

                // Delete doc "0000015":
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, "0000015", true, err));
                    var rq  = new C4DocPutRequest {
                        docID        = FLSlice.Constant("0000015"),
                        history      = (FLSlice *)&doc->revID,
                        historyCount = 1,
                        revFlags     = C4RevisionFlags.Deleted,
                        save         = true
                    };
                    var updatedDoc = (C4Document *)LiteCoreBridge.Check(err => {
                        var localRq = rq;
                        return(Native.c4doc_put(Db, &localRq, null, err));
                    });

                    Native.c4doc_free(doc);
                    Native.c4doc_free(updatedDoc);
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                // Now run a query that would have returned the deleted doc, if it weren't deleted:
                Compile(Json5("['=', ['length()', ['.name.first']], 9]"));
                Run().Should().Equal(new[] { "0000099" }, "because otherwise the query returned incorrect results");
            });
        }
Exemple #4
0
        public void TestInvalidDocID()
        {
            RunTestVariants(() =>
            {
                NativePrivate.c4log_warnOnErrors(false);
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    Action <C4Slice> checkPutBadDocID = (C4Slice docID) =>
                    {
                        C4Error e;
                        var rq   = new C4DocPutRequest();
                        rq.body  = Body;
                        rq.save  = true;
                        rq.docID = docID;
                        ((long)Native.c4doc_put(Db, &rq, null, &e)).Should()
                        .Be(0, "because the invalid doc ID should cause the put to fail");
                        e.domain.Should().Be(C4ErrorDomain.LiteCoreDomain);
                        e.code.Should().Be((int)C4ErrorCode.BadDocID);
                    };

                    checkPutBadDocID(C4Slice.Constant(""));
                    string tooLong = new string(Enumerable.Repeat('x', 241).ToArray());
                    using (var tooLong_ = new C4String(tooLong)) {
                        checkPutBadDocID(tooLong_.AsC4Slice());
                    }

                    checkPutBadDocID(C4Slice.Constant("oops\x00oops")); // Bad UTF-8
                    checkPutBadDocID(C4Slice.Constant("oops\noops"));   // Control characters
                } finally {
                    NativePrivate.c4log_warnOnErrors(true);
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }
            });
        }
Exemple #5
0
        protected void CreateRev(string docID, C4Slice revID, C4Slice body, bool isNew = true)
        {
            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            try {
                var curDoc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, docID,
                                                                                        false, err));
                var history = new[] { revID, curDoc->revID };
                fixed(C4Slice *h = history)
                {
                    var rq = new C4DocPutRequest {
                        existingRevision = true,
                        docID            = curDoc->docID,
                        history          = h,
                        historyCount     = curDoc->revID.buf != null ? 2UL : 1UL,
                        body             = body,
                        deletion         = body.buf == null,
                        save             = true
                    };

                    var doc = (C4Document *)LiteCoreBridge.Check(err => {
                        var localRq = rq;
                        return(Native.c4doc_put(Db, &localRq, null, err));
                    });

                    Native.c4doc_free(doc);
                    Native.c4doc_free(curDoc);
                }
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }
        }
Exemple #6
0
        internal void CreateRev(C4Database *db, string docID, C4Slice revID, C4Slice body, C4RevisionFlags flags = (C4RevisionFlags)0)
        {
            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(db, err));
            try {
                var curDoc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(db, docID,
                                                                                        false, err));
                var history = new[] { revID, curDoc->revID };
                fixed(C4Slice *h = history)
                {
                    var rq = new C4DocPutRequest {
                        existingRevision = true,
                        docID            = curDoc->docID,
                        history          = h,
                        historyCount     = curDoc->revID.buf != null ? 2UL : 1UL,
                        body             = body,
                        revFlags         = flags,
                        save             = true
                    };

                    var doc = (C4Document *)LiteCoreBridge.Check(err => {
                        var localRq = rq;
                        return(Native.c4doc_put(db, &localRq, null, err));
                    });

                    Native.c4doc_free(doc);
                    Native.c4doc_free(curDoc);
                }
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(db, true, err));
            }
        }
Exemple #7
0
        private void CreateConflictingRev(C4Database *db,
                                          FLSlice docID,
                                          FLSlice parentRevID,
                                          FLSlice newRevID,
                                          FLSlice body,
                                          C4RevisionFlags flags)
        {
            var history = new[] { newRevID, parentRevID };

            fixed(FLSlice *h = history)
            {
                var rq = new C4DocPutRequest
                {
                    existingRevision = true,
                    allowConflict    = true,
                    docID            = docID,
                    history          = h,
                    historyCount     = parentRevID.buf != null ? 2UL : 1UL,
                    body             = body,
                    revFlags         = flags,
                    save             = true
                };

                var doc = (C4Document *)LiteCoreBridge.Check(err => {
                    var localRq = rq;
                    return(Native.c4doc_put(db, &localRq, null, err));
                });

                Native.c4doc_release(doc);
            }
        }
Exemple #8
0
        public void TestDuplicateRev()
        {
            RunTestVariants(() =>
            {
                var docID = "mydoc";
                var body  = JSON2Fleece("{'key':'value'}");
                var doc   = PutDoc(docID, null, (FLSlice)body);
                var revID = doc->revID.CreateString();
                Native.c4doc_release(doc);

                Native.FLSliceResult_Release(body);
                body        = JSON2Fleece("{'key':'newvalue'}");
                doc         = PutDoc(docID, revID, (FLSlice)body);
                var revID2a = doc->revID.CreateString();
                Native.c4doc_release(doc);

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                var success = false;
                try {
                    using (var docID_ = new C4String(docID))
                        using (var revID_ = new C4String(revID)) {
                            var history = new FLSlice[] { revID_.AsFLSlice() };
                            fixed(FLSlice * history_ = history)
                            {
                                var rq = new C4DocPutRequest
                                {
                                    allowConflict = true,
                                    docID         = docID_.AsFLSlice(),
                                    history       = history_,
                                    historyCount  = 1,
                                    body          = (FLSlice)body,
                                    revFlags      = 0,
                                    save          = true
                                };

                                doc = (C4Document *)LiteCoreBridge.Check(err =>
                                {
                                    var local = rq;
                                    return(Native.c4doc_put(Db, &local, null, err));
                                });

                                doc->docID.CreateString().Should().Be(docID);
                                Native.FLSliceResult_Release(body);
                            }
                        }
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, success, err));
                }

                var revID2b = doc->revID.CreateString();
                Native.c4doc_release(doc);

                revID2b.Should().Be(revID2a, "because an identical revision was inserted");
            });
        }
        internal void AddPersonInState(string docID, string state, string firstName = null)
        {
            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            C4Error error;
            var     enc = Native.c4db_getSharedFleeceEncoder(Db);

            try {
                Native.FLEncoder_BeginDict(enc, 3);
                Native.FLEncoder_WriteKey(enc, "custom");
                Native.FLEncoder_WriteBool(enc, true);
                if (!string.IsNullOrEmpty(firstName))
                {
                    Native.FLEncoder_WriteKey(enc, "name");
                    Native.FLEncoder_BeginDict(enc, 2);
                    Native.FLEncoder_WriteKey(enc, "first");
                    Native.FLEncoder_WriteString(enc, firstName);
                    Native.FLEncoder_WriteKey(enc, "last");
                    Native.FLEncoder_WriteString(enc, "lastname");
                    Native.FLEncoder_EndDict(enc);
                }

                Native.FLEncoder_WriteKey(enc, "contact");
                Native.FLEncoder_BeginDict(enc, 1);
                Native.FLEncoder_WriteKey(enc, "address");
                Native.FLEncoder_BeginDict(enc, 1);
                Native.FLEncoder_WriteKey(enc, "state");
                Native.FLEncoder_WriteString(enc, state);
                Native.FLEncoder_EndDict(enc);
                Native.FLEncoder_EndDict(enc);
                Native.FLEncoder_EndDict(enc);

                // Save document:
                FLSliceResult body = NativeRaw.FLEncoder_Finish(enc, null);
                //REQUIRE(body.buf);
                using (var docID_ = new C4String(docID)){
                    var rq = new C4DocPutRequest {
                        allowConflict = false,
                        docID         = docID_.AsFLSlice(),
                        allocedBody   = body,
                        save          = true
                    };

                    C4Document *doc;
                    doc = Native.c4doc_put(Db, &rq, null, &error);
                    ((long)doc).Should().NotBe(0, "because otherwise the put failed");
                    Native.c4doc_release(doc);
                    Native.FLSliceResult_Release(body);
                }
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }
        }
Exemple #10
0
        private C4Document *PutDoc(C4Database *db, string docID, string revID, FLSlice body, C4RevisionFlags flags, C4Error *error = null)
        {
            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(db, err));
            var success = false;

            try {
                var encoded = EncodeBodyIfJSON(body);
                using (var docID_ = new C4String(docID))
                    using (var revID_ = new C4String(revID)) {
                        var history = new FLSlice[] { revID_.AsFLSlice() };
                        fixed(FLSlice *history_ = history)
                        {
                            var rq = new C4DocPutRequest
                            {
                                allowConflict = false,
                                docID         = docID_.AsFLSlice(),
                                history       = revID == null ? null : history_,
                                historyCount  = revID == null ? 0UL : 1UL,
                                body          = encoded.buf == null ? body : (FLSlice)encoded,
                                revFlags      = flags,
                                remoteDBID    = _remoteDocID,
                                save          = true
                            };

                            C4Document *doc;

                            if (error != null)
                            {
                                var local = rq;
                                doc = Native.c4doc_put(db, &local, null, error);
                            }
                            else
                            {
                                doc = (C4Document *)LiteCoreBridge.Check(err =>
                                {
                                    var local = rq;
                                    return(Native.c4doc_put(db, &local, null, err));
                                });
                            }

                            Native.FLSliceResult_Release(encoded);
                            success = true;
                            return(doc);
                        }
                    }
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(db, success, err));
            }
        }
Exemple #11
0
        protected override void SetupVariant(int option)
        {
            base.SetupVariant(option);

            var c = new List <char>(Enumerable.Repeat('a', SizeOfDocument));

            c.Add((char)0);
            var content = new string(c.ToArray());

            C4Error err;

            Native.c4db_beginTransaction(Db, &err).Should().BeTrue("because starting a transaction should succeed");
            var rng = new Random();

            for (int i = 0; i < NumDocuments; i++)
            {
                using (var docID = new C4String($"doc-{rng.Next():D8}-{rng.Next():D8}-{rng.Next():D8}-{i:D4}"))
                    using (var revID = new C4String("1-deadbeefcafebabe80081e50"))
                        using (var json = new C4String("{{\"content\":\"{content}\"}}")) {
                            var history = IsRevTrees() ? new C4String[1] {
                                new C4String("1-deadbeefcafebabe80081e50")
                            }
                        : new C4String[1] {
                                new C4String("1@deadbeefcafebabe80081e50")
                            };

                            var rawHistory = history.Select(x => x.AsC4Slice()).ToArray();
                            fixed(C4Slice *rawHistory_ = rawHistory)
                            {
                                var rq = new C4DocPutRequest();

                                rq.existingRevision = true;
                                rq.docID            = docID.AsC4Slice();
                                rq.history          = rawHistory_;
                                rq.historyCount     = 1;
                                rq.body             = json.AsC4Slice();
                                rq.save             = true;
                                var doc = Native.c4doc_put(Db, &rq, null, &err);

                                ((long)doc).Should().NotBe(0, $"because otherwise the put failed");
                                Native.c4doc_free(doc);
                            }
                        }
            }

            Native.c4db_endTransaction(Db, true, &err).Should().BeTrue("because otherwise the transaction failed to end");
            Console.WriteLine($"Created {NumDocuments} docs");
            Native.c4db_getDocumentCount(Db).Should().Be(NumDocuments, "because the number of documents should be the number that was just inserted");
        }
Exemple #12
0
        private C4Document *PutDoc(C4Database *db, string docID, string revID, string body, C4RevisionFlags flags, C4Error *error = null)
        {
            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(db, err));
            var success = false;

            try {
                using (var docID_ = new C4String(docID))
                    using (var revID_ = new C4String(revID))
                        using (var body_ = new C4String(body)) {
                            var history = new C4Slice[] { revID_.AsC4Slice() };
                            fixed(C4Slice *history_ = history)
                            {
                                var rq = new C4DocPutRequest
                                {
                                    allowConflict = false,
                                    docID         = docID_.AsC4Slice(),
                                    history       = revID == null ? null : history_,
                                    historyCount  = revID == null ? 0UL : 1UL,
                                    body          = body_.AsC4Slice(),
                                    revFlags      = flags,
                                    save          = true
                                };

                                C4Document *doc;

                                if (error != null)
                                {
                                    var local = rq;
                                    doc = Native.c4doc_put(db, &local, null, error);
                                }
                                else
                                {
                                    doc = (C4Document *)LiteCoreBridge.Check(err =>
                                    {
                                        var local = rq;
                                        return(Native.c4doc_put(db, &local, null, err));
                                    });
                                }

                                success = true;
                                return(doc);
                            }
                        }
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(db, success, err));
            }
        }
Exemple #13
0
        public void TestPut()
        {
            C4Error error;

            using (var t = new TransactionHelper(_db)) {
                C4DocPutRequest rq = new C4DocPutRequest();
                rq.docID = DOC_ID;
                rq.body  = BODY;
                rq.save  = true;
                var doc = Native.c4doc_put(_db, rq, null, &error);
                Assert.IsTrue(doc != null);
                Assert.IsTrue(doc->docID.Equals(DOC_ID));
                const string expectedRevID = "1-c10c25442d9fe14fa3ca0db4322d7f1e43140fab";
                Assert.IsTrue(doc->revID.Equals(expectedRevID));
                Assert.AreEqual(C4DocumentFlags.Exists, doc->flags);
                Assert.IsTrue(doc->selectedRev.revID.Equals(expectedRevID));
                Native.c4doc_free(doc);

                // Update doc:
                rq.body    = "{\"ok\":\"go\"}";
                rq.history = new[] { expectedRevID };
                ulong commonAncestorIndex;
                doc = Native.c4doc_put(_db, rq, &commonAncestorIndex, &error);
                Assert.IsTrue(doc != null);
                Assert.AreEqual(1UL, commonAncestorIndex);
                const string expectedRev2ID = "2-32c711b29ea3297e27f3c28c8b066a68e1bb3f7b";
                Assert.IsTrue(doc->revID.Equals(expectedRev2ID));
                Assert.AreEqual(C4DocumentFlags.Exists, doc->flags);
                Assert.IsTrue(doc->selectedRev.revID.Equals(expectedRev2ID));
                Native.c4doc_free(doc);

                // Insert existing rev:
                rq.body             = "{\"from\":\"elsewhere\"}";
                rq.existingRevision = true;
                rq.history          = new[] { REV_2_ID, expectedRevID };
                doc = Native.c4doc_put(_db, rq, &commonAncestorIndex, &error);
                Assert.IsTrue(doc != null);
                Assert.AreEqual(1UL, commonAncestorIndex);
                Assert.IsTrue(doc->revID.Equals(REV_2_ID));
                Assert.AreEqual(C4DocumentFlags.Exists | C4DocumentFlags.Conflicted, doc->flags);
                Assert.IsTrue(doc->selectedRev.revID.Equals(REV_2_ID));
                Native.c4doc_free(doc);
            }
        }
Exemple #14
0
        internal C4BlobKey[] AddDocWithAttachments(C4Slice docID, List <string> atts, string contentType)
        {
            var keys = new List <C4BlobKey>();
            var json = new StringBuilder();

            json.Append("{attached: [");
            foreach (var att in atts)
            {
                var key = new C4BlobKey();
                LiteCoreBridge.Check(err =>
                {
                    var localKey = key;
                    var retVal   = Native.c4blob_create(Native.c4db_getBlobStore(Db, null), Encoding.UTF8.GetBytes(att),
                                                        null, &localKey, err);
                    key = localKey;
                    return(retVal);
                });

                keys.Add(key);
                var keyStr = Native.c4blob_keyToString(key);
                json.Append(
                    $"{{'{Constants.ObjectTypeProperty}': '{Constants.ObjectTypeBlob}', 'digest': '{keyStr}', length: {att.Length}, 'content_type': '{contentType}'}},");
            }

            json.Append("]}");
            var jsonStr = Native.FLJSON5_ToJSON(json.ToString(), null);

            using (var jsonStr_ = new C4String(jsonStr)) {
                C4Error error;
                var     body = NativeRaw.c4db_encodeJSON(Db, jsonStr_.AsC4Slice(), &error);
                ((long)body.buf).Should().NotBe(0, "because otherwise the encode failed");

                var rq = new C4DocPutRequest();
                rq.docID    = docID;
                rq.revFlags = C4RevisionFlags.HasAttachments;
                rq.body     = (C4Slice)body;
                rq.save     = true;
                var doc = Native.c4doc_put(Db, &rq, null, &error);
                Native.c4slice_free(body);
                ((long)doc).Should().NotBe(0, "because otherwise the put failed");
                Native.c4doc_free(doc);
                return(keys.ToArray());
            }
        }
Exemple #15
0
        private void CreateDocs(uint n, bool verbose = false)
        {
            var rng = new Random(42);

            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            try {
                for (uint i = 0; i < n; ++i)
                {
                    var docID = i.ToString();
                    var lat0  = RandomLat(rng);
                    var lon0  = RandomLon(rng);
                    var lat1  = Math.Min(lat0 + 0.5, 90.0);
                    var lon1  = Math.Min(lon0 + 0.5, 180.0);
                    var body  = $"({lon0}, {lat0}, {lon1}, {lat1})";

                    using (var docID_ = new C4String(docID))
                        using (var body_ = new C4String(body)) {
                            var rq = new C4DocPutRequest {
                                docID = docID_.AsC4Slice(),
                                body  = body_.AsC4Slice(),
                                save  = true
                            };

                            var doc = (C4Document *)LiteCoreBridge.Check(err => {
                                var localRq = rq;
                                return(Native.c4doc_put(Db, &localRq, null, err));
                            });

                            if (verbose)
                            {
                                Console.WriteLine($"Added {docID} --> {body}");
                            }

                            Native.c4doc_free(doc);
                        }
                }
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }
        }
        public RevisionInternal PutRevision(string inDocId, RevisionID inPrevRevId, IDictionary<string, object> properties,
            bool deleting, bool allowConflict, Uri source, StoreValidation validationBlock)
        {
            if(_config.HasFlag(C4DatabaseFlags.ReadOnly)) {
                throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.Forbidden, TAG,
                    "Attempting to write to a readonly database (PutRevision)");
            }

            var json = default(string);
            if(properties != null) {
                json = Manager.GetObjectMapper().WriteValueAsString(Database.StripDocumentJSON(properties), true);
            } else {
                json = "{}";
            }

            if(inDocId == null) {
                inDocId = Misc.CreateGUID();
            }

            C4Document* doc = null;
            var putRev = default(RevisionInternal);
            var change = default(DocumentChange);
            var success = RunInTransaction(() =>
            {
                try {
                    var docId = inDocId;
                    var prevRevId = inPrevRevId;
                    C4DocPutRequest rq = new C4DocPutRequest {
                        body = json,
                        docID = docId,
                        deletion = deleting,
                        hasAttachments = properties?.Get("_attachments") != null,
                        existingRevision = false,
                        allowConflict = allowConflict,
                        history = prevRevId == null ? null : new[] { prevRevId.ToString() },
                        save = false
                    };

                    UIntPtr commonAncestorIndex = UIntPtr.Zero;
                    doc = (C4Document*)ForestDBBridge.Check(err =>
                    {
                        UIntPtr tmp;
                        var retVal = Native.c4doc_put(Forest, rq, &tmp, err);
                        commonAncestorIndex = tmp;
                        return retVal;
                    });

                    if(docId == null) {
                        docId = (string)doc->docID;
                    }

                    var newRevID = doc->selectedRev.revID.AsRevID();

                    Body body = null;
                    if(properties != null) {
                        properties.SetDocRevID(docId, newRevID);
                        body = new Body(properties);
                    }

                    putRev = new RevisionInternal(docId, newRevID, deleting, body);
                    if((uint)commonAncestorIndex == 0U) {
                        return true;
                    }

                    if(validationBlock != null) {
                        var prevRev = default(RevisionInternal);
                        if(Native.c4doc_selectParentRevision(doc)) {
                            prevRev = new ForestRevisionInternal(doc, false);
                        }

                        var status = validationBlock(putRev, prevRev, prevRev == null ? null : prevRev.RevID);
                        if(status.IsError) {
                            Log.To.Validation.I(TAG, "{0} ({1}) failed validation", new SecureLogString(docId, LogMessageSensitivity.PotentiallyInsecure), new SecureLogString(newRevID, LogMessageSensitivity.PotentiallyInsecure));
                            throw new CouchbaseLiteException("A document failed validation", status.Code);
                        }
                    }

                    var isWinner = SaveDocument(doc, newRevID, properties);
                    putRev.Sequence = (long)doc->sequence;
                    change = ChangeWithNewRevision(putRev, isWinner, doc, null);
                    return true;
                } finally {
                    Native.c4doc_free(doc);
                }
            });

            if(!success) {
                return null;
            }

            if(Delegate != null && change != null) {
                Delegate.DatabaseStorageChanged(change);
            }

            return putRev;
        }
Exemple #17
0
        public void TestMaxRevTreeDepth()
        {
            RunTestVariants(() =>
            {
                if (IsRevTrees())
                {
                    Native.c4db_getMaxRevTreeDepth(Db).Should().Be(20, "because that is the default");
                    Native.c4db_setMaxRevTreeDepth(Db, 30U);
                    Native.c4db_getMaxRevTreeDepth(Db).Should().Be(30);
                    ReopenDB();
                    Native.c4db_getMaxRevTreeDepth(Db).Should().Be(30, "because the value should be persistent");
                }

                const uint NumRevs = 10000;
                var st             = Stopwatch.StartNew();
                var doc            = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_get(Db, DocID, false, err));
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    for (uint i = 0; i < NumRevs; i++)
                    {
                        var rq          = new C4DocPutRequest();
                        rq.docID        = doc->docID;
                        rq.history      = &doc->revID;
                        rq.historyCount = 1;
                        rq.body         = Body;
                        rq.save         = true;
                        var savedDoc    = (C4Document *)LiteCoreBridge.Check(err =>
                        {
                            var localPut = rq;
                            return(Native.c4doc_put(Db, &localPut, null, err));
                        });
                        Native.c4doc_free(doc);
                        doc = savedDoc;
                    }
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                st.Stop();
                WriteLine($"Created {NumRevs} revisions in {st.ElapsedMilliseconds} ms");

                uint nRevs = 0;
                Native.c4doc_selectCurrentRevision(doc);
                do
                {
                    if (IsRevTrees())
                    {
                        NativeRaw.c4rev_getGeneration(doc->selectedRev.revID).Should()
                        .Be(NumRevs - nRevs, "because the tree should be pruned");
                    }

                    ++nRevs;
                } while (Native.c4doc_selectParentRevision(doc));

                WriteLine($"Document rev tree depth is {nRevs}");
                if (IsRevTrees())
                {
                    nRevs.Should().Be(30, "because the tree should be pruned");
                }

                Native.c4doc_free(doc);
            });
        }
Exemple #18
0
        public void TestPurge()
        {
            RunTestVariants(() => {
                var body2 = C4Slice.Constant("{\"ok\":\"go\"}");
                var body3 = C4Slice.Constant("{\"ubu\":\"roi\"}");
                CreateRev(DocID.CreateString(), RevID, Body);
                CreateRev(DocID.CreateString(), Rev2ID, body2);
                CreateRev(DocID.CreateString(), Rev3ID, body3);

                var history = new[] { C4Slice.Constant("3-ababab"), Rev2ID };
                fixed(C4Slice * history_ = history)
                {
                    var rq = new C4DocPutRequest
                    {
                        existingRevision = true,
                        docID            = DocID,
                        history          = history_,
                        historyCount     = 2,
                        body             = body3,
                        save             = true
                    };

                    C4Error error;
                    C4Document *doc = null;
                    LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                    try {
                        doc = Native.c4doc_put(Db, &rq, null, &error);
                        ((IntPtr)doc).Should().NotBe(IntPtr.Zero);
                        Native.c4doc_free(doc);
                    } finally {
                        LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                    }

                    LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                    try {
                        LiteCoreBridge.Check(err => NativeRaw.c4db_purgeDoc(Db, DocID, err));
                    } finally {
                        LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                    }

                    Native.c4db_getDocumentCount(Db).Should().Be(0UL);

                    CreateRev(DocID.CreateString(), RevID, Body);
                    CreateRev(DocID.CreateString(), Rev2ID, body2);
                    CreateRev(DocID.CreateString(), Rev3ID, body3);

                    LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                    try {
                        doc = Native.c4doc_put(Db, &rq, null, &error);
                        ((IntPtr)doc).Should().NotBe(IntPtr.Zero);
                    } finally {
                        LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                    }

                    LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                    try {
                        LiteCoreBridge.Check(err => Native.c4doc_purgeRevision(doc, null, err));
                        LiteCoreBridge.Check(err => Native.c4doc_save(doc, 0, err));
                    } finally {
                        Native.c4doc_free(doc);
                        LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                    }

                    Native.c4db_getDocumentCount(Db).Should().Be(0UL);
                }
            });
        }
Exemple #19
0
        public void TestCreateVersionedDoc()
        {
            RunTestVariants(() => {
                // Try reading doc with mustExist=true, which should fail:
                C4Error error;
                C4Document *doc = NativeRaw.c4doc_get(Db, DocID, true, &error);
                ((long)doc).Should().Be(0, "because the document does not exist");
                error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain);
                error.code.Should().Be((int)C4ErrorCode.NotFound);
                Native.c4doc_free(doc);

                // Now get the doc with mustExist=false, which returns an empty doc:
                doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_get(Db, DocID, false, err));
                ((int)doc->flags).Should().Be(0, "because the document is empty");
                doc->docID.Equals(DocID).Should().BeTrue("because the doc ID should match what was stored");
                ((long)doc->revID.buf).Should().Be(0, "because the doc has no revision ID yet");
                ((long)doc->selectedRev.revID.buf).Should().Be(0, "because the doc has no revision ID yet");
                Native.c4doc_free(doc);

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    var tmp = RevID;
                    var rq  = new C4DocPutRequest {
                        existingRevision = true,
                        docID            = DocID,
                        history          = &tmp,
                        historyCount     = 1,
                        body             = Body,
                        save             = true
                    };

                    doc = (C4Document *)LiteCoreBridge.Check(err => {
                        var localRq = rq;
                        return(Native.c4doc_put(Db, &localRq, null, err));
                    });
                    doc->revID.Equals(RevID).Should().BeTrue("because the doc should have the stored revID");
                    doc->selectedRev.revID.Equals(RevID).Should().BeTrue("because the doc should have the stored revID");
                    doc->selectedRev.flags.Should().Be(C4RevisionFlags.Leaf, "because this is a leaf revision");
                    doc->selectedRev.body.Equals(Body).Should().BeTrue("because the body should be stored correctly");
                    Native.c4doc_free(doc);
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                // Reload the doc:
                doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_get(Db, DocID, true, err));
                doc->flags.Should().Be(C4DocumentFlags.DocExists, "because this is an existing document");
                doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the stored doc ID");
                doc->revID.Equals(RevID).Should().BeTrue("because the doc should have the stored rev ID");
                doc->selectedRev.revID.Equals(RevID).Should().BeTrue("because the doc should have the stored rev ID");
                doc->selectedRev.sequence.Should().Be(1, "because it is the first stored document");
                doc->selectedRev.body.Equals(Body).Should().BeTrue("because the doc should have the stored body");
                Native.c4doc_free(doc);

                // Get the doc by its sequence
                doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_getBySequence(Db, 1, err));
                doc->flags.Should().Be(C4DocumentFlags.DocExists, "because this is an existing document");
                doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the stored doc ID");
                doc->revID.Equals(RevID).Should().BeTrue("because the doc should have the stored rev ID");
                doc->selectedRev.revID.Equals(RevID).Should().BeTrue("because the doc should have the stored rev ID");
                doc->selectedRev.sequence.Should().Be(1, "because it is the first stored document");
                doc->selectedRev.body.Equals(Body).Should().BeTrue("because the doc should have the stored body");
                Native.c4doc_free(doc);
            });
        }
        public void TestConflict()
        {
            RunTestVariants(() =>
            {
                if (!IsRevTrees())
                {
                    return;
                }

                var body2 = JSON2Fleece("{\"ok\":\"go\"}");
                var body3 = JSON2Fleece("{\"ubu\":\"roi\"}");
                CreateRev(DocID.CreateString(), RevID, FleeceBody);
                CreateRev(DocID.CreateString(), Rev2ID, (FLSlice)body2, C4RevisionFlags.KeepBody);
                CreateRev(DocID.CreateString(), FLSlice.Constant("3-aaaaaa"), (FLSlice)body3);

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    // "Pull" a conflicting revision:
                    var history = new FLSlice[] { FLSlice.Constant("4-dddd"), FLSlice.Constant("3-ababab"), Rev2ID };
                    fixed(FLSlice * history_ = history)
                    {
                        var rq = new C4DocPutRequest
                        {
                            existingRevision = true,
                            docID            = DocID,
                            history          = history_,
                            historyCount     = 3,
                            allowConflict    = true,
                            body             = (FLSlice)body3,
                            save             = true,
                            remoteDBID       = 1
                        };

                        C4Error error;
                        var doc = Native.c4doc_put(Db, &rq, null, &error);
                        ((IntPtr)doc).Should().NotBe(IntPtr.Zero);

                        Native.FLSliceResult_Release(body2);
                        Native.FLSliceResult_Release(body3);

                        Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", "4-dddd").Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());
                        Native.c4doc_selectCommonAncestorRevision(doc, "4-dddd", "3-aaaaaa").Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());

                        Native.c4doc_selectCommonAncestorRevision(doc, "3-ababab", "3-aaaaaa").Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());
                        Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", "3-ababab").Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());

                        Native.c4doc_selectCommonAncestorRevision(doc, Rev2ID.CreateString(), "3-aaaaaa").Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());
                        Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", Rev2ID.CreateString()).Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());

                        NativeRaw.c4doc_selectCommonAncestorRevision(doc, Rev2ID, Rev2ID).Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());
                    }
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                var mergedBody = JSON2Fleece("{\"merged\":true}");
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, DocID.CreateString(), true, err));
                    LiteCoreBridge.Check(err => NativeRaw.c4doc_resolveConflict(doc, FLSlice.Constant("4-dddd"), FLSlice.Constant("3-aaaaaa"), (FLSlice)mergedBody, 0, err));
                    Native.c4doc_selectCurrentRevision(doc);
                    doc->selectedRev.revID.CreateString().Should().Be("5-79b2ecd897d65887a18c46cc39db6f0a3f7b38c4");
                    doc->selectedRev.body.Equals(mergedBody).Should().BeTrue();
                    Native.c4doc_selectParentRevision(doc);
                    doc->selectedRev.revID.CreateString().Should().Be("4-dddd");
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, false, err));
                }

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, DocID.CreateString(), true, err));
                    LiteCoreBridge.Check(err => NativeRaw.c4doc_resolveConflict(doc, FLSlice.Constant("3-aaaaaa"), FLSlice.Constant("4-dddd"), (FLSlice)mergedBody, 0, err));
                    Native.c4doc_selectCurrentRevision(doc);
                    doc->selectedRev.revID.CreateString().Should().Be("4-1fa2dbcb66b5e0456f6d6fc4a90918d42f3dd302");
                    doc->selectedRev.body.Equals(mergedBody).Should().BeTrue();
                    Native.c4doc_selectParentRevision(doc);
                    doc->selectedRev.revID.CreateString().Should().Be("3-aaaaaa");
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, false, err));
                }
            });
        }
Exemple #21
0
        protected uint ImportJSONFile(string path, string idPrefix, TimeSpan timeout, bool verbose)
        {
            WriteLine($"Reading {path} ...");
            var st = Stopwatch.StartNew();

#if WINDOWS_UWP
            var url  = $"ms-appx:///Assets/{path}";
            var file = Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri(url))
                       .AsTask()
                       .ConfigureAwait(false)
                       .GetAwaiter()
                       .GetResult();

            var buffer = Windows.Storage.FileIO.ReadBufferAsync(file).AsTask().ConfigureAwait(false).GetAwaiter()
                         .GetResult();
            var jsonData = System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(buffer);
#elif __ANDROID__
            var    ctx = global::Couchbase.Lite.Tests.Android.MainActivity.ActivityContext;
            byte[] jsonData;
            using (var stream = ctx.Assets.Open(path))
                using (var ms = new MemoryStream()) {
                    stream.CopyTo(ms);
                    jsonData = ms.ToArray();
                }
#elif __IOS__
            var    bundlePath = ios::Foundation.NSBundle.MainBundle.PathForResource(Path.GetFileNameWithoutExtension(path), Path.GetExtension(path));
            byte[] jsonData;
            using (var stream = File.Open(bundlePath, FileMode.Open, FileAccess.Read))
                using (var ms = new MemoryStream()) {
                    stream.CopyTo(ms);
                    jsonData = ms.ToArray();
                }
#else
            var jsonData = File.ReadAllBytes(path);
#endif

            FLError       error;
            FLSliceResult fleeceData;
            fixed(byte *jsonData_ = jsonData)
            {
                fleeceData = NativeRaw.FLData_ConvertJSON(new FLSlice(jsonData_, (ulong)jsonData.Length), &error);
            }

            ((long)fleeceData.buf).Should().NotBe(0, "because otherwise the conversion failed");
            var root = Native.FLValue_AsArray(NativeRaw.FLValue_FromTrustedData((FLSlice)fleeceData));
            ((long)root).Should().NotBe(0, "because otherwise the value is not of the expected type");

            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            try {
                FLArrayIterator iter;
                FLValue *       item;
                uint            numDocs = 0;
                for (Native.FLArrayIterator_Begin(root, &iter);
                     null != (item = Native.FLArrayIterator_GetValue(&iter));
                     Native.FLArrayIterator_Next(&iter))
                {
                    var docID = idPrefix != null ? $"{idPrefix}{numDocs + 1:D7}" : $"doc{numDocs + 1:D7}";
                    var enc   = Native.c4db_getSharedFleeceEncoder(Db);
                    Native.FLEncoder_WriteValue(enc, item);
                    var body = NativeRaw.FLEncoder_Finish(enc, &error);

                    var rq = new C4DocPutRequest {
                        docID = C4Slice.Allocate(docID),
                        body  = (C4Slice)body,
                        save  = true
                    };

                    var doc = (C4Document *)LiteCoreBridge.Check(err =>
                    {
                        var localPut = rq;
                        return(Native.c4doc_put(Db, &localPut, null, err));
                    });

                    Native.c4doc_free(doc);
                    Native.FLSliceResult_Free(body);
                    C4Slice.Free(rq.docID);
                    ++numDocs;
                    if ((numDocs % 1000) == 0 && st.Elapsed > timeout)
                    {
                        WriteLine($"WARNING: Stopping JSON import after {st.Elapsed}");
                        return(numDocs);
                    }
                    if (verbose && (numDocs % 100000) == 0)
                    {
                        WriteLine($"{numDocs}  ");
                    }
                }

                if (verbose)
                {
                    st.PrintReport("Importing", numDocs, "doc", _output);
                }

                return(numDocs);
            }
            finally {
                Native.FLSliceResult_Free(fleeceData);
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }
        }
Exemple #22
0
        // Read a file that contains a JSON document per line. Every line becomes a document.
        protected uint ImportJSONLines(string path, TimeSpan timeout, bool verbose)
        {
            if (verbose)
            {
                WriteLine($"Reading {path}...");
            }

            var  st      = Stopwatch.StartNew();
            uint numDocs = 0;

            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            try {
                ReadFileByLines(path, line => {
                    C4Error error;
                    var body = NativeRaw.c4db_encodeJSON(Db, (C4Slice)line, &error);
                    ((long)body.buf).Should().NotBe(0, "because otherwise the encode failed");

                    var docID = (numDocs + 1).ToString("D7");

                    // Save document:
                    using (var docID_ = new C4String(docID)) {
                        var rq = new C4DocPutRequest {
                            docID = docID_.AsC4Slice(),
                            body  = (C4Slice)body,
                            save  = true
                        };
                        var doc = (C4Document *)LiteCoreBridge.Check(err => {
                            var localRq = rq;
                            return(Native.c4doc_put(Db, &localRq, null, err));
                        });
                        Native.c4doc_free(doc);
                    }

                    Native.c4slice_free(body);
                    ++numDocs;
                    if (numDocs % 1000 == 0 && st.Elapsed >= timeout)
                    {
                        Console.Write($"Stopping JSON import after {st.Elapsed.TotalSeconds:F3} sec ");
                        return(false);
                    }

                    if (verbose && numDocs % 10000 == 0)
                    {
                        Console.Write($"{numDocs} ");
                    }

                    return(true);
                });

                if (verbose)
                {
                    WriteLine("Committing...");
                }
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }

            if (verbose)
            {
                st.PrintReport("Importing", numDocs, "doc", _output);
            }

            return(numDocs);
        }
Exemple #23
0
        private uint InsertDocs(FLArray *docs)
        {
            var typeKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Track Type"), true);
            var idKey      = NativeRaw.FLDictKey_Init(FLSlice.Constant("Persistent ID"), true);
            var nameKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Name"), true);
            var albumKey   = NativeRaw.FLDictKey_Init(FLSlice.Constant("Album"), true);
            var artistKey  = NativeRaw.FLDictKey_Init(FLSlice.Constant("Artist"), true);
            var timeKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Total Time"), true);
            var genreKey   = NativeRaw.FLDictKey_Init(FLSlice.Constant("Genre"), true);
            var yearKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Year"), true);
            var trackNoKey = NativeRaw.FLDictKey_Init(FLSlice.Constant("Track Number"), true);
            var compKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Compilation"), true);

            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            try {
                var             enc = Native.FLEncoder_New();
                FLArrayIterator iter;
                Native.FLArrayIterator_Begin(docs, &iter);
                uint numDocs = 0;
                while (Native.FLArrayIterator_Next(&iter))
                {
                    // Check that track is correct type:
                    var track     = Native.FLValue_AsDict(Native.FLArrayIterator_GetValue(&iter));
                    var trackType = NativeRaw.FLValue_AsString(Native.FLDict_GetWithKey(track, &typeKey));
                    if (!trackType.Equals(FLSlice.Constant("File")) && !trackType.Equals(FLSlice.Constant("Remote")))
                    {
                        continue;
                    }

                    var trackID = NativeRaw.FLValue_AsString(Native.FLDict_GetWithKey(track, &idKey));
                    ((long)trackID.buf).Should().NotBe(0, "because otherwise the data was not read correctly");

                    // Encode doc body:
                    Native.FLEncoder_BeginDict(enc, 0);
                    CopyValue(track, &nameKey, enc).Should().BeTrue("because otherwise the copy failed");
                    CopyValue(track, &albumKey, enc);
                    CopyValue(track, &artistKey, enc);
                    CopyValue(track, &timeKey, enc);
                    CopyValue(track, &genreKey, enc);
                    CopyValue(track, &yearKey, enc);
                    CopyValue(track, &trackNoKey, enc);
                    CopyValue(track, &compKey, enc);
                    Native.FLEncoder_EndDict(enc);
                    FLError err;
                    var     body = NativeRaw.FLEncoder_Finish(enc, &err);
                    body.Should().NotBeNull("because otherwise the encoding process failed");
                    Native.FLEncoder_Reset(enc);

                    // Save Document:
                    var rq = new C4DocPutRequest();
                    rq.docID = (C4Slice)trackID;
                    rq.body  = body;
                    rq.save  = true;
                    var doc = (C4Document *)LiteCoreBridge.Check(c4err => {
                        var localRq = rq;
                        return(Native.c4doc_put(Db, &localRq, null, c4err));
                    });

                    Native.c4doc_free(doc);
                    ++numDocs;
                }

                Native.FLEncoder_Free(enc);
                return(numDocs);
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }
        }
        public void TestPut()
        {
            RunTestVariants(() => {
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    // Creating doc given ID:
                    var rq = new C4DocPutRequest {
                        docID = DocID,
                        body  = FleeceBody,
                        save  = true
                    };

                    var doc = (C4Document *)LiteCoreBridge.Check(err => {
                        var localRq = rq;
                        return(Native.c4doc_put(Db, &localRq, null, err));
                    });

                    doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID");
                    var expectedRevID = IsRevTrees() ? FLSlice.Constant("1-042ca1d3a1d16fd5ab2f87efc7ebbf50b7498032") :
                                        FLSlice.Constant("1@*");
                    doc->revID.Equals(expectedRevID).Should().BeTrue("because the doc should have the correct rev ID");
                    doc->flags.Should().Be(C4DocumentFlags.DocExists, "because the document exists");
                    doc->selectedRev.revID.Equals(expectedRevID).Should().BeTrue("because the selected rev should have the correct rev ID");
                    Native.c4doc_free(doc);

                    // Update doc:
                    var tmp                   = new[] { expectedRevID };
                    var body                  = JSON2Fleece("{\"ok\":\"go\"}");
                    rq.body                   = (FLSlice)body;
                    rq.historyCount           = 1;
                    ulong commonAncestorIndex = 0UL;
                    fixed(FLSlice * history   = tmp)
                    {
                        rq.history = history;
                        doc        = (C4Document *)LiteCoreBridge.Check(err => {
                            var localRq = rq;
                            ulong cai;
                            var retVal          = Native.c4doc_put(Db, &localRq, &cai, err);
                            commonAncestorIndex = cai;
                            return(retVal);
                        });
                    }

                    commonAncestorIndex.Should().Be(0UL, "because there are no common ancestors");
                    var expectedRev2ID = IsRevTrees() ? FLSlice.Constant("2-201796aeeaa6ddbb746d6cab141440f23412ac51") :
                                         FLSlice.Constant("2@*");
                    doc->revID.Equals(expectedRev2ID).Should().BeTrue("because the doc should have the updated rev ID");
                    doc->flags.Should().Be(C4DocumentFlags.DocExists, "because the document exists");
                    doc->selectedRev.revID.Equals(expectedRev2ID).Should().BeTrue("because the selected rev should have the correct rev ID");
                    Native.c4doc_free(doc);

                    // Insert existing rev that conflicts:
                    Native.FLSliceResult_Release(body);
                    body                = JSON2Fleece("{\"from\":\"elsewhere\"}");
                    rq.body             = (FLSlice)body;
                    rq.existingRevision = true;
                    rq.remoteDBID       = 1;
                    var conflictRevID   = IsRevTrees() ? FLSlice.Constant("2-deadbeef") : FLSlice.Constant("1@binky");
                    tmp                     = new[] { conflictRevID, expectedRevID };
                    rq.historyCount         = 2;
                    rq.allowConflict        = true;
                    fixed(FLSlice * history = tmp)
                    {
                        rq.history = history;
                        doc        = (C4Document *)LiteCoreBridge.Check(err => {
                            var localRq = rq;
                            ulong cai;
                            var retVal          = Native.c4doc_put(Db, &localRq, &cai, err);
                            commonAncestorIndex = cai;
                            return(retVal);
                        });
                    }

                    commonAncestorIndex.Should().Be(1UL, "because the common ancestor is at sequence 1");
                    doc->flags.Should().Be(C4DocumentFlags.DocExists | C4DocumentFlags.DocConflicted, "because the document exists");
                    doc->selectedRev.revID.Equals(conflictRevID).Should().BeTrue("because the selected rev should have the correct rev ID");
                    doc->revID.Equals(expectedRev2ID).Should().BeTrue("because the conflicting rev should never be the default");
                    Native.FLSliceResult_Release(body);
                    Native.c4doc_free(doc);
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }
            });
        }
Exemple #25
0
        public void TestPut()
        {
            RunTestVariants(() => {
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    // Creating doc given ID:
                    var rq = new C4DocPutRequest {
                        docID = DocID,
                        body  = Body,
                        save  = true
                    };

                    var doc = (C4Document *)LiteCoreBridge.Check(err => {
                        var localRq = rq;
                        return(Native.c4doc_put(Db, &localRq, null, err));
                    });

                    doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID");
                    var expectedRevID = IsRevTrees() ? C4Slice.Constant("1-c10c25442d9fe14fa3ca0db4322d7f1e43140fab") :
                                        C4Slice.Constant("1@*");
                    doc->revID.Equals(expectedRevID).Should().BeTrue("because the doc should have the correct rev ID");
                    doc->flags.Should().Be(C4DocumentFlags.DocExists, "because the document exists");
                    doc->selectedRev.revID.Equals(expectedRevID).Should().BeTrue("because the selected rev should have the correct rev ID");
                    Native.c4doc_free(doc);

                    // Update doc:
                    var tmp                   = new[] { expectedRevID };
                    rq.body                   = C4Slice.Constant("{\"ok\":\"go\"}");
                    rq.historyCount           = 1;
                    ulong commonAncestorIndex = 0UL;
                    fixed(C4Slice * history   = tmp)
                    {
                        rq.history = history;
                        doc        = (C4Document *)LiteCoreBridge.Check(err => {
                            var localRq = rq;
                            ulong cai;
                            var retVal          = Native.c4doc_put(Db, &localRq, &cai, err);
                            commonAncestorIndex = cai;
                            return(retVal);
                        });
                    }

                    commonAncestorIndex.Should().Be(0UL, "because there are no common ancestors");
                    var expectedRev2ID = IsRevTrees() ? C4Slice.Constant("2-32c711b29ea3297e27f3c28c8b066a68e1bb3f7b") :
                                         C4Slice.Constant("2@*");
                    doc->revID.Equals(expectedRev2ID).Should().BeTrue("because the doc should have the updated rev ID");
                    doc->flags.Should().Be(C4DocumentFlags.DocExists, "because the document exists");
                    doc->selectedRev.revID.Equals(expectedRev2ID).Should().BeTrue("because the selected rev should have the correct rev ID");
                    Native.c4doc_free(doc);

                    // Insert existing rev that conflicts:
                    rq.body             = C4Slice.Constant("{\"from\":\"elsewhere\"}");
                    rq.existingRevision = true;
                    var conflictRevID   = IsRevTrees() ? C4Slice.Constant("2-deadbeef") : C4Slice.Constant("1@binky");
                    tmp                     = new[] { conflictRevID, expectedRevID };
                    rq.historyCount         = 2;
                    fixed(C4Slice * history = tmp)
                    {
                        rq.history = history;
                        doc        = (C4Document *)LiteCoreBridge.Check(err => {
                            var localRq = rq;
                            ulong cai;
                            var retVal          = Native.c4doc_put(Db, &localRq, &cai, err);
                            commonAncestorIndex = cai;
                            return(retVal);
                        });
                    }

                    commonAncestorIndex.Should().Be(1UL, "because the common ancestor is at sequence 1");
                    doc->flags.Should().Be(C4DocumentFlags.DocExists | C4DocumentFlags.DocConflicted, "because the document exists");
                    doc->selectedRev.revID.Equals(conflictRevID).Should().BeTrue("because the selected rev should have the correct rev ID");
                    doc->revID.Equals(expectedRev2ID).Should().BeTrue("because the conflicting rev should never be the default");
                    Native.c4doc_free(doc);
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }
            });
        }
Exemple #26
0
        public void TestConflict()
        {
            RunTestVariants(() =>
            {
                if (!IsRevTrees())
                {
                    return;
                }

                var body2 = C4Slice.Constant("{\"ok\":\"go\"}");
                var body3 = C4Slice.Constant("{\"ubu\":\"roi\"}");
                CreateRev(DocID.CreateString(), RevID, Body);
                CreateRev(DocID.CreateString(), Rev2ID, body2, C4RevisionFlags.KeepBody);
                CreateRev(DocID.CreateString(), C4Slice.Constant("3-aaaaaa"), body3);

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    // "Pull" a conflicting revision:
                    var history = new C4Slice[] { C4Slice.Constant("4-dddd"), C4Slice.Constant("3-ababab"), Rev2ID };
                    fixed(C4Slice * history_ = history)
                    {
                        var rq = new C4DocPutRequest
                        {
                            existingRevision = true,
                            docID            = DocID,
                            history          = history_,
                            historyCount     = 3,
                            body             = body3,
                            save             = true
                        };

                        C4Error error;
                        var doc = Native.c4doc_put(Db, &rq, null, &error);
                        ((IntPtr)doc).Should().NotBe(IntPtr.Zero);

                        Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", "4-dddd").Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());
                        Native.c4doc_selectCommonAncestorRevision(doc, "4-dddd", "3-aaaaaa").Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());

                        Native.c4doc_selectCommonAncestorRevision(doc, "3-ababab", "3-aaaaaa").Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());
                        Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", "3-ababab").Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());

                        Native.c4doc_selectCommonAncestorRevision(doc, Rev2ID.CreateString(), "3-aaaaaa").Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());
                        Native.c4doc_selectCommonAncestorRevision(doc, "3-aaaaaa", Rev2ID.CreateString()).Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());

                        NativeRaw.c4doc_selectCommonAncestorRevision(doc, Rev2ID, Rev2ID).Should().BeTrue();
                        doc->selectedRev.revID.CreateString().Should().Be(Rev2ID.CreateString());
                    }
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, DocID.CreateString(), true, err));
                    LiteCoreBridge.Check(err => Native.c4doc_resolveConflict(doc, "4-dddd", "3-aaaaaa", Encoding.UTF8.GetBytes("{\"merged\":true}"), 0, err));
                    Native.c4doc_selectCurrentRevision(doc);
                    doc->selectedRev.revID.CreateString().Should().Be("5-940fe7e020dbf8db0f82a5d764870c4b6c88ae99");
                    doc->selectedRev.body.CreateString().Should().Be("{\"merged\":true}");
                    Native.c4doc_selectParentRevision(doc);
                    doc->selectedRev.revID.CreateString().Should().Be("4-dddd");
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, false, err));
                }

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, DocID.CreateString(), true, err));
                    LiteCoreBridge.Check(err => Native.c4doc_resolveConflict(doc, "3-aaaaaa", "4-dddd", Encoding.UTF8.GetBytes("{\"merged\":true}"), 0, err));
                    Native.c4doc_selectCurrentRevision(doc);
                    doc->selectedRev.revID.CreateString().Should().Be("4-333ee0677b5f1e1e5064b050d417a31d2455dc30");
                    doc->selectedRev.body.CreateString().Should().Be("{\"merged\":true}");
                    Native.c4doc_selectParentRevision(doc);
                    doc->selectedRev.revID.CreateString().Should().Be("3-aaaaaa");
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, false, err));
                }
            });
        }
Exemple #27
0
        private C4Document *ForceInsert(C4Database *db, string docID, string[] history, string body, C4RevisionFlags flags, C4Error *error = null)
        {
            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(db, err));
            var c4History = new C4String[history.Length];
            var success   = false;

            try {
                var i            = 0;
                var sliceHistory = new C4Slice[history.Length];
                foreach (var entry in history)
                {
                    var c4Str = new C4String(entry);
                    c4History[i]      = c4Str;
                    sliceHistory[i++] = c4Str.AsC4Slice();
                }

                using (var docID_ = new C4String(docID))
                    using (var body_ = new C4String(body))
                    {
                        fixed(C4Slice *sliceHistory_ = sliceHistory)
                        {
                            var rq = new C4DocPutRequest
                            {
                                docID            = docID_.AsC4Slice(),
                                existingRevision = true,
                                allowConflict    = true,
                                history          = sliceHistory_,
                                historyCount     = (ulong)history.Length,
                                body             = body_.AsC4Slice(),
                                revFlags         = flags,
                                save             = true
                            };

                            C4Document *doc;

                            if (error != null)
                            {
                                var local = rq;
                                doc = Native.c4doc_put(db, &local, null, error);
                            }
                            else
                            {
                                doc = (C4Document *)LiteCoreBridge.Check(err =>
                                {
                                    var local = rq;
                                    return(Native.c4doc_put(db, &local, null, err));
                                });
                            }

                            success = true;
                            return(doc);
                        }
                    }
            } finally {
                foreach (var entry in c4History)
                {
                    entry.Dispose();
                }

                LiteCoreBridge.Check(err => Native.c4db_endTransaction(db, success, err));
            }
        }