Exemplo n.º 1
0
        // Read a file that contains a JSON document per line. Every line becomes a document.
        protected uint ImportJSONLines(string path, TimeSpan timeout, bool verbose)
        {
            if (verbose)
            {
                WriteLine($"Reading {path}...");
            }

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

            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            try {
                var encoder = Native.FLEncoder_New();
                ReadFileByLines(path, line => {
                    FLError error;
                    NativeRaw.FLEncoder_ConvertJSON(encoder, line);
                    var body = NativeRaw.FLEncoder_Finish(encoder, &error);
                    ((long)body.buf).Should().NotBe(0, "because otherwise the encode failed");
                    Native.FLEncoder_Reset(encoder);

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

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

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

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

                    return(true);
                });

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

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

            return(numDocs);
        }
        public void TestQueryFullTextIndex()
        {
            RunTestVariants(() => {
                CreateFullTextIndex(3);

                // Search for "somewhere":
                var e = (C4QueryEnumerator *)LiteCoreBridge.Check(err => Native.c4view_fullTextQuery(_view,
                                                                                                     "somewhere", null, null, err));

                C4Error error;
                Native.c4queryenum_next(e, &error).Should().BeTrue("because otherwise the full text query failed");
                e->docID.Equals(C4Slice.Constant("doc-001")).Should().BeTrue("because doc-001 contains the text");
                e->docSequence.Should().Be(1, "because the enumerator should have the correct sequence for the current doc");
                e->fullTextTermCount.Should().Be(1, "because the full text information should be correct");
                e->fullTextTerms[0].termIndex.Should().Be(0, "because the full text information should be correct");
                e->fullTextTerms[0].start.Should().Be(8, "because the full text information should be correct");
                e->fullTextTerms[0].length.Should().Be(9, "because the full text information should be correct");

                Native.c4queryenum_next(e, &error).Should().BeFalse("beacuse the query only has one row");
                error.Code.Should().Be(0, "because otherwise an error occurred somewhere");
                Native.c4queryenum_free(e);

                // Search for "cat":
                e = (C4QueryEnumerator *)LiteCoreBridge.Check(err => Native.c4view_fullTextQuery(_view,
                                                                                                 "cat", null, null, err));
                int i = 0;
                while (Native.c4queryenum_next(e, &error))
                {
                    ++i;
                    e->fullTextTermCount.Should().Be(1, "because the full text information should be correct");
                    e->fullTextTerms[0].termIndex.Should().Be(0, "because the full text information should be correct");
                    if (e->docSequence == 1)
                    {
                        e->fullTextTerms[0].start.Should().Be(20, "because the full text information should be correct");
                        e->fullTextTerms[0].length.Should().Be(4, "because the full text information should be correct");
                    }
                    else
                    {
                        e->docSequence.Should().Be(3, "because the correct document should be indexed");
                        e->fullTextTerms[0].start.Should().Be(4, "because the full text information should be correct");
                        e->fullTextTerms[0].length.Should().Be(3, "because the full text information should be correct");
                    }
                }

                Native.c4queryenum_free(e);
                error.Code.Should().Be(0, "because otherwise an error occurred somewhere");
                i.Should().Be(2, "because there are two documents valid for the query");

                // Search for "cat bark":
                e = (C4QueryEnumerator *)LiteCoreBridge.Check(err => Native.c4view_fullTextQuery(_view,
                                                                                                 "cat bark", null, null, err));
                Native.c4queryenum_next(e, &error).Should().BeTrue("because otherwise the full text query failed");
                e->docID.Equals(C4Slice.Constant("doc-001")).Should().BeTrue("because doc-001 contains the text");
                e->docSequence.Should().Be(1, "because the enumerator should have the correct sequence for the current doc");
                e->fullTextTermCount.Should().Be(2, "because the full text information should be correct");
                e->fullTextTerms[0].termIndex.Should().Be(0, "because the full text information should be correct");
                e->fullTextTerms[0].start.Should().Be(20, "because the full text information should be correct");
                e->fullTextTerms[0].length.Should().Be(4, "because the full text information should be correct");
                e->fullTextTerms[1].termIndex.Should().Be(1, "because the full text information should be correct");
                e->fullTextTerms[1].start.Should().Be(29, "because the full text information should be correct");
                e->fullTextTerms[1].length.Should().Be(7, "because the full text information should be correct");

                Native.c4queryenum_next(e, &error).Should().BeFalse("beacuse the query only has one row");
                error.Code.Should().Be(0, "because otherwise an error occurred somewhere");
                Native.c4queryenum_free(e);
            });
        }
Exemplo n.º 3
0
        private C4Document *ForceInsert(C4Database *db, string docID, string[] history, FLSlice 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))
                {
                    fixed(FLSlice *sliceHistory_ = sliceHistory)
                    {
                        var rq = new C4DocPutRequest
                        {
                            docID            = docID_.AsFLSlice(),
                            existingRevision = true,
                            allowConflict    = true,
                            history          = sliceHistory_,
                            historyCount     = (ulong)history.Length,
                            body             = body,
                            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));
            }
        }
