예제 #1
0
        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));
                }
            });
        }
예제 #2
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));
            }
        }
예제 #3
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      = (FLSlice *)&doc->revID;
                        rq.historyCount = 1;
                        rq.body         = FleeceBody;
                        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);
            });
        }
예제 #4
0
        public void TestUpdate()
        {
            RunTestVariants(() =>
            {
                WriteLine("Begin test");
                C4Document *doc = null;
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin create");
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_create(Db, DocID, FleeceBody, 0, err));
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                WriteLine("After save");
                var expectedRevID = IsRevTrees()
                    ? FLSlice.Constant("1-042ca1d3a1d16fd5ab2f87efc7ebbf50b7498032")
                    : FLSlice.Constant("1@*");

                doc->revID.Equals(expectedRevID).Should().BeTrue();
                doc->flags.Should().Be(C4DocumentFlags.DocExists, "because the document was saved");
                doc->selectedRev.revID.Equals(expectedRevID).Should().BeTrue();
                doc->docID.Equals(DocID).Should().BeTrue("because that is the document ID that it was saved with");

                // Read the doc into another C4Document
                var doc2 = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_get(Db, DocID, false, err));
                doc->revID.Equals(expectedRevID).Should()
                .BeTrue("because the other reference should have the same rev ID");

                for (int i = 2; i <= 5; i++)
                {
                    LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                    try
                    {
                        WriteLine($"Begin save #{i}");
                        var body       = JSON2Fleece("{\"ok\":\"go\"}");
                        var oldRevID   = doc->revID;
                        var updatedDoc =
                            (C4Document *)LiteCoreBridge.Check(
                                err => NativeRaw.c4doc_update(doc, (FLSlice)body, 0, err));
                        doc->selectedRev.revID.Equals(oldRevID).Should().BeTrue();
                        doc->revID.Equals(oldRevID).Should().BeTrue();
                        Native.c4doc_free(doc);
                        doc = updatedDoc;
                        Native.FLSliceResult_Release(body);
                    }
                    finally
                    {
                        LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                    }
                }

                WriteLine("After multiple updates");
                var expectedRev2ID = IsRevTrees()
                    ? FLSlice.Constant("5-a452899fa8e69b06d936a5034018f6fff0a8f906")
                    : FLSlice.Constant("5@*");
                doc->revID.Equals(expectedRev2ID).Should().BeTrue();
                doc->selectedRev.revID.Equals(expectedRev2ID).Should().BeTrue();

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin conflicting save");
                    C4Error error;
                    var body = JSON2Fleece("{\"ok\":\"no way\"}");
                    ((long)NativeRaw.c4doc_update(doc2, (FLSlice)body, 0, &error)).Should().Be(0, "because this is a conflict");
                    error.code.Should().Be((int)C4ErrorCode.Conflict);
                    error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain);
                    Native.FLSliceResult_Release(body);
                }
                finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin conflicting create");
                    C4Error error;
                    var body = JSON2Fleece("{\"ok\":\"no way\"}");
                    ((long)NativeRaw.c4doc_create(Db, DocID, (FLSlice)body, 0, &error)).Should().Be(0, "because this is a conflict");
                    error.code.Should().Be((int)C4ErrorCode.Conflict);
                    error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain);
                    Native.FLSliceResult_Release(body);
                }
                finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                Native.c4doc_free(doc);
                Native.c4doc_free(doc2);
            });
        }
예제 #5
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             = FleeceBody,
                        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(FleeceBody).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(FleeceBody).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(FleeceBody).Should().BeTrue("because the doc should have the stored body");
                Native.c4doc_free(doc);
            });
        }
예제 #6
0
        public void TestPurge()
        {
            RunTestVariants(() => {
                var body2 = JSON2Fleece("{\"ok\":\"go\"}");
                var body3 = JSON2Fleece("{\"ubu\":\"roi\"}");
                CreateRev(DocID.CreateString(), RevID, FleeceBody);
                CreateRev(DocID.CreateString(), Rev2ID, (FLSlice)body2);
                CreateRev(DocID.CreateString(), Rev3ID, (FLSlice)body3);

                var history = new[] { FLSlice.Constant("3-ababab"), Rev2ID };
                fixed(FLSlice * history_ = history)
                {
                    var rq = new C4DocPutRequest
                    {
                        existingRevision = true,
                        docID            = DocID,
                        history          = history_,
                        historyCount     = 2,
                        allowConflict    = true,
                        body             = (FLSlice)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, FleeceBody);
                    CreateRev(DocID.CreateString(), Rev2ID, (FLSlice)body2);
                    CreateRev(DocID.CreateString(), Rev3ID, (FLSlice)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);
                    Native.FLSliceResult_Release(body2);
                    Native.FLSliceResult_Release(body3);
                }
            });
        }
