private static void DoOpen(C4Socket *socket, C4Address *address, C4Slice options) { var builder = new UriBuilder { Host = address->hostname.CreateString(), Scheme = address->scheme.CreateString(), Port = address->port, Path = address->path.CreateString() }; Uri uri; try { uri = builder.Uri; } catch (Exception) { Native.c4socket_closed(socket, new C4Error(C4ErrorCode.InvalidParameter)); return; } if (uri == null) { Native.c4socket_closed(socket, new C4Error(C4ErrorCode.InvalidParameter)); return; } var opts = FLSliceExtensions.ToObject(NativeRaw.FLValue_FromTrustedData((FLSlice)options)) as Dictionary <string, object>; var replicationOptions = new ReplicatorOptionsDictionary(opts); var socketWrapper = new WebSocketWrapper(uri, socket, replicationOptions); var id = Interlocked.Increment(ref _NextID); socket->nativeHandle = (void *)id; Sockets[id] = socketWrapper; socketWrapper.Start(); }
protected void CreateRev(string docID, C4Slice revID, C4Slice body, bool isNew = true) { LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { var curDoc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, docID, false, err)); var history = new[] { revID, curDoc->revID }; fixed(C4Slice *h = history) { var rq = new C4DocPutRequest { existingRevision = true, docID = curDoc->docID, history = h, historyCount = curDoc->revID.buf != null ? 2UL : 1UL, body = body, deletion = body.buf == null, save = true }; var doc = (C4Document *)LiteCoreBridge.Check(err => { var localRq = rq; return(Native.c4doc_put(Db, &localRq, null, err)); }); Native.c4doc_free(doc); Native.c4doc_free(curDoc); } } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } }
public void TestQueryIndex() { RunTestVariants(() => { CreateIndex(); var e = (C4QueryEnumerator *)LiteCoreBridge.Check(err => Native.c4view_query(_view, null, err)); ulong i = 0; C4Error error; string buf; while (Native.c4queryenum_next(e, &error)) { ++i; if (i <= 100) { buf = i.ToString(); e->docSequence.Should().Be(i, "because the sequences should be ordered"); } else { buf = $"\"doc-{i-100:D3}\""; e->docSequence.Should().Be(i - 100, "because the sequence number should be ordered"); } Native.c4key_toJSON(&e->key).Should().Be(buf, "because the key on the enumerator should be correct"); e->value.Equals(C4Slice.Constant("1234")).Should().BeTrue("because the value should be correct"); } Native.c4queryenum_free(e); error.Code.Should().Be(0, "because otherwise an error occurred somewhere"); i.Should().Be(200, "because all index entries should be covered"); }); }
private void UpdateIndex() { var ind = (C4Indexer *)LiteCoreBridge.Check(err => Native.c4indexer_begin(Db, new[] { _view }, err)); var e = (C4DocEnumerator *)LiteCoreBridge.Check(err => Native.c4indexer_enumerateDocuments(ind, err)); C4Document *doc; C4Error error; while (null != (doc = Native.c4enum_nextDocument(e, &error))) { // Index 'doc': var keys = new C4Key *[2]; var values = new C4Slice[2]; keys[0] = Native.c4key_new(); keys[1] = Native.c4key_new(); NativeRaw.c4key_addString(keys[0], doc->docID); Native.c4key_addNumber(keys[1], doc->sequence); values[0] = values[1] = C4Slice.Constant("1234"); LiteCoreBridge.Check(err => Native.c4indexer_emit(ind, doc, 0, keys, values, err)); Native.c4key_free(keys[0]); Native.c4key_free(keys[1]); Native.c4doc_free(doc); } error.Code.Should().Be(0, "because otherwise an error occurred somewhere"); Native.c4enum_free(e); LiteCoreBridge.Check(err => Native.c4indexer_end(ind, true, err)); }
public void TestDBObserver() { RunTestVariants(() => { var handle = GCHandle.Alloc(this); try { _dbObserver = Native.c4dbobs_create(Db, DBObserverCallback, GCHandle.ToIntPtr(handle).ToPointer()); CreateRev("A", C4Slice.Constant("1-aa"), Body); _dbCallbackCalls.Should().Be(1, "because we should have received a callback"); CreateRev("B", C4Slice.Constant("1-bb"), Body); _dbCallbackCalls.Should().Be(1, "because we should have received a callback"); CheckChanges(new[] { "A", "B" }, new[] { "1-aa", "1-bb" }); CreateRev("B", C4Slice.Constant("2-bbbb"), Body); _dbCallbackCalls.Should().Be(2, "because we should have received a callback"); CreateRev("C", C4Slice.Constant("1-cc"), Body); _dbCallbackCalls.Should().Be(2, "because we should have received a callback"); CheckChanges(new[] { "B", "C" }, new[] { "2-bbbb", "1-cc" }); Native.c4dbobs_free(_dbObserver); _dbObserver = null; CreateRev("A", C4Slice.Constant("2-aaaa"), Body); _dbCallbackCalls.Should().Be(2, "because the observer was disposed"); } finally { handle.Free(); } }); }
public void TestInvalidDocID() { RunTestVariants(() => { NativePrivate.c4log_warnOnErrors(false); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { Action <C4Slice> checkPutBadDocID = (C4Slice docID) => { C4Error e; var rq = new C4DocPutRequest(); rq.body = Body; rq.save = true; rq.docID = docID; ((long)Native.c4doc_put(Db, &rq, null, &e)).Should() .Be(0, "because the invalid doc ID should cause the put to fail"); e.domain.Should().Be(C4ErrorDomain.LiteCoreDomain); e.code.Should().Be((int)C4ErrorCode.BadDocID); }; checkPutBadDocID(C4Slice.Constant("")); string tooLong = new string(Enumerable.Repeat('x', 241).ToArray()); using (var tooLong_ = new C4String(tooLong)) { checkPutBadDocID(tooLong_.AsC4Slice()); } checkPutBadDocID(C4Slice.Constant("oops\x00oops")); // Bad UTF-8 checkPutBadDocID(C4Slice.Constant("oops\noops")); // Control characters } finally { NativePrivate.c4log_warnOnErrors(true); LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } }); }
public void TestImportNames() { // Docs look like: // {"name":{"first":"Travis","last":"Mutchler"},"gender":"female","birthday":"1990-12-21","contact":{"address":{"street":"22 Kansas Cir","zip":"45384","city":"Wilberforce","state":"OH"},"email":["*****@*****.**","*****@*****.**"],"region":"937","phone":["937-3512486"]},"likes":["travelling"],"memberSince":"2010-01-01"} RunTestVariants(() => { var numDocs = ImportJSONLines("../../../C/tests/data/names_300000.json", TimeSpan.FromSeconds(15), true); var complete = numDocs == 300000; #if !DEBUG numDocs.Should().Be(300000, "because otherwise the operation was too slow"); #endif for (int pass = 0; pass < 2; ++pass) { var st = Stopwatch.StartNew(); var n = QueryWhere("{\"contact.address.state\": \"WA\"}", true); st.PrintReport("SQL query of state", n, "doc", _output); if (complete) { n.Should().Be(5053, "because that is the number of WA state contact addresses in the document"); } if (pass == 0) { var st2 = Stopwatch.StartNew(); LiteCoreBridge.Check(err => NativeRaw.c4db_createIndex(Db, C4Slice.Constant("contact.address.state"), C4IndexType.ValueIndex, null, err)); st2.PrintReport("Creating SQL index of state", 1, "index", _output); } } }); }
public void TestDeleteIndexedDoc() { RunTestVariants(() => { LiteCoreBridge.Check(err => Native.c4db_createIndex(Db, "test", Json5("[['length()', ['.name.first']]]"), C4IndexType.ValueIndex, null, err)); // Delete doc "0000015": LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, "0000015", true, err)); var rq = new C4DocPutRequest { docID = C4Slice.Constant("0000015"), history = &doc->revID, historyCount = 1, revFlags = C4RevisionFlags.Deleted, save = true }; var updatedDoc = (C4Document *)LiteCoreBridge.Check(err => { var localRq = rq; return(Native.c4doc_put(Db, &localRq, null, err)); }); Native.c4doc_free(doc); Native.c4doc_free(updatedDoc); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } // Now run a query that would have returned the deleted doc, if it weren't deleted: Compile(Json5("['=', ['length()', ['.name.first']], 9]")); Run().Should().Equal(new[] { "0000099" }, "because otherwise the query returned incorrect results"); }); }
public void TestCreateBlobKeyMismatch() { RunTestVariants(() => { var blobToStore = C4Slice.Constant("This is a blob to store in the store!"); C4BlobKey key, expectedKey = new C4BlobKey(); var i = 0; foreach (var b in Enumerable.Repeat <byte>(0x55, sizeof(C4BlobKey))) { expectedKey.bytes[i++] = b; } C4Error error; NativePrivate.c4log_warnOnErrors(false); try { NativeRaw.c4blob_create(_store, blobToStore, &expectedKey, &key, &error).Should().BeFalse(); error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain); error.code.Should().Be((int)C4ErrorCode.CorruptData); } finally { NativePrivate.c4log_warnOnErrors(true); } Native.c4blob_keyFromString("sha1-QneWo5IYIQ0ZrbCG0hXPGC6jy7E=", &expectedKey); NativeRaw.c4blob_create(_store, blobToStore, &expectedKey, &key, &error).Should().BeTrue(); }); }
private static void TotalAccumulate(object context, C4Key *key, C4Slice value) { var ctx = context as TotalContext; var v = NativeRaw.FLValue_FromTrustedData((FLSlice)value); ctx.total += Native.FLValue_AsDouble(v); }
private void CreateIndex() { var ind = (C4Indexer *)LiteCoreBridge.Check(err => Native.c4indexer_begin(Db, new[] { _view }, err)); var e = (C4DocEnumerator *)LiteCoreBridge.Check(err => Native.c4indexer_enumerateDocuments(ind, err)); C4Document *doc; C4Error error; while (null != (doc = Native.c4enum_nextDocument(e, &error))) { var body = doc->selectedRev.body.CreateString(); var components = body.Trim('(', ')').Split(','); var area = new C4GeoArea(); area.xmin = Double.Parse(components[0]); area.ymin = Double.Parse(components[1]); area.xmax = Double.Parse(components[2]); area.ymax = Double.Parse(components[3]); var keys = new C4Key *[1]; var values = new C4Slice[1]; keys[0] = Native.c4key_newGeoJSON("{\"geo\":true}", area); 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); } Native.c4enum_free(e); error.Code.Should().Be(0, "because otherwise an error occurred somewhere"); LiteCoreBridge.Check(err => Native.c4indexer_end(ind, true, err)); }
internal void CreateRev(C4Database *db, string docID, C4Slice revID, C4Slice body, C4RevisionFlags flags = (C4RevisionFlags)0) { LiteCoreBridge.Check(err => Native.c4db_beginTransaction(db, err)); try { var curDoc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(db, docID, false, err)); var history = new[] { revID, curDoc->revID }; fixed(C4Slice *h = history) { var rq = new C4DocPutRequest { existingRevision = true, docID = curDoc->docID, history = h, historyCount = curDoc->revID.buf != null ? 2UL : 1UL, body = body, revFlags = flags, save = true }; var doc = (C4Document *)LiteCoreBridge.Check(err => { var localRq = rq; return(Native.c4doc_put(db, &localRq, null, err)); }); Native.c4doc_free(doc); Native.c4doc_free(curDoc); } } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(db, true, err)); } }
public void TestCreateRawDoc() { RunTestVariants(() => { var key = C4Slice.Constant("key"); var meta = C4Slice.Constant("meta"); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); LiteCoreBridge.Check(err => NativeRaw.c4raw_put(Db, C4Slice.Constant("test"), key, meta, Body, err)); LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); var doc = (C4RawDocument *)LiteCoreBridge.Check(err => NativeRaw.c4raw_get(Db, C4Slice.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(Body).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"); }); }
public void TestMultiDbObserver() { RunTestVariants(() => { _dbObserver = Native.c4dbobs_create(Db, DBObserverCallback, this); CreateRev("A", C4Slice.Constant("1-aa"), Body); _dbCallbackCalls.Should().Be(1, "because we should have received a callback"); CreateRev("B", C4Slice.Constant("1-bb"), Body); _dbCallbackCalls.Should().Be(1, "because we should have received a callback"); CheckChanges(new[] { "A", "B" }, new[] { "1-aa", "1-bb" }); // Open another database on the same file var otherdb = (C4Database *)LiteCoreBridge.Check(err => Native.c4db_open(DatabasePath(), Native.c4db_getConfig(Db), err)); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(otherdb, err)); try { CreateRev(otherdb, "C", C4Slice.Constant("1-cc"), Body); CreateRev(otherdb, "D", C4Slice.Constant("1-dd"), Body); CreateRev(otherdb, "E", C4Slice.Constant("1-ee"), Body); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(otherdb, true, err)); } _dbCallbackCalls.Should().Be(2, "because the observer should cover all connections"); CheckChanges(new[] { "C", "D", "E" }, new[] { "1-cc", "1-dd", "1-ee" }, true); _dbObserver.Dispose(); _dbObserver = null; CreateRev("A", C4Slice.Constant("2-aaaa"), Body); _dbCallbackCalls.Should().Be(2, "because the observer was disposed"); LiteCoreBridge.Check(err => Native.c4db_close(otherdb, err)); Native.c4db_free(otherdb); }); }
public void TestCreateMultipleRevisions() { RunTestVariants(() => { var docID = DocID.CreateString(); var Body2 = C4Slice.Constant("{\"ok\":\"go\"}"); CreateRev(docID, RevID, Body); CreateRev(docID, Rev2ID, Body2); CreateRev(docID, Rev2ID, Body2, false); // test redundant insert // Reload the doc: var doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_get(Db, DocID, true, err)); doc->flags.Should().Be(C4DocumentFlags.Exists); doc->docID.Equals(DocID).Should().BeTrue("because the doc should have the stored doc ID"); doc->revID.Equals(Rev2ID).Should().BeTrue("because the doc should have the current rev ID"); doc->selectedRev.revID.Equals(Rev2ID).Should().BeTrue("because the current revision is selected"); doc->selectedRev.sequence.Should().Be(2L, "because the current revision is the second one"); doc->selectedRev.body.Equals(Body2).Should().BeTrue("because the selected rev should have the correct body"); if (Versioning == C4DocumentVersioning.RevisionTrees) { // Select 1st revision: Native.c4doc_selectParentRevision(doc).Should().BeTrue("because otherwise selecting the parent revision failed"); doc->selectedRev.revID.Equals(RevID).Should().BeTrue("because the first revision is selected"); doc->selectedRev.sequence.Should().Be(1L, "because the first revision is selected"); doc->selectedRev.body.Equals(C4Slice.Null).Should().BeTrue("because the body hasn't been loaded"); Native.c4doc_hasRevisionBody(doc).Should().BeTrue("because the body still exists"); LiteCoreBridge.Check(err => Native.c4doc_loadRevisionBody(doc, err)); doc->selectedRev.body.Equals(Body).Should().BeTrue("because the body is loaded"); Native.c4doc_selectParentRevision(doc).Should().BeFalse("because the root revision is selected"); Native.c4doc_free(doc); // Compact database: LiteCoreBridge.Check(err => Native.c4db_compact(Db, err)); doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_get(Db, DocID, true, err)); Native.c4doc_selectParentRevision(doc).Should().BeTrue("because otherwise selecting the parent revision failed"); doc->selectedRev.revID.Equals(RevID).Should().BeTrue("because the first revision is selected"); doc->selectedRev.sequence.Should().Be(1L, "because the first revision is selected"); if (Storage != C4StorageEngine.SQLite) { doc->selectedRev.body.Equals(C4Slice.Null).Should().BeTrue("because the body is not available"); Native.c4doc_hasRevisionBody(doc).Should().BeFalse("because the body has been compacted"); C4Error error; Native.c4doc_loadRevisionBody(doc, &error).Should().BeFalse("because the body is unavailable"); } LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { C4Error error; NativeRaw.c4doc_purgeRevision(doc, Rev2ID, &error).Should().Be(2); LiteCoreBridge.Check(err => Native.c4doc_save(doc, 20, err)); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } } Native.c4doc_free(doc); }); }
public void TestCreateMultipleRevisions() { RunTestVariants(() => { C4Slice Body2 = C4Slice.Constant("{\"ok\":\"go\"}"); C4Slice Body3 = C4Slice.Constant("{\"ubu\":\"roi\"}"); var docID = DocID.CreateString(); CreateRev(docID, RevID, Body); CreateRev(docID, Rev2ID, Body2, C4RevisionFlags.KeepBody); CreateRev(docID, Rev2ID, Body2); // test redundant Insert // Reload the doc: var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, docID, true, err)); doc->flags.Should().HaveFlag(C4DocumentFlags.DocExists, "because the document was saved"); doc->docID.Should().Equal(DocID, "because the doc ID should save correctly"); doc->revID.Should().Equal(Rev2ID, "because the doc's rev ID should load correctly"); doc->selectedRev.revID.Should().Equal(Rev2ID, "because the rev's rev ID should load correctly"); doc->selectedRev.sequence.Should().Be(2, "because it is the second revision"); doc->selectedRev.body.Should().Equal(Body2, "because the body should load correctly"); if (Versioning == C4DocumentVersioning.RevisionTrees) { // Select 1st revision: LiteCoreBridge.Check(err => Native.c4doc_selectParentRevision(doc)); doc->selectedRev.revID.Should().Equal(RevID, "because now the first revision is selected"); doc->selectedRev.sequence.Should().Be(1, "because now the first revision is selected"); doc->selectedRev.body.Should().Equal(C4Slice.Null, "because the body of the old revision should be gone"); Native.c4doc_hasRevisionBody(doc).Should().BeFalse("because the body of the old revision should be gone"); Native.c4doc_selectParentRevision(doc).Should().BeFalse("because a root revision has no parent"); Native.c4doc_free(doc); // Add a 3rd revision: CreateRev(docID, Rev3ID, Body3); // Revision 2 should keep its body due to the KeepBody flag doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, docID, true, err)); Native.c4doc_selectParentRevision(doc).Should().BeTrue("because otherwise the selection of the 2nd revision failed"); doc->selectedRev.revID.Should().Equal(Rev2ID, "because the rev's rev ID should load correctly"); doc->selectedRev.sequence.Should().Be(2, "because it is the second revision"); doc->selectedRev.flags.Should().HaveFlag(C4RevisionFlags.KeepBody, "because the KeepBody flag was saved on the revision"); doc->selectedRev.body.Should().Equal(Body2, "because the body should load correctly"); Native.c4doc_free(doc); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, docID, true, err)); var nPurged = NativeRaw.c4doc_purgeRevision(doc, Rev3ID, null); nPurged.Should().Be(3, "because there are three revisions to purge"); LiteCoreBridge.Check(err => Native.c4doc_save(doc, 20, err)); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); Native.c4doc_free(doc); doc = null; } } Native.c4doc_free(doc); }); }
private static void OnDocError(C4Replicator *repl, bool pushing, C4Slice docID, C4Error error, bool transient, void *context) { var replicator = GCHandle.FromIntPtr((IntPtr)context).Target as Replicator; replicator?.DispatchQueue.DispatchAsync(() => { replicator.OnDocError(error, pushing, docID.CreateString() ?? "", transient); }); }
public void TestDatabaseRekey() { RunTestVariants(() => { CreateNumberedDocs(99); // Add blob to the store: var blobToStore = C4Slice.Constant("This is a blob to store in the store!"); var blobKey = new C4BlobKey(); var blobStore = (C4BlobStore *)LiteCoreBridge.Check(err => Native.c4db_getBlobStore(Db, err)); LiteCoreBridge.Check(err => { C4BlobKey local; var retVal = NativeRaw.c4blob_create(blobStore, blobToStore, null, &local, err); blobKey = local; return(retVal); }); C4Error error; var blobResult = NativeRaw.c4blob_getContents(blobStore, blobKey, &error); ((C4Slice)blobResult).Should().Equal(blobToStore); Native.c4slice_free(blobResult); // If we're on the unexcrypted pass, encrypt the db. Otherwise, decrypt it: var newKey = new C4EncryptionKey(); if (Native.c4db_getConfig(Db)->encryptionKey.algorithm == C4EncryptionAlgorithm.None) { newKey.algorithm = C4EncryptionAlgorithm.AES256; var keyBytes = Encoding.ASCII.GetBytes("a different key than default...."); Marshal.Copy(keyBytes, 0, (IntPtr)newKey.bytes, 32); } var tmp = newKey; LiteCoreBridge.Check(err => { var local = tmp; return(Native.c4db_rekey(Db, &local, err)); }); // Verify the db works: Native.c4db_getDocumentCount(Db).Should().Be(99); ((IntPtr)blobStore).Should().NotBe(IntPtr.Zero); blobResult = NativeRaw.c4blob_getContents(blobStore, blobKey, &error); ((C4Slice)blobResult).Should().Equal(blobToStore); Native.c4slice_free(blobResult); // Check thqat db can be reopened with the new key: Native.c4db_getConfig(Db)->encryptionKey.algorithm.Should().Be(newKey.algorithm); for (int i = 0; i < 32; i++) { Native.c4db_getConfig(Db)->encryptionKey.bytes[i].Should().Be(newKey.bytes[i]); } ReopenDB(); }); }
private uint IndexLikesView() { var likesKey = Native.FLDictKey_Init("likes", true); var keys = new[] { Native.c4key_new(), Native.c4key_new(), Native.c4key_new() }; var values = new C4Slice[3]; uint totalLikes = 0; if (_likesView == null) { _likesView = (C4View *)LiteCoreBridge.Check(err => Native.c4view_open(Db, null, "likes", "1", Native.c4db_getConfig(Db), err)); } var views = new[] { _likesView }; var indexer = (C4Indexer *)LiteCoreBridge.Check(err => Native.c4indexer_begin(Db, views, err)); var e = (C4DocEnumerator *)LiteCoreBridge.Check(err => Native.c4indexer_enumerateDocuments(indexer, err)); C4Error error; while (Native.c4enum_next(e, &error)) { var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4enum_getDocument(e, err)); var body = Native.FLValue_AsDict(NativeRaw.FLValue_FromTrustedData((FLSlice)doc->selectedRev.body)); var likes = Native.FLValue_AsArray(Native.FLDict_GetWithKey(body, &likesKey)); FLArrayIterator iter; Native.FLArrayIterator_Begin(likes, &iter); uint nLikes; for (nLikes = 0; nLikes < 3; ++nLikes) { var like = NativeRaw.FLValue_AsString(Native.FLArrayIterator_GetValue(&iter)); if (like.buf == null) { break; } Native.c4key_reset(keys[nLikes]); NativeRaw.c4key_addString(keys[nLikes], (C4Slice)like); Native.FLArrayIterator_Next(&iter); } totalLikes += nLikes; LiteCoreBridge.Check(err => Native.c4indexer_emit(indexer, doc, 0, nLikes, keys, values, err)); Native.c4doc_free(doc); } Native.c4enum_free(e); error.Code.Should().Be(0, "because otherwise an error occurred somewhere"); LiteCoreBridge.Check(err => Native.c4indexer_end(indexer, true, err)); for (uint i = 0; i < 3; i++) { Native.c4key_free(keys[i]); } return(totalLikes); }
internal static unsafe void PinAndUse(this RevisionID revID, Action <C4Slice> action) { var data = revID.AsData(); fixed(byte *dataPtr = data) { var slice = new C4Slice(dataPtr, (uint)data.Length); action(slice); } }
public void TestDuplicateRev() { RunTestVariants(() => { var docID = "mydoc"; var body = "{\"key\":\"value\"}"; var doc = PutDoc(docID, null, body); var revID = doc->revID.CreateString(); Native.c4doc_free(doc); body = "{\"key\":\"newvalue\"}"; doc = PutDoc(docID, revID, body); var revID2a = doc->revID.CreateString(); Native.c4doc_free(doc); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); var success = false; try { using (var docID_ = new C4String(docID)) using (var revID_ = new C4String(revID)) using (var body_ = new C4String(body)) { var history = new C4Slice[] { revID_.AsC4Slice() }; fixed(C4Slice * history_ = history) { var rq = new C4DocPutRequest { allowConflict = true, docID = docID_.AsC4Slice(), history = history_, historyCount = 1, body = body_.AsC4Slice(), revFlags = 0, save = true }; doc = (C4Document *)LiteCoreBridge.Check(err => { var local = rq; return(Native.c4doc_put(Db, &local, null, err)); }); doc->docID.CreateString().Should().Be(docID); } } } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, success, err)); } var revID2b = doc->revID.CreateString(); Native.c4doc_free(doc); revID2b.Should().Be(revID2a, "because an identical revision was inserted"); }); }
public void TestDocObserver() { RunTestVariants(() => { CreateRev("A", C4Slice.Constant("1-aa"), Body); _docObserver = Native.c4docobs_create(Db, "A", DocObserverCallback, this); CreateRev("A", C4Slice.Constant("2-bb"), Body); CreateRev("B", C4Slice.Constant("1-bb"), Body); _docCallbackCalls.Should().Be(1, "because there was only one update to the doc in question"); }); }
static Test() { var enc = Native.FLEncoder_New(); Native.FLEncoder_BeginDict(enc, 1); Native.FLEncoder_WriteKey(enc, "answer"); Native.FLEncoder_WriteInt(enc, 42); Native.FLEncoder_EndDict(enc); var result = NativeRaw.FLEncoder_Finish(enc, null); FleeceBody = result; }
public void TestDatabaseCompact() { RunTestVariants(() => { var doc1ID = C4Slice.Constant("doc001"); var doc2ID = C4Slice.Constant("doc002"); var doc3ID = C4Slice.Constant("doc003"); var content1 = "This is the first attachment"; var content2 = "This is the second attachment"; var atts = new List <string>(); C4BlobKey key1, key2; atts.Add(content1); LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err)); try { key1 = AddDocWithAttachments(doc1ID, atts, "text/plain")[0]; atts.Clear(); atts.Add(content2); key2 = AddDocWithAttachments(doc2ID, atts, "text/plain")[0]; AddDocWithAttachments(doc3ID, atts, "text/plain"); } finally { LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err)); } var store = (C4BlobStore *)LiteCoreBridge.Check(err => Native.c4db_getBlobStore(Db, err)); LiteCoreBridge.Check(err => Native.c4db_compact(Db, err)); Native.c4blob_getSize(store, key1).Should() .BeGreaterThan(0, "because the attachment should survive the first compact"); Native.c4blob_getSize(store, key2).Should() .BeGreaterThan(0, "because the attachment should survive the first compact"); CreateRev("doc001", Rev2ID, C4Slice.Null, C4RevisionFlags.Deleted); LiteCoreBridge.Check(err => Native.c4db_compact(Db, err)); Native.c4blob_getSize(store, key1).Should().Be(-1, "because the attachment should be collected in the second compact"); Native.c4blob_getSize(store, key2).Should() .BeGreaterThan(0, "because the attachment should survive the second compact"); CreateRev("doc002", Rev2ID, C4Slice.Null, C4RevisionFlags.Deleted); LiteCoreBridge.Check(err => Native.c4db_compact(Db, err)); Native.c4blob_getSize(store, key1).Should().Be(-1, "because the attachment should still be gone in the third compact"); Native.c4blob_getSize(store, key2).Should() .BeGreaterThan(0, "because the attachment should survive the third compact"); CreateRev("doc003", Rev2ID, C4Slice.Null, C4RevisionFlags.Deleted); LiteCoreBridge.Check(err => Native.c4db_compact(Db, err)); Native.c4blob_getSize(store, key1).Should().Be(-1, "because the attachment should still be gone in the fourth compact"); Native.c4blob_getSize(store, key2).Should().Be(-1, "because the attachment should be collected in the fourth compact"); }); }
static Test() { #if NETCOREAPP1_0 LoadDLL(); #endif var enc = Native.FLEncoder_New(); Native.FLEncoder_BeginDict(enc, 1); Native.FLEncoder_WriteKey(enc, "answer"); Native.FLEncoder_WriteInt(enc, 42); Native.FLEncoder_EndDict(enc); var result = NativeRaw.FLEncoder_Finish(enc, null); FleeceBody = result; }
static Test() { #if NETCOREAPP2_0 Couchbase.Lite.Support.NetDesktop.Activate(); #endif var enc = Native.FLEncoder_New(); Native.FLEncoder_BeginDict(enc, 1); Native.FLEncoder_WriteKey(enc, "answer"); Native.FLEncoder_WriteInt(enc, 42); Native.FLEncoder_EndDict(enc); var result = NativeRaw.FLEncoder_Finish(enc, null); FleeceBody = (C4Slice)result; }
private C4Document *PutDoc(C4Database *db, string docID, string revID, string body, C4RevisionFlags flags, C4Error *error = null) { LiteCoreBridge.Check(err => Native.c4db_beginTransaction(db, err)); var success = false; try { using (var docID_ = new C4String(docID)) using (var revID_ = new C4String(revID)) using (var body_ = new C4String(body)) { var history = new C4Slice[] { revID_.AsC4Slice() }; fixed(C4Slice *history_ = history) { var rq = new C4DocPutRequest { allowConflict = false, docID = docID_.AsC4Slice(), history = revID == null ? null : history_, historyCount = revID == null ? 0UL : 1UL, body = body_.AsC4Slice(), revFlags = flags, 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 { LiteCoreBridge.Check(err => Native.c4db_endTransaction(db, success, err)); } }
private void CreateFullTextIndex(uint docCount) { for (uint i = 1; i <= docCount; i++) { string docID = $"doc-{i:D3}"; var body = C4Slice.Null; switch (i % 3) { case 0: body = C4Slice.Constant("The cat sat on the mat"); break; case 1: body = C4Slice.Constant("Outside SomeWhere a c\u00e4t was barking"); break; case 2: body = C4Slice.Constant("The bark of a tree is rough?"); break; } CreateRev(docID, RevID, body); } var ind = (C4Indexer *)LiteCoreBridge.Check(err => Native.c4indexer_begin(Db, new[] { _view }, err)); var e = (C4DocEnumerator *)LiteCoreBridge.Check(err => Native.c4indexer_enumerateDocuments(ind, err)); C4Document *doc; C4Error error; while (null != (doc = Native.c4enum_nextDocument(e, &error))) { // Index 'doc': var keys = new C4Key *[1]; var values = new C4Slice[1]; keys[0] = NativeRaw.c4key_newFullTextString(doc->selectedRev.body, C4Slice.Constant("en")); 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); LiteCoreBridge.Check(err => Native.c4indexer_end(ind, true, err)); }
public void TestDocObserver() { RunTestVariants(() => { var handle = GCHandle.Alloc(this); try { CreateRev("A", C4Slice.Constant("1-aa"), Body); _docObserver = Native.c4docobs_create(Db, "A", DocObserverCallback, GCHandle.ToIntPtr(handle).ToPointer()); CreateRev("A", C4Slice.Constant("2-bb"), Body); CreateRev("B", C4Slice.Constant("1-bb"), Body); _docCallbackCalls.Should().Be(1, "because there was only one update to the doc in question"); } finally { handle.Free(); } }); }
internal C4BlobKey[] AddDocWithAttachments(C4Slice docID, List <string> atts, string contentType) { var keys = new List <C4BlobKey>(); var json = new StringBuilder(); json.Append("{attached: ["); foreach (var att in atts) { var key = new C4BlobKey(); LiteCoreBridge.Check(err => { var localKey = key; var retVal = Native.c4blob_create(Native.c4db_getBlobStore(Db, null), Encoding.UTF8.GetBytes(att), null, &localKey, err); key = localKey; return(retVal); }); keys.Add(key); var keyStr = Native.c4blob_keyToString(key); json.Append( $"{{'{Constants.ObjectTypeProperty}': '{Constants.ObjectTypeBlob}', 'digest': '{keyStr}', length: {att.Length}, 'content_type': '{contentType}'}},"); } json.Append("]}"); var jsonStr = Native.FLJSON5_ToJSON(json.ToString(), null); using (var jsonStr_ = new C4String(jsonStr)) { C4Error error; var body = NativeRaw.c4db_encodeJSON(Db, jsonStr_.AsC4Slice(), &error); ((long)body.buf).Should().NotBe(0, "because otherwise the encode failed"); var rq = new C4DocPutRequest(); rq.docID = docID; rq.revFlags = C4RevisionFlags.HasAttachments; rq.body = (C4Slice)body; rq.save = true; var doc = Native.c4doc_put(Db, &rq, null, &error); Native.c4slice_free(body); ((long)doc).Should().NotBe(0, "because otherwise the put failed"); Native.c4doc_free(doc); return(keys.ToArray()); } }
private static void c4log_wedge(C4LogLevel level, C4Slice message) { if (_LogCallback != null) { var sharpString = (string)message; _LogCallback(level, sharpString); } }
/// <summary> /// Emits new keys/values derived from one document, for one view. /// This function needs to be called once for each(document, view) pair.Even if the view's map /// function didn't emit anything, the old keys/values need to be cleaned up. /// /// Values are uninterpreted by CBForest, but by convention are JSON. A special value "*" /// (a single asterisk) is used as a placeholder for the entire document. /// /// </summary> /// <param name="indexer">The indexer to operate on</param> /// <param name="document">The document being indexed</param> /// <param name="viewNumber">The position of the view in the indexer's views[] array</param> /// <param name="emittedKeys">Array of keys being emitted</param> /// <param name="emittedValues">Array of values being emitted. (JSON by convention.)</param> /// <param name="outError">The error that occurred if the operation doesn't succeed</param> /// <returns>true on success, false otherwise</returns> public static bool c4indexer_emit(C4Indexer *indexer, C4Document *document, uint viewNumber, C4Key*[] emittedKeys, C4Slice[] emittedValues, C4Error *outError) { fixed(C4Key** keysPtr = emittedKeys) fixed(C4Slice* valuesPtr = emittedValues) { return c4indexer_emit(indexer, document, viewNumber, (uint)emittedKeys.Length, keysPtr, valuesPtr, outError); } }
public static extern bool c4raw_put(C4Database *db, C4Slice storeName, C4Slice key, C4Slice meta, C4Slice body, C4Error *outError);
private static extern C4RawDocument* _c4raw_get(C4Database *db, C4Slice storeName, C4Slice docID, C4Error *outError);
public static extern int c4doc_insertRevisionWithHistory(C4Document *doc, C4Slice body, [MarshalAs(UnmanagedType.U1)]bool deleted, [MarshalAs(UnmanagedType.U1)]bool hasAttachments, C4Slice* history, uint historyCount, C4Error *outError);
private static void Log(C4LogLevel level, C4Slice message) { string[] levelNames = new[] { "debug", "info", "WARNING", "ERROR" }; Console.Error.WriteLineAsync(String.Format("CBForest-C {0}: {1}", levelNames[(int)level], (string)message)); }
public static extern void c4key_addString(C4Key *key, C4Slice s);
private static extern C4View* _c4view_open(C4Database *db, C4Slice path, C4Slice viewName, C4Slice version, C4DatabaseFlags flags, C4EncryptionKey *encryptionKey, C4Error *outError);
public static C4Key* c4key_withBytes(C4Slice slice) { #if DEBUG var retVal = _c4key_withBytes(slice); if(retVal != null) { _AllocatedObjects[(IntPtr)retVal] = "C4Key"; #if ENABLE_LOGGING Console.WriteLine("[c4key_withBytes] Allocated 0x{0}", ((IntPtr)retVal).ToString("X")); #endif } return retVal; #else return _c4key_withBytes(slice); #endif }
/// <summary> /// Creates a C4Key by copying the data, which must be in the C4Key binary format. /// </summary> /// <param name="bytes">The data to use in the C4Key</param> /// <returns>A pointer to the created C4Key</returns> public static C4Key* c4key_withBytes(IEnumerable<byte> bytes) { var realized = bytes.ToArray(); fixed(byte* ptr = realized) { var slice = new C4Slice(ptr, (uint)realized.Length); return c4key_withBytes(slice); } }
private static extern C4Key* _c4key_withBytes(C4Slice slice);
public static extern int c4doc_purgeRevision(C4Document *doc, C4Slice revId, C4Error *outError);
public static extern bool c4doc_setType(C4Document *doc, C4Slice docType, C4Error *outError);
public static extern bool c4doc_selectRevision(C4Document *doc, C4Slice revID, [MarshalAs(UnmanagedType.U1)]bool withBody, C4Error *outError);
/// <summary> /// Reads a raw document from the database. In Couchbase Lite the store named "info" is used for /// per-database key/value pairs, and the store "_local" is used for local documents. /// </summary> /// <param name="db">The database to operate on</param> /// <param name="storeName">The name of the store to read from</param> /// <param name="docID">The ID of the document to retrieve</param> /// <param name="outError">The error that occurred if the operation doesn't succeed</param> /// <returns>A pointer to the retrieved document on success, or null on failure</returns> public static C4RawDocument* c4raw_get(C4Database *db, C4Slice storeName, C4Slice docID, C4Error *outError) { #if DEBUG var retVal = _c4raw_get(db, storeName, docID, outError); if(retVal != null) { _AllocatedObjects.TryAdd((IntPtr)retVal, "C4RawDocument"); #if ENABLE_LOGGING Console.WriteLine("[c4raw_get] Allocated 0x{0}", ((IntPtr)retVal).ToString("X")); #endif } return retVal; #else return _c4raw_get(db, storeName, docID, outError); #endif }
public static extern bool c4indexer_emit(C4Indexer *indexer, C4Document *document, uint viewNumber, uint emitCount, C4Key** emittedKeys, C4Slice* emittedValues, C4Error *outError);
public static extern bool c4db_purgeDoc(C4Database *db, C4Slice docId, C4Error *outError);
private static extern void c4key_addMapKey(C4Key *key, C4Slice s);
public static extern int c4doc_insertRevision(C4Document *doc, C4Slice revID, C4Slice body, [MarshalAs(UnmanagedType.U1)]bool deleted, [MarshalAs(UnmanagedType.U1)]bool hasAttachments, [MarshalAs(UnmanagedType.U1)]bool allowConflict, C4Error *outError);
/// <summary> /// Opens a view, or creates it if the file doesn't already exist. /// </summary> /// <param name="db">The database the view is associated with</param> /// <param name="path">The file that the view is stored in</param> /// <param name="viewName">The name of the view</param> /// <param name="version">The version of the view's map function</param> /// <param name="flags">The flags for opening the view file</param> /// <param name="encryptionKey">The option encryption key used to encrypt the database</param> /// <param name="outError">The error that occurred if the operation doesn't succeed</param> /// <returns>A pointer to the view on success, otherwise null</returns> public static C4View* c4view_open(C4Database *db, C4Slice path, C4Slice viewName, C4Slice version, C4DatabaseFlags flags, C4EncryptionKey *encryptionKey, C4Error *outError) { #if DEBUG var retVal = _c4view_open(db, path, viewName, version, flags, encryptionKey, outError); if(retVal != null) { _AllocatedObjects.TryAdd((IntPtr)retVal, "C4View"); #if ENABLE_LOGGING Console.WriteLine("[c4view_open] Allocated 0x{0}", ((IntPtr)retVal).ToString("X")); #endif } return retVal; #else return _c4view_open(db, path, viewName, version, flags, encryptionKey, outError); #endif }
private static extern C4DocEnumerator* _c4db_enumerateAllDocs(C4Database *db, C4Slice startDocID, C4Slice endDocID, C4EnumeratorOptions *options, C4Error *outError);
public static extern void c4key_addMapKey(C4Key *key, C4Slice s);
public static C4Document* c4doc_get(C4Database *db, C4Slice docID, bool mustExist, C4Error *outError) { #if DEBUG var retVal = _c4doc_get(db, docID, mustExist, outError); if(retVal != null) { _AllocatedObjects[(IntPtr)retVal] = "C4Document"; #if ENABLE_LOGGING Console.WriteLine("[c4doc_get] Allocated 0x{0}", ((IntPtr)retVal).ToString("X")); #endif } return retVal; #else return _c4doc_get(db, docID, mustExist, outError); #endif }
public static extern void c4slice_free(C4Slice slice);
public static C4DocEnumerator* c4db_enumerateAllDocs(C4Database *db, C4Slice startDocID, C4Slice endDocID, C4EnumeratorOptions *options, C4Error *outError) { #if DEBUG var retVal = _c4db_enumerateAllDocs(db, startDocID, endDocID, options, outError); if(retVal != null) { _AllocatedObjects[(IntPtr)retVal] = "C4DocEnumerator"; #if ENABLE_LOGGING Console.WriteLine("[c4db_enumerateAllDocs] Allocated 0x{0}", ((IntPtr)retVal).ToString("X")); #endif } return retVal; #else return _c4db_enumerateAllDocs(db, startDocID, endDocID, options, outError); #endif }
private static extern C4Database* _c4db_open(C4Slice path, C4DatabaseFlags flags, C4EncryptionKey *encryptionKey, C4Error *outError);
public static C4Database* c4db_open(C4Slice path, C4DatabaseFlags flags, C4EncryptionKey *encryptionKey, C4Error *outError) { #if DEBUG var retVal = _c4db_open(path, flags, encryptionKey, outError); if(retVal != null) { _AllocatedObjects[(IntPtr)retVal] = "C4Database"; #if ENABLE_LOGGING Console.WriteLine("[c4db_open] Allocated 0x{0}", ((IntPtr)retVal).ToString("X")); #endif } return retVal; #else return _c4db_open(path, flags, encryptionKey, outError); #endif }
private static extern C4DocEnumerator* _c4db_enumerateSomeDocs(C4Database *db, C4Slice* docIDs, uint docIDsCount, C4EnumeratorOptions *options, C4Error *outError);
private static extern C4Document* _c4doc_get(C4Database *db, C4Slice docID, [MarshalAs(UnmanagedType.U1)]bool mustExist, C4Error *outError);