Exemplo n.º 4
0
        public void TestRevTree()
        {
            RunTestVariants(() =>
            {
                if (!IsRevTrees(Db))
                {
                    return;
                }

                // TODO: Observer

                var docID   = "MyDocID";
                var body    = JSON2Fleece("{'message':'hi'}");
                var history = new[] { "4-4444", "3-3333", "2-2222", "1-1111" };
                ForceInsert(docID, history, (FLSlice)body);

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

                var doc = GetDoc(docID, C4DocContentLevel.DocGetAll);
                VerifyRev(doc, history, (FLSlice)body);
                Native.c4doc_release(doc);

                var lastSeq = Native.c4db_getLastSequence(Db);
                ForceInsert(docID, history, (FLSlice)body);
                Native.c4db_getLastSequence(Db).Should().Be(lastSeq, "because the last operation should have been a no-op");

                _remoteDocID        = 1;
                var conflictHistory = new[] { "5-5555", "4-4545", "3-3030", "2-2222", "1-1111" };
                var conflictBody    = JSON2Fleece("{'message':'yo'}");
                ForceInsert(docID, conflictHistory, (FLSlice)conflictBody);
                _remoteDocID = 0;

                // Conflicts are a bit different than CBL 1.x here.  A conflicted revision is marked with the Conflict flag,
                // and such revisions can never be current.  So in other words, the oldest revision always wins the conflict;
                // it has nothing to do with revIDs
                Native.c4db_getDocumentCount(Db).Should().Be(1UL);
                doc = GetDoc(docID, C4DocContentLevel.DocGetAll);
                VerifyRev(doc, history, (FLSlice)body);
                Native.c4doc_release(doc);

                // TODO: Conflict check

                var otherDocID   = "AnotherDocID";
                var otherBody    = JSON2Fleece("{'language':'jp'}");
                var otherHistory = new[] { "1-1010" };
                ForceInsert(otherDocID, otherHistory, (FLSlice)otherBody);

                doc = GetDoc(docID, C4DocContentLevel.DocGetAll);
                LiteCoreBridge.Check(err => Native.c4doc_selectRevision(doc, "2-2222", false, err));
                doc->selectedRev.flags.Should().NotHaveFlag(C4RevisionFlags.KeepBody);
                NativeRaw.c4doc_getRevisionBody(doc).CreateString().Should().BeNull();
                Native.c4doc_release(doc);

                doc = GetDoc(otherDocID, C4DocContentLevel.DocGetAll);
                C4Error error;
                Native.c4doc_selectRevision(doc, "666-6666", false, &error).Should().BeFalse();
                error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain);
                error.code.Should().Be((int)C4ErrorCode.NotFound);
                Native.c4doc_release(doc);

                Native.c4db_getLastSequence(Db).Should().Be(3UL, "because duplicate inserted rows should not advance the last sequence");

                doc = GetDoc(docID);
                doc->revID.CreateString().Should().Be(history[0], "because the earlier revision should win the conflict");
                doc->selectedRev.revID.CreateString().Should().Be(history[0]);
                Native.c4doc_release(doc);

                doc = GetDoc(docID, C4DocContentLevel.DocGetAll);
                var conflictingRevs = GetRevisionHistory(doc, true, true);
                conflictingRevs.Count.Should().Be(2);
                conflictingRevs.Should().Equal(history[0], conflictHistory[0]);
                Native.c4doc_release(doc);

                var e = (C4DocEnumerator *)LiteCoreBridge.Check(err =>
                {
                    var options = C4EnumeratorOptions.Default;
                    return(Native.c4db_enumerateChanges(Db, 0, &options, err));
                });

                var counter = 0;
                while (Native.c4enum_next(e, &error))
                {
                    C4DocumentInfo docInfo;
                    Native.c4enum_getDocumentInfo(e, &docInfo);
                    if (counter == 0)
                    {
                        docInfo.docID.CreateString().Should().Be(docID);
                        docInfo.revID.CreateString().Should().Be(history[0]);
                    }
                    else if (counter == 1)
                    {
                        docInfo.docID.CreateString().Should().Be(otherDocID);
                        docInfo.revID.CreateString().Should().Be(otherHistory[0]);
                    }

                    counter++;
                }

                Native.c4enum_free(e);
                counter.Should().Be(2, "because only two documents are present");

                e = (C4DocEnumerator *)LiteCoreBridge.Check(err =>
                {
                    var options    = C4EnumeratorOptions.Default;
                    options.flags |= C4EnumeratorFlags.IncludeDeleted;
                    return(Native.c4db_enumerateChanges(Db, 0, &options, err));
                });

                counter = 0;
                while (Native.c4enum_next(e, &error))
                {
                    doc = Native.c4enum_getDocument(e, &error);
                    if (doc == null)
                    {
                        break;
                    }

                    do
                    {
                        if (counter == 0)
                        {
                            doc->docID.CreateString().Should().Be(docID);
                            doc->selectedRev.revID.CreateString().Should().Be(history[0]);
                        }
                        else if (counter == 1)
                        {
                            doc->docID.CreateString().Should().Be(docID);
                            doc->selectedRev.revID.CreateString().Should().Be(conflictHistory[0]);
                        }
                        else if (counter == 2)
                        {
                            doc->docID.CreateString().Should().Be(otherDocID);
                            doc->selectedRev.revID.CreateString().Should().Be(otherHistory[0]);
                        }

                        counter++;
                    } while (Native.c4doc_selectNextLeafRevision(doc, true, false, &error));

                    Native.c4doc_release(doc);
                }

                Native.c4enum_free(e);
                counter.Should().Be(3, "because only two documents are present, but one has two conflicting revisions");

                doc = PutDoc(docID, conflictHistory[0], FLSlice.Null, C4RevisionFlags.Deleted);
                Native.c4doc_release(doc);
                doc = GetDoc(docID);
                //TODO: Uncomment once https://github.com/couchbase/couchbase-lite-core/issues/57 is fixed
                //doc->revID.CreateString().Should().Be(history[0]);
                //doc->selectedRev.revID.CreateString().Should().Be(history[0]);
                //VerifyRev(doc, history, (FLSlice)body);
                Native.c4doc_release(doc);

                doc = PutDoc(docID, history[0], FLSlice.Null, C4RevisionFlags.Deleted);
                Native.c4doc_release(doc);
                Native.FLSliceResult_Release(body);
                Native.FLSliceResult_Release(otherBody);
                Native.FLSliceResult_Release(conflictBody);

                // TODO: Need to implement following tests
            });
        }
Exemplo n.º 5
0
 public void TestDatabaseBlobStore()
 {
     RunTestVariants(() => {
         LiteCoreBridge.Check(err => Native.c4db_getBlobStore(Db, 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));
            }
        }
Exemplo n.º 7
0
        public void TestUpdate()
        {
            RunTestVariants(() =>
            {
                WriteLine("Begin test");
                C4Document *doc = null;
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin create");
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_create(Db, DocID, FleeceBody, 0, err));
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

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

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

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

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

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

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

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

                Native.c4doc_free(doc);
                Native.c4doc_free(doc2);
            });
        }
        public void TestExpired()
        {
            RunTestVariants(() => {
                const string docID = "expire_me";
                CreateRev(docID, RevID, Body);
                var expire = DateTimeOffset.UtcNow.Add(TimeSpan.FromSeconds(1)).ToUnixTimeSeconds();
                LiteCoreBridge.Check(err => Native.c4doc_setExpiration(Db, docID, (ulong)expire, err));

                expire = DateTimeOffset.UtcNow.Add(TimeSpan.FromSeconds(2)).ToUnixTimeSeconds();
                // Make sure setting it to the same is also true
                LiteCoreBridge.Check(err => Native.c4doc_setExpiration(Db, docID, (ulong)expire, err));
                LiteCoreBridge.Check(err => Native.c4doc_setExpiration(Db, docID, (ulong)expire, err));

                const string docID2 = "expire_me_too";
                CreateRev(docID2, RevID, Body);
                LiteCoreBridge.Check(err => Native.c4doc_setExpiration(Db, docID2, (ulong)expire, err));

                const string docID3 = "dont_expire_me";
                CreateRev(docID3, RevID, Body);

                WriteLine("---- Wait till expiration time...");
                Task.Delay(TimeSpan.FromSeconds(2)).Wait();

                WriteLine("---- Scan expired docs (#1)");
                var e            = (C4ExpiryEnumerator *)LiteCoreBridge.Check(err => Native.c4db_enumerateExpired(Db, err));
                int expiredCount = 0;
                while (Native.c4exp_next(e, null))
                {
                    var existingDocID = Native.c4exp_getDocID(e);
                    existingDocID.Should().NotBe(docID3, "because the last document is not scheduled for expiration");
                    expiredCount++;
                }

                Native.c4exp_free(e);
                expiredCount.Should().Be(2, "because 2 documents were scheduled for expiration");
                Native.c4doc_getExpiration(Db, docID).Should().Be((ulong)expire, "because that was what was set as the expiration");
                Native.c4doc_getExpiration(Db, docID2).Should().Be((ulong)expire, "because that was what was set as the expiration");
                Native.c4db_nextDocExpiration(Db).Should().Be((ulong)expire, "because that is the closest expiration date");

                WriteLine("---- Scan expired docs (#2)");
                e            = (C4ExpiryEnumerator *)LiteCoreBridge.Check(err => Native.c4db_enumerateExpired(Db, err));
                expiredCount = 0;
                while (Native.c4exp_next(e, null))
                {
                    var existingDocID = Native.c4exp_getDocID(e);
                    existingDocID.Should().NotBe(docID3, "because the last document is not scheduled for expiration");
                    expiredCount++;
                }

                WriteLine("---- Purge expired docs");
                LiteCoreBridge.Check(err => Native.c4exp_purgeExpired(e, err));
                Native.c4exp_free(e);
                expiredCount.Should().Be(2, "because 2 documents were scheduled for expiration");

                WriteLine("---- Scan expired docs (#3)");
                e            = (C4ExpiryEnumerator *)LiteCoreBridge.Check(err => Native.c4db_enumerateExpired(Db, err));
                expiredCount = 0;
                while (Native.c4exp_next(e, null))
                {
                    expiredCount++;
                }

                Write("---- Purge expired docs (again");
                LiteCoreBridge.Check(err => Native.c4exp_purgeExpired(e, err));
                Native.c4exp_free(e);
                expiredCount.Should().Be(0, "because no more documents were scheduled for expiration");
            });
        }
