private C4Document *ForceInsert(C4Database *db, string docID, string[] history, string body, C4RevisionFlags flags, C4Error *error = null) { LiteCoreBridge.Check(err => Native.c4db_beginTransaction(db, err)); var c4History = new C4String[history.Length]; var success = false; try { var i = 0; var sliceHistory = new FLSlice[history.Length]; foreach (var entry in history) { var c4Str = new C4String(entry); c4History[i] = c4Str; sliceHistory[i++] = c4Str.AsFLSlice(); } using (var docID_ = new C4String(docID)) using (var body_ = new C4String(body)) { fixed(FLSlice *sliceHistory_ = sliceHistory) { var rq = new C4DocPutRequest { docID = docID_.AsFLSlice(), existingRevision = true, allowConflict = true, history = sliceHistory_, historyCount = (ulong)history.Length, body = body_.AsFLSlice(), revFlags = flags, remoteDBID = _remoteDocID, save = true }; C4Document *doc; if (error != null) { var local = rq; doc = Native.c4doc_put(db, &local, null, error); } else { doc = (C4Document *)LiteCoreBridge.Check(err => { var local = rq; return(Native.c4doc_put(db, &local, null, err)); }); } success = true; return(doc); } } } finally { foreach (var entry in c4History) { entry.Dispose(); } LiteCoreBridge.Check(err => Native.c4db_endTransaction(db, success, 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, 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_release(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_release(doc); Native.c4doc_release(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)); } 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)); } }); }
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_release(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_release(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 TestPut() { RunTestVariants(() => { LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { // Creating doc given ID: var rq = new C4DocPutRequest { docID = DocID, body = FleeceBody, save = true }; var doc = (C4Document *)LiteCoreBridge.Check(err => { var localRq = rq; return(Native.c4doc_put(Db, &localRq, null, err)); }); doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the correct doc ID"); var expectedRevID = IsRevTrees() ? FLSlice.Constant("1-042ca1d3a1d16fd5ab2f87efc7ebbf50b7498032") : FLSlice.Constant("1@*"); doc->revID.Equals(expectedRevID).Should().BeTrue("because the doc should have the correct rev ID"); doc->flags.Should().Be(C4DocumentFlags.DocExists, "because the document exists"); doc->selectedRev.revID.Equals(expectedRevID).Should().BeTrue("because the selected rev should have the correct rev ID"); Native.c4doc_release(doc); // Update doc: var tmp = new[] { expectedRevID }; var body = JSON2Fleece("{\"ok\":\"go\"}"); rq.body = (FLSlice)body; rq.historyCount = 1; ulong commonAncestorIndex = 0UL; fixed(FLSlice * history = tmp) { rq.history = history; doc = (C4Document *)LiteCoreBridge.Check(err => { var localRq = rq; ulong cai; var retVal = Native.c4doc_put(Db, &localRq, &cai, err); commonAncestorIndex = cai; return(retVal); }); } commonAncestorIndex.Should().Be(0UL, "because there are no common ancestors"); var expectedRev2ID = IsRevTrees() ? FLSlice.Constant("2-201796aeeaa6ddbb746d6cab141440f23412ac51") : FLSlice.Constant("2@*"); doc->revID.Equals(expectedRev2ID).Should().BeTrue("because the doc should have the updated rev ID"); doc->flags.Should().Be(C4DocumentFlags.DocExists, "because the document exists"); doc->selectedRev.revID.Equals(expectedRev2ID).Should().BeTrue("because the selected rev should have the correct rev ID"); Native.c4doc_release(doc); // Insert existing rev that conflicts: Native.FLSliceResult_Release(body); body = JSON2Fleece("{\"from\":\"elsewhere\"}"); rq.body = (FLSlice)body; rq.existingRevision = true; rq.remoteDBID = 1; var conflictRevID = IsRevTrees() ? FLSlice.Constant("2-deadbeef") : FLSlice.Constant("1@binky"); tmp = new[] { conflictRevID, expectedRevID }; rq.historyCount = 2; rq.allowConflict = true; fixed(FLSlice * history = tmp) { rq.history = history; doc = (C4Document *)LiteCoreBridge.Check(err => { var localRq = rq; ulong cai; var retVal = Native.c4doc_put(Db, &localRq, &cai, err); commonAncestorIndex = cai; return(retVal); }); } commonAncestorIndex.Should().Be(1UL, "because the common ancestor is at sequence 1"); doc->flags.Should().Be(C4DocumentFlags.DocExists | C4DocumentFlags.DocConflicted, "because the document exists"); doc->selectedRev.revID.Equals(conflictRevID).Should().BeTrue("because the selected rev should have the correct rev ID"); doc->revID.Equals(expectedRev2ID).Should().BeTrue("because the conflicting rev should never be the default"); Native.FLSliceResult_Release(body); Native.c4doc_release(doc); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } }); }
private void Log(C4LogLevel level, FLSlice s) { WriteLine($"[{level}] {s.CreateString()}"); }
protected uint ImportJSONFile(string path, string idPrefix, TimeSpan timeout, bool verbose) { WriteLine($"Reading {path} ..."); var st = Stopwatch.StartNew(); #if WINDOWS_UWP var url = $"ms-appx:///Assets/{path}"; var file = Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri(url)) .AsTask() .ConfigureAwait(false) .GetAwaiter() .GetResult(); var buffer = Windows.Storage.FileIO.ReadBufferAsync(file).AsTask().ConfigureAwait(false).GetAwaiter() .GetResult(); var jsonData = System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(buffer); #elif __ANDROID__ var ctx = global::Couchbase.Lite.Tests.Android.MainActivity.ActivityContext; byte[] jsonData; using (var stream = ctx.Assets.Open(path)) using (var ms = new MemoryStream()) { stream.CopyTo(ms); jsonData = ms.ToArray(); } #elif __IOS__ var bundlePath = ios::Foundation.NSBundle.MainBundle.PathForResource(Path.GetFileNameWithoutExtension(path), Path.GetExtension(path)); byte[] jsonData; using (var stream = File.Open(bundlePath, FileMode.Open, FileAccess.Read)) using (var ms = new MemoryStream()) { stream.CopyTo(ms); jsonData = ms.ToArray(); } #else var jsonData = File.ReadAllBytes(path); #endif FLError error; FLSliceResult fleeceData; fixed(byte *jsonData_ = jsonData) { fleeceData = NativeRaw.FLData_ConvertJSON(new FLSlice(jsonData_, (ulong)jsonData.Length), &error); } ((long)fleeceData.buf).Should().NotBe(0, "because otherwise the conversion failed"); var root = Native.FLValue_AsArray(NativeRaw.FLValue_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_free(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 TestCreateRawDoc() { RunTestVariants(() => { var key = FLSlice.Constant("key"); var meta = FLSlice.Constant("meta"); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); LiteCoreBridge.Check(err => NativeRaw.c4raw_put(Db, FLSlice.Constant("test"), key, meta, FleeceBody, err)); LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); var doc = (C4RawDocument *)LiteCoreBridge.Check(err => NativeRaw.c4raw_get(Db, FLSlice.Constant("test"), key, err)); doc->key.Equals(key).Should().BeTrue("because the key should not change"); doc->meta.Equals(meta).Should().BeTrue("because the meta should not change"); doc->body.Equals(FleeceBody).Should().BeTrue("because the body should not change"); Native.c4raw_free(doc); // Nonexistent: C4Error error; ((long)Native.c4raw_get(Db, "test", "bogus", &error)).Should().Be(0, "because the document does not exist"); error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain, "because that is the correct domain"); error.code.Should().Be((int)C4ErrorCode.NotFound, "because that is the correct error code"); }); }
internal void CreateRev(string docID, FLSlice revID, FLSlice body, C4RevisionFlags flags = (C4RevisionFlags)0) { CreateRev(Db, docID, revID, body, flags); }
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 { docID = (C4Slice)trackID, body = (C4Slice)body, 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)); } }
private static void DocObserverCallback(C4DocumentObserver *obs, FLSlice docId, ulong sequence, void *context) { var obj = GCHandle.FromIntPtr((IntPtr)context).Target as ObserverTest; obj.DocObserverCalled(obs, docId.CreateString(), sequence); }
private C4Document *PutDoc(string docID, string revID, FLSlice body, C4RevisionFlags flags = 0) { return(PutDoc(Db, docID, revID, body, flags)); }
private void ForceInsert(string docID, string[] history, FLSlice body, C4RevisionFlags flags = 0) { var doc = ForceInsert(Db, docID, history, body, flags); Native.c4doc_free(doc); }
private void PutDocMustFail(string docID, string revID, FLSlice body, C4RevisionFlags flags, C4Error expected) { PutDocMustFail(Db, docID, revID, body, flags, expected); }
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); }
private static bool PushFilterCallback(FLSlice collectionName, FLSlice docID, FLSlice revID, C4RevisionFlags revisionFlags, FLDict *dict, void *context) { var replicator = GCHandle.FromIntPtr((IntPtr)context).Target as Replicator; if (replicator == null) { WriteLog.To.Database.E(Tag, "Push filter context pointing to invalid object {0}, aborting and returning true...", replicator); return(true); } var docIDStr = docID.CreateString(); if (docIDStr == null) { WriteLog.To.Database.E(Tag, "Null document ID received in push filter, rejecting..."); return(false); } var flags = revisionFlags.ToDocumentFlags(); return(replicator.PushFilterCallback(docIDStr, revID.CreateString(), dict, flags)); }