예제 #7
0
        public void TestUpdate()
        {
            RunTestVariants(() =>
            {
                WriteLine("Begin test");
                C4Document *doc = null;
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin create");
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_create(Db, DocID, FleeceBody, 0, err));
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                WriteLine("After save");
                var expectedRevID = IsRevTrees()
                    ? FLSlice.Constant("1-d41ffed6be0529153fd3d27b3218d9052e1c2b40")
                    : FLSlice.Constant("1@*");

                doc->revID.Equals(expectedRevID).Should().BeTrue();
                doc->flags.Should().Be(C4DocumentFlags.DocExists, "because the document was saved");
                doc->selectedRev.revID.Equals(expectedRevID).Should().BeTrue();
                doc->docID.Equals(DocID).Should().BeTrue("because that is the document ID that it was saved with");

                // Read the doc into another C4Document
                var doc2 = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_get(Db, DocID, false, err));
                doc->revID.Equals(expectedRevID).Should()
                .BeTrue("because the other reference should have the same rev ID");

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin 2nd save");
                    var body       = JSON2Fleece("{\"ok\":\"go\"}");
                    var updatedDoc =
                        (C4Document *)LiteCoreBridge.Check(
                            err => NativeRaw.c4doc_update(doc, (FLSlice)body, 0, err));
                    doc->selectedRev.revID.Equals(expectedRevID).Should().BeTrue();
                    doc->revID.Equals(expectedRevID).Should().BeTrue();
                    Native.c4doc_free(doc);
                    doc = updatedDoc;
                    Native.FLSliceResult_Release(body);
                }
                finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                WriteLine("After 2nd save");
                var expectedRev2ID = IsRevTrees()
                    ? FLSlice.Constant("2-e388dff9126ba5a0d93c7af05bc72f3cdf450598")
                    : FLSlice.Constant("2@*");
                doc->revID.Equals(expectedRev2ID).Should().BeTrue();
                doc->selectedRev.revID.Equals(expectedRev2ID).Should().BeTrue();

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin conflicting save");
                    C4Error error;
                    var body = JSON2Fleece("{\"ok\":\"no way\"}");
                    ((long)NativeRaw.c4doc_update(doc2, (FLSlice)body, 0, &error)).Should().Be(0, "because this is a conflict");
                    error.code.Should().Be((int)C4ErrorCode.Conflict);
                    error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain);
                    Native.FLSliceResult_Release(body);
                }
                finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin conflicting create");
                    C4Error error;
                    var body = JSON2Fleece("{\"ok\":\"no way\"}");
                    ((long)NativeRaw.c4doc_create(Db, DocID, (FLSlice)body, 0, &error)).Should().Be(0, "because this is a conflict");
                    error.code.Should().Be((int)C4ErrorCode.Conflict);
                    error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain);
                    Native.FLSliceResult_Release(body);
                }
                finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                Native.c4doc_free(doc);
                Native.c4doc_free(doc2);
            });
        }
예제 #8
0
        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));
                }

                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));
                }
            });
        }
예제 #9
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, (FLSlice)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_.AsFLSlice(),
                            body  = (FLSlice)body,
                            save  = true
                        };
                        var doc = (C4Document *)LiteCoreBridge.Check(err => {
                            var localRq = rq;
                            return(Native.c4doc_put(Db, &localRq, null, err));
                        });
                        Native.c4doc_release(doc);
                    }

                    Native.FLSliceResult_Release(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);
        }