Exemplo n.º 9
0
        public void TestFullTextMultipleProperties()
        {
            RunTestVariants(() =>
            {
                LiteCoreBridge.Check(err => Native.c4db_createIndex(Db, "byAddress", "[[\".contact.address.street\"],[\".contact.address.city\"],[\".contact.address.state\"]]",
                                                                    C4IndexType.FullTextIndex, null, err));
                Compile(Json5("['MATCH', 'byAddress', 'Santa']"));
                var expected = new[]
                {
                    new C4FullTextMatch(15, 1, 0, 0, 5),
                    new C4FullTextMatch(44, 0, 0, 3, 5),
                    new C4FullTextMatch(68, 0, 0, 3, 5),
                    new C4FullTextMatch(72, 1, 0, 0, 5)
                };

                int index = 0;
                foreach (var result in RunFTS())
                {
                    foreach (var match in result)
                    {
                        match.Should().Be(expected[index++]);
                    }
                }

                Compile(Json5("['MATCH', 'byAddress', 'contact.address.street:Santa']"));
                expected = new[]
                {
                    new C4FullTextMatch(44, 0, 0, 3, 5),
                    new C4FullTextMatch(68, 0, 0, 3, 5)
                };

                index = 0;
                foreach (var result in RunFTS())
                {
                    foreach (var match in result)
                    {
                        match.Should().Be(expected[index++]);
                    }
                }

                Compile(Json5("['MATCH', 'byAddress', 'contact.address.street:Santa Saint']"));
                expected = new[]
                {
                    new C4FullTextMatch(68, 0, 0, 3, 5),
                    new C4FullTextMatch(68, 1, 1, 0, 5)
                };

                index = 0;
                foreach (var result in RunFTS())
                {
                    foreach (var match in result)
                    {
                        match.Should().Be(expected[index++]);
                    }
                }

                Compile(Json5("['MATCH', 'byAddress', 'contact.address.street:Santa OR Saint']"));
                expected = new[]
                {
                    new C4FullTextMatch(20, 1, 1, 0, 5),
                    new C4FullTextMatch(44, 0, 0, 3, 5),
                    new C4FullTextMatch(68, 0, 0, 3, 5),
                    new C4FullTextMatch(68, 1, 1, 0, 5),
                    new C4FullTextMatch(77, 1, 1, 0, 5)
                };

                index = 0;
                foreach (var result in RunFTS())
                {
                    foreach (var match in result)
                    {
                        match.Should().Be(expected[index++]);
                    }
                }
            });
        }
Exemplo n.º 10
0
        public void TestAllDocs()
        {
            RunTestVariants(() => {
                SetupAllDocs();

                Native.c4db_getDocumentCount(Db).Should().Be(99UL, "because there are 99 non-deleted documents");

                // No start or end ID:
                var options    = C4EnumeratorOptions.Default;
                options.flags &= ~C4EnumeratorFlags.IncludeBodies;
                var e          = (C4DocEnumerator *)LiteCoreBridge.Check(err => {
                    var localOpts = options;
                    return(Native.c4db_enumerateAllDocs(Db, null, null, &localOpts, err));
                });

                int i = 1;
                C4Error error;
                while (Native.c4enum_next(e, &error))
                {
                    var doc   = (C4Document *)LiteCoreBridge.Check(err => Native.c4enum_getDocument(e, err));
                    var docID = $"doc-{i:D3}";
                    doc->docID.CreateString().Should().Be(docID, "because the doc should have the correct doc ID");
                    doc->revID.Equals(RevID).Should().BeTrue("because the doc should have the current revID");
                    doc->selectedRev.revID.Equals(RevID).Should().BeTrue("because the selected rev should have the correct rev ID");
                    doc->selectedRev.sequence.Should().Be((ulong)i, "because the sequences should come in order");
                    doc->selectedRev.body.Equals(C4Slice.Null).Should().BeTrue("because the body is not loaded yet");
                    LiteCoreBridge.Check(err => Native.c4doc_loadRevisionBody(doc, err));
                    doc->selectedRev.body.Equals(Body).Should().BeTrue("because the loaded body should be correct");

                    C4DocumentInfo info;
                    Native.c4enum_getDocumentInfo(e, &info).Should().BeTrue("because otherwise the doc info load failed");
                    info.docID.CreateString().Should().Be(docID, "because the doc info should have the correct doc ID");
                    info.revID.Equals(RevID).Should().BeTrue("because the doc info should have the correct rev ID");

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

                Native.c4enum_free(e);
                i.Should().Be(100);

                // Start and end ID:
                e = (C4DocEnumerator *)LiteCoreBridge.Check(err => Native.c4db_enumerateAllDocs(Db,
                                                                                                "doc-007", "doc-090", null, err));
                i = 7;
                while (Native.c4enum_next(e, &error))
                {
                    error.code.Should().Be(0, "because otherwise an enumeration error occurred");
                    var doc   = (C4Document *)LiteCoreBridge.Check(err => Native.c4enum_getDocument(e, err));
                    var docID = $"doc-{i:D3}";
                    doc->docID.CreateString().Should().Be(docID, "because the doc should have the correct doc ID");
                    Native.c4doc_free(doc);
                    i++;
                }

                Native.c4enum_free(e);
                i.Should().Be(91, "because that is how many documents fall in the given range");

                // Some docs, by ID:
                options        = C4EnumeratorOptions.Default;
                options.flags |= C4EnumeratorFlags.IncludeDeleted;
                var docIDs     = new[] { "doc-042", "doc-007", "bogus", "doc-001" };
                e              = (C4DocEnumerator *)LiteCoreBridge.Check(err => {
                    var localOpts = options;
                    return(Native.c4db_enumerateSomeDocs(Db, docIDs, &localOpts, err));
                });

                i = 0;
                while (Native.c4enum_next(e, &error))
                {
                    error.code.Should().Be(0, "because otherwise an enumeration error occurred");
                    var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4enum_getDocument(e, err));
                    doc->docID.CreateString().Should().Be(docIDs[i], "because the doc should have the correct sorted doc ID");
                    if (doc->sequence != 0)
                    {
                        i.Should().NotBe(2, "because no document exists with the 'bogus' key");
                    }

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

                Native.c4enum_free(e);
                i.Should().Be(4, "because four document IDs were specified");
            });
        }
Exemplo n.º 11
0
        public void TestUpdate()
        {
            RunTestVariants(() =>
            {
                WriteLine("Begin test");
                C4Document *doc = null;
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin create");
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_create(Db, DocID, 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);
            });
        }
Exemplo n.º 12
0
        public void TestPut()
        {
            RunTestVariants(() => {
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    // Creating doc given ID:
                    var rq = new C4DocPutRequest {
                        docID = DocID,
                        body  = Body,
                        save  = true
                    };

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

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

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

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

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

                    commonAncestorIndex.Should().Be(1UL, "because the common ancestor is at sequence 1");
                    doc->flags.Should().Be(C4DocumentFlags.DocExists | C4DocumentFlags.DocConflicted, "because the document exists");
                    doc->selectedRev.revID.Equals(conflictRevID).Should().BeTrue("because the selected rev should have the correct rev ID");
                    doc->revID.Equals(expectedRev2ID).Should().BeTrue("because the conflicting rev should never be the default");
                    Native.c4doc_free(doc);
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }
            });
        }
