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)); } }); }
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 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); }); }
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); }); }
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); }); }
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); } }); }
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); }); }
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)); } }); }
// 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); }
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); }); }
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)); } }); }
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); }
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); }