예제 #10
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 = 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_FromData((FLSlice)fleeceData, FLTrust.Trusted));
            ((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 = FLSlice.Allocate(docID),
                        body  = (FLSlice)body,
                        save  = true
                    };

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

                    Native.c4doc_release(doc);
                    Native.FLSliceResult_Release(body);
                    FLSlice.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_Release(fleeceData);
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }
        }
        public void TestUpdate()
        {
            RunTestVariants(() =>
            {
                WriteLine("Begin test");
                C4Document *doc = null;
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin create");
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_create(Db, DocID, Body, 0, err));
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                WriteLine("After save");
                var expectedRevID = IsRevTrees()
                    ? C4Slice.Constant("1-c10c25442d9fe14fa3ca0db4322d7f1e43140fab")
                    : C4Slice.Constant("1@*");

                doc->revID.Equals(expectedRevID).Should().BeTrue();
                doc->flags.Should().Be(C4DocumentFlags.DocExists, "because the document was saved");
                doc->selectedRev.revID.Equals(expectedRevID).Should().BeTrue();
                doc->docID.Equals(DocID).Should().BeTrue("because that is the document ID that it was saved with");

                // Read the doc into another C4Document
                var doc2 = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_get(Db, DocID, false, err));
                doc->revID.Equals(expectedRevID).Should()
                .BeTrue("because the other reference should have the same rev ID");

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin 2nd save");
                    var updatedDoc =
                        (C4Document *)LiteCoreBridge.Check(
                            err => Native.c4doc_update(doc, Encoding.UTF8.GetBytes("{\"ok\":\"go\"}"), 0, err));
                    doc->selectedRev.revID.Equals(expectedRevID).Should().BeTrue();
                    doc->revID.Equals(expectedRevID).Should().BeTrue();
                    Native.c4doc_free(doc);
                    doc = updatedDoc;
                }
                finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                WriteLine("After 2nd save");
                var expectedRev2ID = IsRevTrees()
                    ? C4Slice.Constant("2-32c711b29ea3297e27f3c28c8b066a68e1bb3f7b")
                    : C4Slice.Constant("2@*");
                doc->revID.Equals(expectedRev2ID).Should().BeTrue();
                doc->selectedRev.revID.Equals(expectedRev2ID).Should().BeTrue();

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin conflicting save");
                    C4Error error;
                    ((long)Native.c4doc_update(doc2, Encoding.UTF8.GetBytes("{\"ok\":\"no way\"}"), 0, &error)).Should().Be(0, "because this is a conflict");
                    error.code.Should().Be((int)C4ErrorCode.Conflict);
                    error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain);
                }
                finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin conflicting create");
                    C4Error error;
                    ((long)NativeRaw.c4doc_create(Db, DocID, C4Slice.Constant("{\"ok\":\"no way\"}"), 0, &error)).Should().Be(0, "because this is a conflict");
                    error.code.Should().Be((int)C4ErrorCode.Conflict);
                    error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain);
                }
                finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

                Native.c4doc_free(doc);
                Native.c4doc_free(doc2);
            });
        }