Exemplo n.º 13
0
        public void TestGetForPut()
        {
            RunTestVariants(() => {
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    // Creating doc given ID:
                    var doc = (C4Document *)LiteCoreBridge.Check(err => NativeRawPrivate.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 => NativeRawPrivate.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 = NativeRawPrivate.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)C4ErrorCode.NotFound, "because the correct error should be returned");

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

                    // Adding new rev of existing doc:
                    CreateRev(DocID.CreateString(), RevID, Body);
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRawPrivate.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.DocExists, "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 = NativeRawPrivate.c4doc_getForPut(Db, DocID, Rev2ID, false, false, &error);
                    ((long)doc).Should().Be(0, "because the document does not exist");
                    error.code.Should().Be((int)C4ErrorCode.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 = NativeRawPrivate.c4doc_getForPut(Db, DocID, RevID, false, false, &error);
                    ((long)doc).Should().Be(0, "because the document does not exist");
                    error.code.Should().Be((int)C4ErrorCode.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 => NativeRawPrivate.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 => NativeRawPrivate.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 delete it:
                    CreateRev(DocID.CreateString(), Rev3ID, C4Slice.Null, C4RevisionFlags.Deleted);

                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                    LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));

                    // Re-creating the doc (no revID given):
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRawPrivate.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.DocExists | C4DocumentFlags.DocDeleted, "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));
                }
            });
        }
Exemplo n.º 14
0
        private Document Save([NotNull] Document document, bool deletion)
        {
            if (document.IsInvalidated)
            {
                throw new CouchbaseLiteException(StatusCode.NotAllowed, "Cannot save or delete a MutableDocument that has already been used to save or delete");
            }

            if (deletion && document.RevID == null)
            {
                throw new CouchbaseLiteException(StatusCode.NotAllowed, "Cannot delete a document that has not yet been saved");
            }

            var         docID = document.Id;
            var         doc = document;
            Document    baseDoc = null, otherDoc = null;
            C4Document *newDoc = null;
            Document    retVal = null;

            while (true)
            {
                var resolve = false;
                retVal = ThreadSafety.DoLocked(() =>
                {
                    VerifyDB(doc);
                    LiteCoreBridge.Check(err => Native.c4db_beginTransaction(_c4db, err));
                    try {
                        if (deletion)
                        {
                            // Check for no-op case if the document does not exist
                            var curDoc = (C4Document *)NativeHandler.Create().AllowError(new C4Error(C4ErrorCode.NotFound))
                                         .Execute(err => Native.c4doc_get(_c4db, docID, true, err));
                            if (curDoc == null)
                            {
                                (document as MutableDocument)?.MarkAsInvalidated();
                                return(null);
                            }

                            Native.c4doc_free(curDoc);
                        }

                        var newDocOther = newDoc;
                        Save(doc, &newDocOther, baseDoc?.c4Doc?.HasValue == true ? baseDoc.c4Doc.RawDoc : null, deletion);
                        if (newDocOther != null)
                        {
                            // Save succeeded, so commit
                            newDoc = newDocOther;
                            LiteCoreBridge.Check(err =>
                            {
                                var success = Native.c4db_endTransaction(_c4db, true, err);
                                if (!success)
                                {
                                    Native.c4doc_free(newDoc);
                                }

                                return(success);
                            });

                            (document as MutableDocument)?.MarkAsInvalidated();
                            baseDoc?.Dispose();
                            return(new Document(this, docID, new C4DocumentWrapper(newDoc)));
                        }

                        // There was a conflict
                        if (deletion && !doc.IsDeleted)
                        {
                            var deletedDoc = doc.ToMutable();
                            deletedDoc.MarkAsDeleted();
                            doc = deletedDoc;
                        }

                        if (doc.c4Doc != null)
                        {
                            baseDoc = new Document(this, docID, doc.c4Doc.Retain <C4DocumentWrapper>());
                        }

                        otherDoc = new Document(this, docID);
                        if (!otherDoc.Exists)
                        {
                            LiteCoreBridge.Check(err => Native.c4db_endTransaction(_c4db, false, err));
                            return(null);
                        }
                    } catch (Exception) {
                        baseDoc?.Dispose();
                        otherDoc?.Dispose();
                        LiteCoreBridge.Check(err => Native.c4db_endTransaction(_c4db, false, err));
                        throw;
                    }

                    resolve = true;
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(_c4db, false, err));
                    return(null);
                });

                if (!resolve)
                {
                    return(retVal);
                }

                // Resolve Conflict
                Document resolved = null;
                try {
                    var resolver = Config.ConflictResolver;
                    var conflict = new Conflict(doc, otherDoc, baseDoc);
                    resolved = resolver.Resolve(conflict);
                    if (resolved == null)
                    {
                        throw new LiteCoreException(new C4Error(C4ErrorCode.Conflict));
                    }
                } finally {
                    baseDoc?.Dispose();
                    if (!ReferenceEquals(resolved, otherDoc))
                    {
                        otherDoc?.Dispose();
                    }
                }

                retVal = ThreadSafety.DoLocked(() =>
                {
                    var current = new Document(this, docID);
                    if (resolved.RevID == current.RevID)
                    {
                        (document as MutableDocument)?.MarkAsInvalidated();
                        current.Dispose();
                        return(resolved); // Same as current
                    }

                    // For saving
                    doc      = resolved;
                    baseDoc  = current;
                    deletion = resolved.IsDeleted;
                    return(null);
                });

                if (retVal != null)
                {
                    return(retVal);
                }
            }
        }
Exemplo n.º 15
0
        public void TestConflict()
        {
            RunTestVariants(() =>
            {
                if (!IsRevTrees())
                {
                    return;
                }

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

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

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

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

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

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

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

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

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

                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    var doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, DocID.CreateString(), true, err));
                    LiteCoreBridge.Check(err => Native.c4doc_resolveConflict(doc, "3-aaaaaa", "4-dddd", Encoding.UTF8.GetBytes("{\"merged\":true}"), 0, err));
                    Native.c4doc_selectCurrentRevision(doc);
                    doc->selectedRev.revID.CreateString().Should().Be("4-333ee0677b5f1e1e5064b050d417a31d2455dc30");
                    doc->selectedRev.body.CreateString().Should().Be("{\"merged\":true}");
                    Native.c4doc_selectParentRevision(doc);
                    doc->selectedRev.revID.CreateString().Should().Be("3-aaaaaa");
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, false, err));
                }
            });
        }
Exemplo n.º 16
0
        internal void ResolveConflict([NotNull] string docID, [NotNull] IConflictResolver resolver)
        {
            Debug.Assert(docID != null);

            Document doc = null, otherDoc = null, baseDoc = null;
            var      inConflict = true;

            while (inConflict)
            {
                ThreadSafety.DoLocked(() =>
                {
                    LiteCoreBridge.Check(err => Native.c4db_beginTransaction(_c4db, err));
                    try {
                        doc = new Document(this, docID);
                        if (!doc.Exists)
                        {
                            doc.Dispose();
                            return;
                        }

                        otherDoc = new Document(this, docID);
                        if (!otherDoc.Exists)
                        {
                            doc.Dispose();
                            otherDoc.Dispose();
                            return;
                        }

                        otherDoc.SelectConflictingRevision();
                        baseDoc = new Document(this, docID);
                        if (!baseDoc.SelectCommonAncestor(doc, otherDoc) || baseDoc.ToDictionary() == null)
                        {
                            baseDoc.Dispose();
                            baseDoc = null;
                        }

                        LiteCoreBridge.Check(err => Native.c4db_endTransaction(_c4db, true, err));
                    } catch (Exception) {
                        doc?.Dispose();
                        otherDoc?.Dispose();
                        baseDoc?.Dispose();
                        LiteCoreBridge.Check(err => Native.c4db_endTransaction(_c4db, false, err));
                    }
                });

                var conflict = new Conflict(doc, otherDoc, baseDoc);
                var logID    = new SecureLogString(doc.Id, LogMessageSensitivity.PotentiallyInsecure);
                Log.To.Database.I(Tag, $"Resolving doc '{logID}' with {resolver.GetType().Name} (mine={doc.RevID}, theirs={otherDoc.RevID}, base={baseDoc?.RevID})");
                Document resolved = null;
                try {
                    resolved = resolver.Resolve(conflict);
                    if (resolved == null)
                    {
                        throw new LiteCoreException(new C4Error(C4ErrorCode.Conflict));
                    }

                    SaveResolvedDocument(resolved, conflict);
                    inConflict = false;
                } catch (LiteCoreException e) {
                    if (e.Error.domain == C4ErrorDomain.LiteCoreDomain && e.Error.code == (int)C4ErrorCode.Conflict)
                    {
                        continue;
                    }

                    throw;
                } finally {
                    resolved?.Dispose();
                    if (resolved != doc)
                    {
                        doc?.Dispose();
                    }

                    if (resolved != otherDoc)
                    {
                        otherDoc?.Dispose();
                    }

                    if (resolved != baseDoc)
                    {
                        baseDoc?.Dispose();
                    }
                }
            }
        }
Exemplo n.º 17
0
        public void TestCreateVersionedDoc()
        {
            RunTestVariants(() => {
                // Try reading doc with mustExist=true, which should fail:
                C4Error error;
                C4Document *doc = NativeRaw.c4doc_get(Db, DocID, true, &error);
                ((long)doc).Should().Be(0, "because the document does not exist");
                error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain);
                error.code.Should().Be((int)C4ErrorCode.NotFound);
                Native.c4doc_free(doc);

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

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

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

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

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

                CreateRev(doc1ID, RevID, Body);
                CreateRev(doc2ID, RevID, Body);

                var srcPath  = Native.c4db_getPath(Db);
                var destPath = Path.Combine(Path.GetTempPath(), $"nudb.cblite2{Path.DirectorySeparatorChar}");
                C4Error error;
                var config = *(Native.c4db_getConfig(Db));

                if (!Native.c4db_deleteAtPath(destPath, &error))
                {
                    error.code.Should().Be(0);
                }

                LiteCoreBridge.Check(err =>
                {
                    var localConfig = config;
                    return(Native.c4db_copy(srcPath, destPath, &localConfig, err));
                });
                var nudb = (C4Database *)LiteCoreBridge.Check(err =>
                {
                    var localConfig = config;
                    return(Native.c4db_open(destPath, &localConfig, err));
                });

                try {
                    Native.c4db_getDocumentCount(nudb).Should().Be(2L, "because the database was seeded");
                    LiteCoreBridge.Check(err => Native.c4db_delete(nudb, err));
                }
                finally {
                    Native.c4db_free(nudb);
                }

                nudb = (C4Database *)LiteCoreBridge.Check(err =>
                {
                    var localConfig = config;
                    return(Native.c4db_open(destPath, &localConfig, err));
                });

                try {
                    CreateRev(nudb, doc1ID, RevID, Body);
                    Native.c4db_getDocumentCount(nudb).Should().Be(1L, "because a document was inserted");
                }
                finally {
                    Native.c4db_free(nudb);
                }

                var originalDest = destPath;
                destPath         = Path.Combine(Path.GetTempPath(), "bogus", $"nudb.cblite2{Path.DirectorySeparatorChar}");
                Action a         = () => LiteCoreBridge.Check(err =>
                {
                    var localConfig = config;
                    return(Native.c4db_copy(srcPath, destPath, &localConfig, err));
                });
                a.ShouldThrow <CouchbaseLiteException>().Where(e =>
                                                               e.Error == CouchbaseLiteError.NotFound && e.Domain == CouchbaseLiteErrorType.CouchbaseLite);

                nudb = (C4Database *)LiteCoreBridge.Check(err =>
                {
                    var localConfig = config;
                    return(Native.c4db_open(originalDest, &localConfig, err));
                });

                try {
                    Native.c4db_getDocumentCount(nudb).Should().Be(1L, "because the original database should remain");
                }
                finally {
                    Native.c4db_free(nudb);
                }

                var originalSrc = srcPath;
                srcPath         = $"{srcPath}bogus{Path.DirectorySeparatorChar}";
                destPath        = originalDest;
                a.ShouldThrow <CouchbaseLiteException>().Where(e =>
                                                               e.Error == CouchbaseLiteError.NotFound && e.Domain == CouchbaseLiteErrorType.CouchbaseLite);

                nudb = (C4Database *)LiteCoreBridge.Check(err =>
                {
                    var localConfig = config;
                    return(Native.c4db_open(destPath, &localConfig, err));
                });

                try {
                    Native.c4db_getDocumentCount(nudb).Should().Be(1L, "because the original database should remain");
                }
                finally {
                    Native.c4db_free(nudb);
                }

                srcPath = originalSrc;
                a.ShouldThrow <CouchbasePosixException>().Where(e =>
                                                                e.Error == PosixBase.GetCode(nameof(PosixBase.EEXIST)) && e.Domain == CouchbaseLiteErrorType.POSIX);
                nudb = (C4Database *)LiteCoreBridge.Check(err =>
                {
                    var localConfig = config;
                    return(Native.c4db_open(destPath, &localConfig, err));
                });

                try {
                    Native.c4db_getDocumentCount(nudb).Should().Be(1L, "because the database copy failed");
                    LiteCoreBridge.Check(err => Native.c4db_delete(nudb, err));
                }
                finally {
                    Native.c4db_free(nudb);
                }
            });
        }