예제 #12
0
 public FLValue *GetDictValue(FLDict *dict, FLSlice key)
 {
     return(NativeRaw.FLDict_GetSharedKey(dict, key, SharedKeys));
 }
        public void TestGetForPut()
        {
            RunTestVariants(() => {
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    // Creating doc given ID:
                    var doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_getForPut(Db,
                                                                                                  DocID, C4Slice.Null, false, false, err));
                    doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID");
                    doc->revID.Equals(C4Slice.Null).Should().BeTrue("because a rev ID has not been assigned yet");
                    ((int)doc->flags).Should().Be(0, "because the document has no flags yet");
                    doc->selectedRev.revID.Equals(C4Slice.Null).Should().BeTrue("because no rev ID has been assigned yet");
                    Native.c4doc_free(doc);

                    // Creating doc, no ID:
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_getForPut(Db,
                                                                                              C4Slice.Null, C4Slice.Null, false, false, err));
                    doc->docID.size.Should().BeGreaterOrEqualTo(20, "because the document should be assigned a random ID");
                    doc->revID.Equals(C4Slice.Null).Should().BeTrue("because the doc doesn't have a rev ID yet");
                    ((int)doc->flags).Should().Be(0, "because the document has no flags yet");
                    doc->selectedRev.revID.Equals(C4Slice.Null).Should().BeTrue("because no rev ID has been assigned yet");
                    Native.c4doc_free(doc);

                    // Delete with no revID given
                    C4Error error;
                    doc = NativeRaw.c4doc_getForPut(Db, C4Slice.Null, C4Slice.Null, true, false, &error);
                    ((long)doc).Should().Be(0, "because the document does not exist");
                    error.Code.Should().Be((int)LiteCoreError.NotFound, "because the correct error should be returned");

                    // Adding new rev of nonexistent doc:
                    doc = NativeRaw.c4doc_getForPut(Db, DocID, RevID, false, false, &error);
                    ((long)doc).Should().Be(0, "because the document does not exist");
                    error.Code.Should().Be((int)LiteCoreError.NotFound, "because the correct error should be returned");

                    // Adding new rev of existing doc:
                    CreateRev(DocID.CreateString(), RevID, Body);
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_getForPut(Db, DocID, RevID, false,
                                                                                              false, err));
                    doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID");
                    doc->revID.Equals(RevID).Should().BeTrue("because the doc should have the correct rev ID");
                    doc->flags.Should().Be(C4DocumentFlags.Exists, "because the document has no flags yet");
                    doc->selectedRev.revID.Equals(RevID).Should().BeTrue("because the selected rev should have the correct rev ID");
                    Native.c4doc_free(doc);

                    // Adding new rev, with nonexistent parent
                    doc = NativeRaw.c4doc_getForPut(Db, DocID, Rev2ID, false, false, &error);
                    ((long)doc).Should().Be(0, "because the document does not exist");
                    error.Code.Should().Be((int)LiteCoreError.Conflict, "because the correct error should be returned");

                    // Conflict -- try & fail to update non-current rev:
                    var body2 = C4Slice.Constant("{\"ok\":\"go\"}");
                    CreateRev(DocID.CreateString(), Rev2ID, body2);
                    doc = NativeRaw.c4doc_getForPut(Db, DocID, RevID, false, false, &error);
                    ((long)doc).Should().Be(0, "because the document does not exist");
                    error.Code.Should().Be((int)LiteCoreError.Conflict, "because the correct error should be returned");

                    if (Versioning == C4DocumentVersioning.RevisionTrees)
                    {
                        // Conflict -- force an update of non-current rev:
                        doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_getForPut(Db, DocID,
                                                                                                  RevID, false, true, err));
                        doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID");
                        doc->selectedRev.revID.Equals(RevID).Should().BeTrue("because the doc should have the correct rev ID");
                        Native.c4doc_free(doc);
                    }

                    // Deleting the doc:
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_getForPut(Db, DocID,
                                                                                              Rev2ID, true, false, err));
                    doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID");
                    doc->selectedRev.revID.Equals(Rev2ID).Should().BeTrue("because the doc should have the correct rev ID");
                    Native.c4doc_free(doc);

                    // Actually tdelete it:
                    CreateRev(DocID.CreateString(), Rev3ID, C4Slice.Null);

                    // Re-creating the doc (no revID given):
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_getForPut(Db, DocID,
                                                                                              C4Slice.Null, false, false, err));
                    doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID");
                    doc->selectedRev.revID.Equals(Rev3ID).Should().BeTrue("because the doc should have the correct rev ID");
                    doc->flags.Should().Be(C4DocumentFlags.Exists | C4DocumentFlags.Deleted, "because the document was deleted");
                    doc->selectedRev.revID.Equals(Rev3ID).Should().BeTrue("because the doc should have the correct rev ID");
                    Native.c4doc_free(doc);
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }
            });
        }
예제 #14
0
        private void UpdateIndex(C4Database *updateDB, C4View *view)
        {
            var oldLastSeqIndexed = Native.c4view_getLastSequenceIndexed(view);
            var lastSeq           = oldLastSeqIndexed;
            var ind = (C4Indexer *)LiteCoreBridge.Check(err => Native.c4indexer_begin(updateDB,
                                                                                      new[] { view }, err));
            var e = (C4DocEnumerator *)LiteCoreBridge.Check(err => Native.c4indexer_enumerateDocuments(ind, err));

            if (e == null)
            {
                LiteCoreBridge.Check(err => Native.c4indexer_end(ind, true, err));
                return;
            }

            if (Log)
            {
                Console.Write("<< ");
            }

            C4Document *doc;
            C4Error     error;

            while (null != (doc = Native.c4enum_nextDocument(e, &error)))
            {
                // Index 'doc':
                if (Log)
                {
                    Console.Write($"(#{doc->sequence}) ");
                }

                if (lastSeq > 0)
                {
                    doc->sequence.Should().Be(lastSeq + 1, "because the sequences should be ordered");
                }

                lastSeq = doc->sequence;
                var keys   = new C4Key *[1];
                var values = new C4Slice[1];
                keys[0] = Native.c4key_new();
                NativeRaw.c4key_addString(keys[0], doc->docID);
                values[0] = C4Slice.Constant("1234");
                LiteCoreBridge.Check(err => Native.c4indexer_emit(ind, doc, 0, keys, values, err));
                Native.c4key_free(keys[0]);
                Native.c4doc_free(doc);
            }

            error.Code.Should().Be(0, "because otherwise an error occurred somewhere");
            Native.c4enum_free(e);
            if (Log)
            {
                Console.Write($">>indexed_to:{lastSeq} ");
            }

            LiteCoreBridge.Check(err => Native.c4indexer_end(ind, true, err));


            var newLastSeqIndexed = Native.c4view_getLastSequenceIndexed(view);

            if (newLastSeqIndexed != lastSeq)
            {
                if (Log)
                {
                    Console.Write($"BUT view.lastSequenceIndexed={newLastSeqIndexed}! (Started at {oldLastSeqIndexed})");
                }
            }

            newLastSeqIndexed.Should().Be(lastSeq, "because the last sequence in the loop should be current");
            Native.c4view_getLastSequenceChangedAt(view).Should().Be(lastSeq);
        }