Exemplo n.º 19
0
        public void TestPurge()
        {
            RunTestVariants(() => {
                var body2 = JSON2Fleece("{\"ok\":\"go\"}");
                var body3 = JSON2Fleece("{\"ubu\":\"roi\"}");
                CreateRev(DocID.CreateString(), RevID, FleeceBody);
                CreateRev(DocID.CreateString(), Rev2ID, (FLSlice)body2);
                CreateRev(DocID.CreateString(), Rev3ID, (FLSlice)body3);

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

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

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

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

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

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

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

                    Native.c4db_getDocumentCount(Db).Should().Be(0UL);
                    Native.FLSliceResult_Release(body2);
                    Native.FLSliceResult_Release(body3);
                }
            });
        }
        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("/Couchbase/example-datasets-master/RandomUsers/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
                {
                    var st         = Stopwatch.StartNew();
                    var totalLikes = IndexLikesView();
                    Console.WriteLine($"Total of {totalLikes} likes");
                    st.PrintReport("Indexing Likes view", numDocs, "doc");
                    if (complete)
                    {
                        totalLikes.Should().Be(345986, "because otherwise the index missed data set objects");
                    }
                }
                {
                    var st      = Stopwatch.StartNew();
                    var context = new CountContext();
                    using (var reduce = new C4ManagedReduceFunction(CountAccumulate, CountReduce, context)) {
                        var numLikes = QueryGrouped(_likesView, reduce.Native, true);
                        st.PrintReport("Querying all likes", numLikes, "like");
                        if (complete)
                        {
                            numLikes.Should().Be(15, "because that is the number of likes in the data set");
                        }
                    }
                }
                {
                    var st    = Stopwatch.StartNew();
                    var total = IndexStatesView();
                    st.PrintReport("Indexing States view", numDocs, "doc");
                    if (complete)
                    {
                        total.Should().Be(300000, "because otherwise the index missed some dataset objects");
                    }
                }
                {
                    var options = C4QueryOptions.Default;
                    var key     = Native.c4key_new();
                    NativeRaw.c4key_addString(key, C4Slice.Constant("WA"));
                    options.startKey = options.endKey = key;
                    var st           = Stopwatch.StartNew();
                    var total        = RunQuery(_statesView, options);
                    Native.c4key_free(key);
                    st.PrintReport("Querying States view", total, "row");
                    if (complete)
                    {
                        total.Should().Be(5053, "because that is the number of states in the data set");
                    }
                }
                {
                    if (Storage == C4StorageEngine.SQLite && !IsRevTrees())
                    {
                        for (int pass = 0; pass < 2; ++pass)
                        {
                            var st = Stopwatch.StartNew();
                            var n  = QueryWhere("{\"contact.address.state\": \"WA\"}");
                            st.PrintReport("SQL query of state", n, "doc");
                            if (complete)
                            {
                                n.Should().Be(5053, "because that is the number of states in the data set");
                            }
                            if (pass == 0)
                            {
                                var st2 = Stopwatch.StartNew();
                                LiteCoreBridge.Check(err => Native.c4db_createIndex(Db, "contact.address.state", err));
                                st2.PrintReport("Creating SQL index of state", 1, "index");
                            }
                        }
                    }
                }
            });
        }
Exemplo n.º 21
0
        public void TestMaxRevTreeDepth()
        {
            RunTestVariants(() =>
            {
                if (IsRevTrees())
                {
                    Native.c4db_getMaxRevTreeDepth(Db).Should().Be(20, "because that is the default");
                    Native.c4db_setMaxRevTreeDepth(Db, 30U);
                    Native.c4db_getMaxRevTreeDepth(Db).Should().Be(30);
                    ReopenDB();
                    Native.c4db_getMaxRevTreeDepth(Db).Should().Be(30, "because the value should be persistent");
                }

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

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

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

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

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

                Native.c4doc_free(doc);
            });
        }
        private void IndexViews()
        {
            var nameKey    = Native.FLDictKey_Init("Name", true);
            var albumKey   = Native.FLDictKey_Init("Album", true);
            var artistKey  = Native.FLDictKey_Init("Artist", true);
            var timeKey    = Native.FLDictKey_Init("Total Time", true);
            var trackNoKey = Native.FLDictKey_Init("Track Number", true);
            var compKey    = Native.FLDictKey_Init("Compilation", true);

            var enc = Native.FLEncoder_New();
            var key = Native.c4key_new();

            C4Error error;

            if (_artistsView == null)
            {
                var config = Native.c4db_getConfig(Db);
                _artistsView = (C4View *)LiteCoreBridge.Check(err => Native.c4view_open(Db, null, "Artists", "1",
                                                                                        Native.c4db_getConfig(Db), err));
            }

            if (_albumsView == null)
            {
                _albumsView = (C4View *)LiteCoreBridge.Check(err => Native.c4view_open(Db, null, "Albums", "1",
                                                                                       Native.c4db_getConfig(Db), err));
            }

            var views   = new[] { _artistsView, _albumsView };
            var indexer = (C4Indexer *)LiteCoreBridge.Check(err => Native.c4indexer_begin(Db, views, err));
            var e       = (C4DocEnumerator *)LiteCoreBridge.Check(err => Native.c4indexer_enumerateDocuments(indexer, err));

            while (Native.c4enum_next(e, &error))
            {
                var doc  = Native.c4enum_getDocument(e, &error);
                var body = Native.FLValue_AsDict(NativeRaw.FLValue_FromTrustedData((FLSlice)doc->selectedRev.body));
                ((long)body).Should().NotBe(0, "because otherwise the data got corrupted somehow");

                FLSlice artist;
                if (Native.FLValue_AsBool(Native.FLDict_GetWithKey(body, &compKey)))
                {
                    artist = FLSlice.Constant("-Compilations-");
                }
                else
                {
                    artist = NativeRaw.FLValue_AsString(Native.FLDict_GetWithKey(body, &artistKey));
                }

                var name    = NativeRaw.FLValue_AsString(Native.FLDict_GetWithKey(body, &nameKey));
                var album   = Native.FLValue_AsString(Native.FLDict_GetWithKey(body, &albumKey));
                var trackNo = Native.FLValue_AsInt(Native.FLDict_GetWithKey(body, &trackNoKey));
                var time    = Native.FLDict_GetWithKey(body, &timeKey);

                // Generate Value:
                Native.FLEncoder_WriteValue(enc, time);
                FLError flError;
                var     fval = NativeRaw.FLEncoder_Finish(enc, &flError);
                Native.FLEncoder_Reset(enc);
                ((long)fval.buf).Should().NotBe(0, "because otherwise the encoding failed");
                var value = (C4Slice)fval;

                // Emit to artists view:
                uint nKeys = 0;
                if (!artist.Equals(FLSlice.Null) && !name.Equals(FLSlice.Null))
                {
                    nKeys = 1;
                    // Generate key:
                    Native.c4key_beginArray(key);
                    NativeRaw.c4key_addString(key, (C4Slice)artist);
                    if (album != null)
                    {
                        Native.c4key_addString(key, album);
                    }
                    else
                    {
                        Native.c4key_addNull(key);
                    }

                    Native.c4key_addNumber(key, trackNo);
                    NativeRaw.c4key_addString(key, (C4Slice)name);
                    Native.c4key_addNumber(key, 1.0);
                    Native.c4key_endArray(key);
                }

                Native.c4indexer_emit(indexer, doc, 0, nKeys, new[] { key }, new[] { value }, &error).Should()
                .BeTrue("because otherwise the emit to the artists view failed");
                Native.c4key_reset(key);

                // Emit to albums view:
                nKeys = 0;
                if (album != null)
                {
                    nKeys = 1;
                    Native.c4key_beginArray(key);
                    Native.c4key_addString(key, album);
                    if (!artist.Equals(FLSlice.Null))
                    {
                        NativeRaw.c4key_addString(key, (C4Slice)artist);
                    }
                    else
                    {
                        Native.c4key_addNull(key);
                    }

                    Native.c4key_addNumber(key, trackNo);
                    if (name.buf == null)
                    {
                        name = FLSlice.Constant("");
                    }

                    NativeRaw.c4key_addString(key, (C4Slice)name);
                    Native.c4key_addNumber(key, 1.0);
                    Native.c4key_endArray(key);
                }

                Native.c4indexer_emit(indexer, doc, 1, nKeys, new[] { key }, new[] { value }, &error).Should()
                .BeTrue("because otherwise the emit to the artists view failed");
                Native.c4key_reset(key);

                Native.FLSliceResult_Free(fval);
                Native.c4doc_free(doc);
            }

            Native.c4enum_free(e);
            error.Code.Should().Be(0, "because otherwise an error occurred");
            Native.c4indexer_end(indexer, true, &error).Should().BeTrue("because otherwise the indexer failed to end");
            Native.FLEncoder_Free(enc);
            Native.c4key_free(key);
        }