예제 #15
0
        private C4Error SetupC4Replicator()
        {
            Config.Database.CheckOpenLocked();
            C4Error err = new C4Error();

            if (_repl != null)
            {
                Native.c4repl_setOptions(_repl, ((FLSlice)Config.Options.FLEncode()).ToArrayFast());
                return(err);
            }

            _desc = ToString(); // Cache this; it may be called a lot when logging

            // Target:
            var      addr      = new C4Address();
            Database otherDB   = null;
            var      remoteUrl = Config.RemoteUrl;
            string   dbNameStr = remoteUrl?.Segments?.Last().TrimEnd('/');

            using (var dbNameStr_ = new C4String(dbNameStr))
                using (var remoteUrlStr_ = new C4String(remoteUrl?.AbsoluteUri)) {
                    FLSlice   dn = dbNameStr_.AsFLSlice();
                    C4Address localAddr;
                    var       addrFromUrl = NativeRaw.c4address_fromURL(remoteUrlStr_.AsFLSlice(), &localAddr, &dn);
                    addr = localAddr;

                    if (addrFromUrl)
                    {
                        //get cookies from url and add to replicator options
                        var cookiestring = Config.Database.GetCookies(remoteUrl);
                        if (!String.IsNullOrEmpty(cookiestring))
                        {
                            var split = cookiestring.Split(';') ?? Enumerable.Empty <string>();
                            foreach (var entry in split)
                            {
                                var pieces = entry?.Split('=');
                                if (pieces?.Length != 2)
                                {
                                    WriteLog.To.Sync.W(Tag, "Garbage cookie value, ignoring");
                                    continue;
                                }

                                Config.Options.Cookies.Add(new Cookie(pieces[0]?.Trim(), pieces[1]?.Trim()));
                            }
                        }
                    }
                    else
                    {
                        Config.OtherDB?.CheckOpenLocked();
                        otherDB = Config.OtherDB;
                    }

                    var options = Config.Options;

                    Config.Authenticator?.Authenticate(options);

                    options.Build();
                    var push       = Config.ReplicatorType.HasFlag(ReplicatorType.Push);
                    var pull       = Config.ReplicatorType.HasFlag(ReplicatorType.Pull);
                    var continuous = Config.Continuous;

                    var socketFactory = Config.SocketFactory;
                    socketFactory.context = GCHandle.ToIntPtr(GCHandle.Alloc(this)).ToPointer();
                    _nativeParams         = new ReplicatorParameters(options)
                    {
                        Push            = Mkmode(push, continuous),
                        Pull            = Mkmode(pull, continuous),
                        Context         = this,
                        OnDocumentEnded = OnDocEnded,
                        OnStatusChanged = StatusChangedCallback,
                        SocketFactory   = &socketFactory
                    };

                    // Clear the reset flag, it is a one-time thing
                    options.Reset = false;

                    if (Config.PushFilter != null)
                    {
                        _nativeParams.PushFilter = PushFilterCallback;
                    }
                    if (Config.PullFilter != null)
                    {
                        _nativeParams.PullFilter = PullValidateCallback;
                    }

                    DispatchQueue.DispatchSync(() =>
                    {
                        C4Error localErr = new C4Error();
                    #if COUCHBASE_ENTERPRISE
                        if (otherDB != null)
                        {
                            _repl = Native.c4repl_newLocal(Config.Database.c4db, otherDB.c4db, _nativeParams.C4Params,
                                                           &localErr);
                        }
                        else
                    #endif
                        _repl = Native.c4repl_new(Config.Database.c4db, addr, dbNameStr, _nativeParams.C4Params, &localErr);

                        if (_documentEndedUpdate.Counter > 0)
                        {
                            SetProgressLevel(C4ReplicatorProgressLevel.ReplProgressPerDocument);
                        }

                        err = localErr;
                    });
                }

            return(err);
        }