Exemplo n.º 23
0
        public void TestPut()
        {
            RunTestVariants(() => {
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    // Creating doc given ID:
                    var rq = new C4DocPutRequest {
                        docID = DocID,
                        body  = FleeceBody,
                        save  = true
                    };

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

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

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

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

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

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

                var body        = JSON2Fleece("{'foo':1, 'bar':false}");
                var updatedBody = JSON2Fleece("{'foo':1, 'bar':false, 'status': 'updated!'}");

                // TODO: Observer

                C4Error error;
                var doc = Native.c4db_getDoc(Db, "nonexistent", true, C4DocContentLevel.DocGetAll, &error);
                ((IntPtr)doc).Should().Be(IntPtr.Zero, "because it does not exist");
                error.domain.Should().Be(C4ErrorDomain.LiteCoreDomain);
                error.code.Should().Be((int)C4ErrorCode.NotFound);

                // KeepBody => Revision's body should not be discarded when non-leaf
                doc = PutDoc(null, null, (FLSlice)body, C4RevisionFlags.KeepBody);
                doc->docID.size.Should().BeGreaterOrEqualTo(10, "because otherwise no docID was created");

                var docID  = doc->docID.CreateString();
                var revID1 = doc->revID.CreateString();
                revID1.Should().StartWith("1-", "because otherwise the generation is invalid");
                Native.c4doc_release(doc);

                doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4db_getDoc(Db, docID, true, C4DocContentLevel.DocGetAll, err));
                doc->docID.CreateString().Should().Be(docID);
                doc->selectedRev.revID.CreateString().Should().Be(revID1);
                Native.FLSlice_Equal(NativeRaw.c4doc_getRevisionBody(doc), (FLSlice)body).Should().BeTrue();
                Native.c4doc_release(doc);

                doc = PutDoc(docID, revID1, (FLSlice)updatedBody, C4RevisionFlags.KeepBody);
                doc->docID.CreateString().Should().Be(docID);
                Native.FLSlice_Equal(NativeRaw.c4doc_getRevisionBody(doc), (FLSlice)updatedBody).Should().BeTrue();
                var revID2 = doc->revID.CreateString();
                revID2.Should().StartWith("2-", "because otherwise the generation is invalid");
                Native.c4doc_release(doc);
                Native.FLSliceResult_Release(body);

                error = new C4Error(C4ErrorCode.Conflict);
                PutDocMustFail(docID, revID1, (FLSlice)updatedBody, C4RevisionFlags.KeepBody, error);

                var e = (C4DocEnumerator *)LiteCoreBridge.Check(err =>
                {
                    var options = C4EnumeratorOptions.Default;
                    return(Native.c4db_enumerateChanges(Db, 0, &options, err));
                });

                var seq = 2UL;
                while (null != (doc = c4enum_nextDocument(e, &error)))
                {
                    doc->selectedRev.sequence.Should().Be(seq);
                    doc->selectedRev.revID.CreateString().Should().Be(revID2);
                    doc->docID.CreateString().Should().Be(docID);
                    Native.c4doc_release(doc);
                    seq++;
                }

                seq.Should().Be(3UL);
                Native.c4enum_free(e);
                Native.FLSliceResult_Release(updatedBody);

                // NOTE: Filter is out of LiteCore scope

                error = new C4Error(C4ErrorCode.InvalidParameter);
                PutDocMustFail(docID, null, FLSlice.Null, C4RevisionFlags.Deleted, error);

                doc = PutDoc(docID, revID2, FLSlice.Null, C4RevisionFlags.Deleted);
                doc->flags.Should().Be(C4DocumentFlags.DocExists | C4DocumentFlags.DocDeleted);
                doc->docID.CreateString().Should().Be(docID);
                var revID3 = doc->revID.CreateString();
                revID3.Should().StartWith("3-", "because otherwise the generation is invalid");
                Native.c4doc_release(doc);

                doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4db_getDoc(Db, docID, true, C4DocContentLevel.DocGetAll, err));
                doc->docID.CreateString().Should().Be(docID);
                doc->revID.CreateString().Should().Be(revID3);
                doc->flags.Should().Be(C4DocumentFlags.DocExists | C4DocumentFlags.DocDeleted);
                doc->selectedRev.revID.CreateString().Should().Be(revID3);
                NativeRaw.c4doc_getRevisionBody(doc).CreateString().Should().NotBeNull("because a valid revision should have a valid body");
                doc->selectedRev.flags.Should().Be(C4RevisionFlags.Leaf | C4RevisionFlags.Deleted);
                Native.c4doc_release(doc);

                PutDocMustFail("fake", null, FLSlice.Null, C4RevisionFlags.Deleted, error);

                e = (C4DocEnumerator *)LiteCoreBridge.Check(err =>
                {
                    var options = C4EnumeratorOptions.Default;
                    return(Native.c4db_enumerateChanges(Db, 0, &options, err));
                });

                seq = 3UL;
                while (null != (doc = c4enum_nextDocument(e, &error)))
                {
                    Native.c4doc_release(doc);
                    seq++;
                }

                seq.Should().Be(3UL, "because deleted documents were not included");
                Native.c4enum_free(e);

                e = (C4DocEnumerator *)LiteCoreBridge.Check(err =>
                {
                    var options    = C4EnumeratorOptions.Default;
                    options.flags |= C4EnumeratorFlags.IncludeDeleted;
                    return(Native.c4db_enumerateChanges(Db, 0, &options, err));
                });

                seq = 3UL;
                while (null != (doc = c4enum_nextDocument(e, &error)))
                {
                    doc->selectedRev.sequence.Should().Be(seq);
                    doc->selectedRev.revID.CreateString().Should().Be(revID3);
                    doc->docID.CreateString().Should().Be(docID);
                    Native.c4doc_release(doc);
                    seq++;
                }

                seq.Should().Be(4UL, "because deleted documents were included");
                Native.c4enum_free(e);

                doc        = (C4Document *)LiteCoreBridge.Check(err => Native.c4db_getDoc(Db, docID, true, C4DocContentLevel.DocGetAll, err));
                var latest = 3;
                do
                {
                    switch (latest)
                    {
                    case 3:
                        doc->selectedRev.revID.CreateString().Should().Be(revID3);
                        break;

                    case 2:
                        doc->selectedRev.revID.CreateString().Should().Be(revID2);
                        break;

                    case 1:
                        doc->selectedRev.revID.CreateString().Should().Be(revID1);
                        break;

                    default:
                        throw new InvalidOperationException("Invalid switch portion reached");
                    }

                    latest--;
                } while (Native.c4doc_selectParentRevision(doc));

                latest.Should().Be(0, "because otherwise the history is not valid");
                Native.c4doc_release(doc);

                doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4db_getDoc(Db, docID, true, C4DocContentLevel.DocGetAll, err));
                LiteCoreBridge.Check(err => Native.c4doc_selectRevision(doc, revID2, true, err));
                doc->selectedRev.revID.CreateString().Should().Be(revID2);
                Native.c4doc_release(doc);

                LiteCoreBridge.Check(err => Native.c4db_maintenance(Db, C4MaintenanceType.Compact, err));
                doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4db_getDoc(Db, docID, true, C4DocContentLevel.DocGetAll, err));
                LiteCoreBridge.Check(err => Native.c4doc_selectRevision(doc, revID2, true, err));
                doc->selectedRev.revID.CreateString().Should().Be(revID2);
                // doc->selectedRev.body.CreateString().Should().BeNull("because the database was compacted");
                Native.c4doc_release(doc);

                // Check history again after compaction
                doc    = (C4Document *)LiteCoreBridge.Check(err => Native.c4db_getDoc(Db, docID, true, C4DocContentLevel.DocGetAll, err));
                latest = 3;
                do
                {
                    switch (latest)
                    {
                    case 3:
                        doc->selectedRev.revID.CreateString().Should().Be(revID3);
                        break;

                    case 2:
                        doc->selectedRev.revID.CreateString().Should().Be(revID2);
                        break;

                    case 1:
                        doc->selectedRev.revID.CreateString().Should().Be(revID1);
                        break;

                    default:
                        throw new InvalidOperationException("Invalid switch portion reached");
                    }

                    latest--;
                } while (Native.c4doc_selectParentRevision(doc));

                latest.Should().Be(0, "because otherwise the history is not valid");
                Native.c4doc_release(doc);
            });
        }
Exemplo n.º 25
0
        public void TestUpdate()
        {
            RunTestVariants(() =>
            {
                WriteLine("Begin test");
                C4Document *doc = null;
                LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                try {
                    WriteLine("Begin create");
                    doc = (C4Document *)LiteCoreBridge.Check(err => NativeRaw.c4doc_create(Db, DocID, FleeceBody, 0, err));
                } finally {
                    LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                }

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

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

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

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

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

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

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

                Native.c4doc_free(doc);
                Native.c4doc_free(doc2);
            });
        }
 protected override void TeardownVariant(int options)
 {
     LiteCoreBridge.Check(err => Native.c4blob_deleteStore(_store, err));
 }
Exemplo n.º 27
0
        public void TestConflict()
        {
            RunTestVariants(() =>
            {
                if (!IsRevTrees())
                {
                    return;
                }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                return(numDocs);
            }
            finally {
                Native.FLSliceResult_Free(fleeceData);
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }
        }
Exemplo n.º 29
0
        private void UpdateIndex(C4Database *updateDB, C4View *view)
        {
            var oldLastSeqIndexed = Native.c4view_getLastSequenceIndexed(view);
            var lastSeq           = oldLastSeqIndexed;
            var ind = (C4Indexer *)LiteCoreBridge.Check(err => Native.c4indexer_begin(updateDB,
                                                                                      new[] { view }, err));
            var e = (C4DocEnumerator *)LiteCoreBridge.Check(err => Native.c4indexer_enumerateDocuments(ind, err));

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

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

            C4Document *doc;
            C4Error     error;

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

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

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

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

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


            var newLastSeqIndexed = Native.c4view_getLastSequenceIndexed(view);

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

            newLastSeqIndexed.Should().Be(lastSeq, "because the last sequence in the loop should be current");
            Native.c4view_getLastSequenceChangedAt(view).Should().Be(lastSeq);
        }