Ejemplo n.º 1
0
        public void TestDBQueryWhatReturningObject()
        {
            RunTestVariants(() =>
            {
                var expectedFirst = new[] { "Cleveland", "Georgetta", "Margaretta" };
                var expectedLast  = new[] { "Bejcek", "Kolding", "Ogwynn" };
                CompileSelect(Json5(
                                  "{WHAT: ['.name'], WHERE: ['>=', ['length()', ['.name.first']], 9], ORDER_BY: [['.name.first']]}"));
                Native.c4query_columnCount(_query).Should().Be(1);

                var e = (C4QueryEnumerator *)LiteCoreBridge.Check(err =>
                {
                    var opts = C4QueryOptions.Default;
                    return(Native.c4query_run(_query, &opts, null, err));
                });

                var i = 0;
                C4Error error;
                while (Native.c4queryenum_next(e, &error))
                {
                    var col = Native.FLArrayIterator_GetValueAt(&e->columns, 0);
                    Native.FLValue_GetType(col).Should().Be(FLValueType.Dict);
                    var name = Native.FLValue_AsDict(col);
                    WriteLine(Native.FLValue_ToJSONX(col, false, false));
                    Native.FLValue_AsString(Native.FLDict_Get(name, Encoding.UTF8.GetBytes("first")))
                    .Should().Be(expectedFirst[i]);
                    Native.FLValue_AsString(Native.FLDict_Get(name, Encoding.UTF8.GetBytes("last")))
                    .Should().Be(expectedLast[i]);
                    ++i;
                }

                error.code.Should().Be(0, "because otherwise an error occurred during enumeration");
                i.Should().Be(3, "because that is the number of expected rows");
                Native.c4queryenum_release(e);
            });
        }
Ejemplo n.º 2
0
        protected IList <string> Run(ulong skip = 0, ulong limit = ulong.MaxValue, string bindings = null)
        {
            ((long)_query).Should().NotBe(0, "because otherwise what are we testing?");
            var docIDs  = new List <string>();
            var options = C4QueryOptions.Default;

            options.skip  = skip;
            options.limit = limit;
            var e = (C4QueryEnumerator *)LiteCoreBridge.Check(err => {
                var localOptions = options;
                return(Native.c4query_run(_query, &localOptions, bindings, err));
            });

            C4Error error;

            while (Native.c4queryenum_next(e, &error))
            {
                docIDs.Add(e->docID.CreateString());
            }

            error.code.Should().Be(0, "because otherwise an error occurred during enumeration");
            Native.c4queryenum_free(e);
            return(docIDs);
        }
        public void TestWriteManyBlobSizes()
        {
            var sizes = new[] { 0, 1, 15, 16, 17, 4095, 4096, 4097, 4096 + 15,
                                4096 + 16, 4096 + 17, 8191, 8192, 8193 };
            var chars = Encoding.UTF8.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXY");

            // The interesting sizes for encrypted blobs are right around the file block size (4096)
            // and the cipher block size (16).
            RunTestVariants(() => {
                foreach (var size in sizes)
                {
                    WriteLine($"Testing {size}-byte blob");
                    // Write the blob:
                    var stream = (C4WriteStream *)LiteCoreBridge.Check(err => Native.c4blob_openWriteStream(_store, err));
                    for (int i = 0; i < size; i++)
                    {
                        var c = i % chars.Length;
                        LiteCoreBridge.Check(err => Native.c4stream_write(stream, new[] { chars[c] }, err));
                    }

                    // Get the blob key, and install it:
                    var key = Native.c4stream_computeBlobKey(stream);
                    LiteCoreBridge.Check(err => Native.c4stream_install(stream, null, err));
                    Native.c4stream_closeWriter(stream);

                    // Read it back using the key:
                    C4Error error;
                    var contents = Native.c4blob_getContents(_store, key, &error);
                    contents.Should().HaveCount(size, "because that was the size that was stored");
                    for (int i = 0; i < size; i++)
                    {
                        contents[i].Should().Be(chars[i % chars.Length], "because the data should not change");
                    }
                }
            });
        }
Ejemplo n.º 4
0
        public void TestAllDocsIncludeDeleted()
        {
            RunTestVariants(() => {
                SetupAllDocs();
                var options    = C4EnumeratorOptions.Default;
                options.flags |= C4EnumeratorFlags.IncludeDeleted;
                var e          = (C4DocEnumerator *)LiteCoreBridge.Check(err => {
                    var localOpts = options;
                    return(Native.c4db_enumerateAllDocs(Db, "doc-004", "doc-007", &localOpts, err));
                });

                int i = 4;
                C4Error error;
                while (Native.c4enum_next(e, &error))
                {
                    var doc   = (C4Document *)LiteCoreBridge.Check(err => Native.c4enum_getDocument(e, err));
                    var docID = default(string);
                    if (i == 6)
                    {
                        docID = "doc-005DEL";
                    }
                    else
                    {
                        var docNum = i >= 6 ? i - 1 : i;
                        docID      = $"doc-{docNum: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(9, "because that is the last ID suffix in the given range");
            });
        }
Ejemplo n.º 5
0
        public void TestDBQueryAggregate()
        {
            RunTestVariants(() => {
                Compile(Json5("{WHAT: [['min()', ['.name.last']], ['max()', ['.name.last']]]}"));
                var e = (C4QueryEnumerator *)LiteCoreBridge.Check(err => {
                    var opts = C4QueryOptions.Default;
                    return(Native.c4query_run(_query, &opts, null, err));
                });

                var i = 0;
                C4Error error;
                while (Native.c4queryenum_next(e, &error))
                {
                    var customColumns = Native.c4queryenum_customColumns(e);
                    GetColumn(customColumns, 0).Should().Be("Aerni", "because otherwise the query returned incorrect results");
                    GetColumn(customColumns, 1).Should().Be("Zirk", "because otherwise the query returned incorrect results");
                    ++i;
                }

                error.code.Should().Be(0, "because otherwise an error occurred during enumeration");
                i.Should().Be(1, "because there is only one result for the query");
                Native.c4queryenum_free(e);
            });
        }
Ejemplo n.º 6
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);
            });
        }
Ejemplo n.º 7
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));
                }
            });
        }
Ejemplo n.º 8
0
        public void TestPurge()
        {
            RunTestVariants(() => {
                var body2 = C4Slice.Constant("{\"ok\":\"go\"}");
                var body3 = C4Slice.Constant("{\"ubu\":\"roi\"}");
                CreateRev(DocID.CreateString(), RevID, Body);
                CreateRev(DocID.CreateString(), Rev2ID, body2);
                CreateRev(DocID.CreateString(), Rev3ID, body3);

                var history = new[] { C4Slice.Constant("3-ababab"), Rev2ID };
                fixed(C4Slice * history_ = history)
                {
                    var rq = new C4DocPutRequest
                    {
                        existingRevision = true,
                        docID            = DocID,
                        history          = history_,
                        historyCount     = 2,
                        body             = 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, Body);
                    CreateRev(DocID.CreateString(), Rev2ID, body2);
                    CreateRev(DocID.CreateString(), Rev3ID, 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);
                }
            });
        }
Ejemplo n.º 9
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 = ios::Foundation.NSBundle.MainBundle.PathForResource(Path.GetFileNameWithoutExtension(path), Path.GetExtension(path));
            byte[] jsonData;
            using (var stream = File.Open(bundlePath, FileMode.Open, FileAccess.Read))
                using (var ms = new MemoryStream()) {
                    stream.CopyTo(ms);
                    jsonData = ms.ToArray();
                }
#else
            var jsonData = File.ReadAllBytes(path);
#endif

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

            ((long)fleeceData.buf).Should().NotBe(0, "because otherwise the conversion failed");
            var root = Native.FLValue_AsArray(NativeRaw.FLValue_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}{numDocs + 1:D7}" : $"doc{numDocs + 1:D7}";
                    var enc   = Native.c4db_getSharedFleeceEncoder(Db);
                    Native.FLEncoder_WriteValue(enc, item);
                    var body = NativeRaw.FLEncoder_Finish(enc, &error);

                    var rq = new C4DocPutRequest {
                        docID = 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));
            }
        }
Ejemplo 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");
                    info.bodySize.Should().BeGreaterOrEqualTo(11).And
                    .BeLessOrEqualTo(40, "because the body should have some data");

                    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);
                error.code.Should().Be(0, "because otherwise an error occurred");
                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);
                error.code.Should().Be(0, "because otherwise an error occurred");
                i.Should().Be(4, "because four document IDs were specified");
            });
        }
Ejemplo n.º 11
0
        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");
            });
        }
        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);
        }
        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");
                            }
                        }
                    }
                }
            });
        }
Ejemplo n.º 14
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();
                    }
                }
            }
        }
Ejemplo n.º 15
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);
                }
            }
        }
Ejemplo n.º 16
0
        public void TestRevTree()
        {
            RunTestVariants(() =>
            {
                if (!IsRevTrees())
                {
                    return;
                }

                // TODO: Observer

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

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

                var doc = GetDoc(docID);
                VerifyRev(doc, history, body);
                Native.c4doc_free(doc);

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

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

                // 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);
                VerifyRev(doc, history, body);
                Native.c4doc_free(doc);

                // TODO: Conflict check

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

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

                doc = GetDoc(otherDocID);
                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_free(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_free(doc);

                doc = GetDoc(docID);
                var conflictingRevs = GetRevisionHistory(doc, true, true);
                conflictingRevs.Count.Should().Be(2);
                conflictingRevs.Should().Equal(history[0], conflictHistory[0]);
                Native.c4doc_free(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_free(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], null, C4RevisionFlags.Deleted);
                Native.c4doc_free(doc);
                doc = GetDoc(docID);
                doc->revID.CreateString().Should().Be(history[0]);
                doc->selectedRev.revID.CreateString().Should().Be(history[0]);
                VerifyRev(doc, history, body);
                Native.c4doc_free(doc);

                doc = PutDoc(docID, history[0], null, C4RevisionFlags.Deleted);
                Native.c4doc_free(doc);

                // TODO: Need to implement following tests
            });
        }
Ejemplo n.º 17
0
 public void TestDatabaseBlobStore()
 {
     RunTestVariants(() => {
         LiteCoreBridge.Check(err => Native.c4db_getBlobStore(Db, err));
     });
 }
Ejemplo n.º 18
0
        private uint InsertDocs(FLArray *docs)
        {
            var typeKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Track Type"), true);
            var idKey      = NativeRaw.FLDictKey_Init(FLSlice.Constant("Persistent ID"), true);
            var nameKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Name"), true);
            var albumKey   = NativeRaw.FLDictKey_Init(FLSlice.Constant("Album"), true);
            var artistKey  = NativeRaw.FLDictKey_Init(FLSlice.Constant("Artist"), true);
            var timeKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Total Time"), true);
            var genreKey   = NativeRaw.FLDictKey_Init(FLSlice.Constant("Genre"), true);
            var yearKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Year"), true);
            var trackNoKey = NativeRaw.FLDictKey_Init(FLSlice.Constant("Track Number"), true);
            var compKey    = NativeRaw.FLDictKey_Init(FLSlice.Constant("Compilation"), true);

            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            try {
                var             enc = Native.FLEncoder_New();
                FLArrayIterator iter;
                Native.FLArrayIterator_Begin(docs, &iter);
                uint numDocs = 0;
                while (Native.FLArrayIterator_Next(&iter))
                {
                    // Check that track is correct type:
                    var track     = Native.FLValue_AsDict(Native.FLArrayIterator_GetValue(&iter));
                    var trackType = NativeRaw.FLValue_AsString(Native.FLDict_GetWithKey(track, &typeKey));
                    if (!trackType.Equals(FLSlice.Constant("File")) && !trackType.Equals(FLSlice.Constant("Remote")))
                    {
                        continue;
                    }

                    var trackID = NativeRaw.FLValue_AsString(Native.FLDict_GetWithKey(track, &idKey));
                    ((long)trackID.buf).Should().NotBe(0, "because otherwise the data was not read correctly");

                    // Encode doc body:
                    Native.FLEncoder_BeginDict(enc, 0);
                    CopyValue(track, &nameKey, enc).Should().BeTrue("because otherwise the copy failed");
                    CopyValue(track, &albumKey, enc);
                    CopyValue(track, &artistKey, enc);
                    CopyValue(track, &timeKey, enc);
                    CopyValue(track, &genreKey, enc);
                    CopyValue(track, &yearKey, enc);
                    CopyValue(track, &trackNoKey, enc);
                    CopyValue(track, &compKey, enc);
                    Native.FLEncoder_EndDict(enc);
                    FLError err;
                    var     body = NativeRaw.FLEncoder_Finish(enc, &err);
                    body.Should().NotBeNull("because otherwise the encoding process failed");
                    Native.FLEncoder_Reset(enc);

                    // Save Document:
                    var rq = new C4DocPutRequest();
                    rq.docID = (C4Slice)trackID;
                    rq.body  = body;
                    rq.save  = true;
                    var doc = (C4Document *)LiteCoreBridge.Check(c4err => {
                        var localRq = rq;
                        return(Native.c4doc_put(Db, &localRq, null, c4err));
                    });

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

                Native.FLEncoder_Free(enc);
                return(numDocs);
            } finally {
                LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
            }
        }
Ejemplo n.º 19
0
        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));

                var configCopy = config;
                if (!Native.c4db_deleteAtPath(destPath, &configCopy, &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 <LiteCoreException>().Which.Error.Should().Be(new C4Error(C4ErrorCode.NotFound));

                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 <LiteCoreException>().Which.Error.Should().Be(new C4Error(C4ErrorCode.NotFound));

                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 <LiteCoreException>().Which.Error.Should().Be(new C4Error(C4ErrorDomain.POSIXDomain, (int)PosixStatus.EXIST));
                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);
                }
            });
        }
Ejemplo n.º 20
0
        public void TestQueryObserver()
        {
            RunTestVariants(() =>
            {
                var handle = GCHandle.Alloc(this);
                try {
                    Compile(Json5("['=', ['.', 'contact', 'address', 'state'], 'CA']"));
                    C4Error error;

                    /** Creates a new query observer, with a callback that will be invoked when the query
                     *  results change, with an enumerator containing the new results.
                     *  \note The callback isn't invoked immediately after a change, and won't be invoked after
                     *  every change, to avoid performance problems. Instead, there's a brief delay so multiple
                     *  changes can be coalesced.
                     *  \note The new observer needs to be enabled by calling \ref c4queryobs_setEnabled.
                     */
                    _queryObserver = Native.c4queryobs_create(_query, QueryCallback, GCHandle.ToIntPtr(handle).ToPointer());
                    /** Enables a query observer so its callback can be called, or disables it to stop callbacks. */
                    Native.c4queryobs_setEnabled(_queryObserver, true);

                    WriteLine("---- Waiting for query observer...");
                    Thread.Sleep(2000);

                    WriteLine("Checking query observer...");
                    _queryCallbackCalls.Should().Be(1, "because we should have received a callback");
                    var e = (C4QueryEnumerator *)LiteCoreBridge.Check(err =>
                    {
                        /** Returns the current query results, if any.
                         * When the observer is created, the results are initially NULL until the query finishes
                         * running in the background.
                         * Once the observer callback is called, the results are available.
                         * \note  You are responsible for releasing the returned reference.
                         * @param obs  The query observer.
                         * @param forget  If true, the observer will not hold onto the enumerator, and subsequent calls
                         * will return NULL until the next time the observer notifies you. This can help
                         * conserve memory, since the query result data will be freed as soon as you
                         * release the enumerator.
                         * @param error  If the last evaluation of the query failed, the error will be stored here.
                         * @return  The current query results, or NULL if the query hasn't run or has failed.
                         */
                        return(Native.c4queryobs_getEnumerator(_queryObserver, true, err));
                    });
                    //REQUIRE(e);
                    //CHECK(c4queryobs_getEnumerator(state.obs, true, &error) == nullptr);
                    //CHECK(error.code == 0);
                    Native.c4queryenum_getRowCount(e, &error).Should().Be(8);

                    AddPersonInState("after1", "AL");

                    WriteLine("---- Checking that query observer doesn't fire...");
                    Thread.Sleep(TimeSpan.FromSeconds(1));
                    _queryCallbackCalls.Should().Be(1);

                    WriteLine("---- Changing a doc in the query");
                    LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                    try {
                        AddPersonInState("after2", "CA");
                        // wait, to make sure the observer doesn't try to run the query before the commit
                        Thread.Sleep(TimeSpan.FromSeconds(1));
                        WriteLine("---- Commiting changes");
                    } finally {
                        LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                    }

                    WriteLine("---- Waiting for 2nd call of query observer...");
                    Thread.Sleep(2000);

                    WriteLine("---- Checking query observer again...");
                    _queryCallbackCalls.Should().Be(2, "because we should have received a callback");
                    var e2 = (C4QueryEnumerator *)LiteCoreBridge.Check(err =>
                    {
                        return(Native.c4queryobs_getEnumerator(_queryObserver, false, err));
                    });
                    //REQUIRE(e2);
                    //CHECK(e2 != e);
                    var e3 = (C4QueryEnumerator *)LiteCoreBridge.Check(err =>
                    {
                        return(Native.c4queryobs_getEnumerator(_queryObserver, false, err));
                    });
                    //CHECK(e3 == e2);
                    Native.c4queryenum_getRowCount(e2, &error).Should().Be(9);

                    // Testing with purged document:
                    WriteLine("---- Purging a document...");
                    LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
                    try {
                        Native.c4db_purgeDoc(Db, "after2", &error);
                        WriteLine("---- Commiting changes");
                    } finally {
                        LiteCoreBridge.Check(err => Native.c4db_endTransaction(Db, true, err));
                    }

                    WriteLine("---- Waiting for 3rd call of query observer...");
                    Thread.Sleep(2000);

                    WriteLine("---- Checking query observer again...");
                    _queryCallbackCalls.Should().Be(3, "because we should have received a callback");
                    e2 = (C4QueryEnumerator *)LiteCoreBridge.Check(err =>
                    {
                        return(Native.c4queryobs_getEnumerator(_queryObserver, true, err));
                    });
                    //REQUIRE(e2);
                    //CHECK(e2 != e);
                    Native.c4queryenum_getRowCount(e2, &error).Should().Be(8);
                } finally {
                    handle.Free();
                }
            });
        }
Ejemplo n.º 21
0
        // Read a file that contains a JSON document per line. Every line becomes a document.
        protected uint ImportJSONLines(string path, TimeSpan timeout, bool verbose)
        {
            if (verbose)
            {
                WriteLine($"Reading {path}...");
            }

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

            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(Db, err));
            try {
                ReadFileByLines(path, line => {
                    C4Error error;
                    var body = NativeRaw.c4db_encodeJSON(Db, (C4Slice)line, &error);
                    ((long)body.buf).Should().NotBe(0, "because otherwise the encode failed");

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

                    // Save document:
                    using (var docID_ = new C4String(docID)) {
                        var rq = new C4DocPutRequest {
                            docID = docID_.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.c4slice_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);
        }
Ejemplo n.º 22
0
        public void TestMultipleQueryObservers()
        {
            RunTestVariants(() =>
            {
                var handle = GCHandle.Alloc(this);
                try {
                    Compile(Json5("['=', ['.', 'contact', 'address', 'state'], 'CA']"));
                    C4Error error;

                    _queryObserver = Native.c4queryobs_create(_query, QueryCallback, GCHandle.ToIntPtr(handle).ToPointer());
                    //CHECK(_queryObserver);
                    Native.c4queryobs_setEnabled(_queryObserver, true);

                    _queryObserver2 = Native.c4queryobs_create(_query, QueryCallback, GCHandle.ToIntPtr(handle).ToPointer());
                    //CHECK(_queryObserver2);
                    Native.c4queryobs_setEnabled(_queryObserver2, true);

                    WriteLine("---- Waiting for query observers...");
                    Thread.Sleep(2000);

                    WriteLine("Checking query observers...");
                    _queryCallbackCalls.Should().Be(1, "because we should have received a callback");
                    var e1 = (C4QueryEnumerator *)LiteCoreBridge.Check(err =>
                    {
                        return(Native.c4queryobs_getEnumerator(_queryObserver, true, err));
                    });
                    //REQUIRE(e1);
                    //CHECK(error.code == 0);
                    Native.c4queryenum_getRowCount(e1, &error).Should().Be(8);
                    _queryCallbackCalls2.Should().Be(1, "because we should have received a callback");
                    var e2 = (C4QueryEnumerator *)LiteCoreBridge.Check(err =>
                    {
                        return(Native.c4queryobs_getEnumerator(_queryObserver2, true, err));
                    });
                    //REQUIRE(e2);
                    //CHECK(error.code == 0);
                    //CHECK(e2 != e1);
                    Native.c4queryenum_getRowCount(e2, &error).Should().Be(8);

                    _queryObserver3 = Native.c4queryobs_create(_query, QueryCallback, GCHandle.ToIntPtr(handle).ToPointer());
                    //CHECK(_queryObserver3);
                    Native.c4queryobs_setEnabled(_queryObserver3, true);

                    WriteLine("---- Waiting for a new query observer...");
                    Thread.Sleep(2000);

                    WriteLine("Checking a new query observer...");
                    _queryCallbackCalls3.Should().Be(1, "because we should have received a callback");
                    var e3 = (C4QueryEnumerator *)LiteCoreBridge.Check(err =>
                    {
                        return(Native.c4queryobs_getEnumerator(_queryObserver3, true, err));
                    });
                    //REQUIRE(e3);
                    //CHECK(error.code == 0);
                    //CHECK(e3 != e2);
                    Native.c4queryenum_getRowCount(e3, &error).Should().Be(8);

                    WriteLine("Iterating all query results...");
                    int count = 0;
                    while (Native.c4queryenum_next(e1, null) && Native.c4queryenum_next(e2, null) && Native.c4queryenum_next(e3, null))
                    {
                        ++count;
                        FLArrayIterator col1 = e1->columns;
                        FLArrayIterator col2 = e2->columns;
                        FLArrayIterator col3 = e3->columns;
                        var c = Native.FLArrayIterator_GetCount(&col1);
                        c.Should().Be(Native.FLArrayIterator_GetCount(&col2));
                        c.Should().Be(Native.FLArrayIterator_GetCount(&col3));
                        for (uint i = 0; i < c; ++i)
                        {
                            var v1 = Native.FLArrayIterator_GetValueAt(&col1, i);
                            var v2 = Native.FLArrayIterator_GetValueAt(&col2, i);
                            var v3 = Native.FLArrayIterator_GetValueAt(&col3, i);
                            Native.FLValue_IsEqual(v1, v2).Should().BeTrue();
                            Native.FLValue_IsEqual(v2, v3).Should().BeTrue();
                        }
                    }

                    count.Should().Be(8);
                } finally {
                    handle.Free();
                }
            });
        }
Ejemplo n.º 23
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             = Body,
                        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(Body).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(Body).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(Body).Should().BeTrue("because the doc should have the stored body");
                Native.c4doc_free(doc);
            });
        }
Ejemplo n.º 24
0
 protected override void TeardownVariant(int options)
 {
     LiteCoreBridge.Check(err => Native.c4blob_deleteStore(_store, err));
 }
Ejemplo n.º 25
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      = &doc->revID;
                        rq.historyCount = 1;
                        rq.body         = Body;
                        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);
            });
        }
Ejemplo n.º 26
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++]);
                    }
                }
            });
        }
Ejemplo n.º 27
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));
                }
            });
        }
Ejemplo n.º 28
0
        private C4Document *ForceInsert(C4Database *db, string docID, string[] history, string body, C4RevisionFlags flags, C4Error *error = null)
        {
            LiteCoreBridge.Check(err => Native.c4db_beginTransaction(db, err));
            var c4History = new C4String[history.Length];
            var success   = false;

            try {
                var i            = 0;
                var sliceHistory = new C4Slice[history.Length];
                foreach (var entry in history)
                {
                    var c4Str = new C4String(entry);
                    c4History[i]      = c4Str;
                    sliceHistory[i++] = c4Str.AsC4Slice();
                }

                using (var docID_ = new C4String(docID))
                    using (var body_ = new C4String(body))
                    {
                        fixed(C4Slice *sliceHistory_ = sliceHistory)
                        {
                            var rq = new C4DocPutRequest
                            {
                                docID            = docID_.AsC4Slice(),
                                existingRevision = true,
                                allowConflict    = true,
                                history          = sliceHistory_,
                                historyCount     = (ulong)history.Length,
                                body             = body_.AsC4Slice(),
                                revFlags         = flags,
                                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));
            }
        }
Ejemplo n.º 29
0
        public void TestConflict()
        {
            RunTestVariants(() =>
            {
                if (!IsRevTrees())
                {
                    return;
                }

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

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

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

                        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));
                }
            });
        }
Ejemplo n.º 30
0
        public void TestCRUD()
        {
            RunTestVariants(() =>
            {
                if (!IsRevTrees())
                {
                    return;
                }

                var body        = "{\"foo\":1, \"bar\":false}";
                var updatedBody = "{\"foo\":1, \"bar\":false, \"status\": \"updated!\"}";

                // TODO: Observer

                C4Error error;
                var doc = Native.c4doc_get(Db, "nonexistent", true, &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, 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_free(doc);

                doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, docID, true, err));
                doc->docID.CreateString().Should().Be(docID);
                doc->selectedRev.revID.CreateString().Should().Be(revID1);
                doc->selectedRev.body.CreateString().Should().Be(body);
                Native.c4doc_free(doc);

                doc = PutDoc(docID, revID1, updatedBody, C4RevisionFlags.KeepBody);
                doc->docID.CreateString().Should().Be(docID);
                doc->selectedRev.body.CreateString().Should().Be(updatedBody);
                var revID2 = doc->revID.CreateString();
                revID2.Should().StartWith("2-", "because otherwise the generation is invalid");
                Native.c4doc_free(doc);

                error = new C4Error(C4ErrorCode.Conflict);
                PutDocMustFail(docID, revID1, 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_free(doc);
                    seq++;
                }

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

                // NOTE: Filter is out of LiteCore scope

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

                doc = PutDoc(docID, revID2, 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_free(doc);

                doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, docID, true, 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);
                doc->selectedRev.body.CreateString().Should().NotBeNull("because a valid revision should have a valid body");
                doc->selectedRev.flags.Should().Be(C4RevisionFlags.Leaf | C4RevisionFlags.Deleted);
                Native.c4doc_free(doc);

                PutDocMustFail("fake", null, 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_free(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_free(doc);
                    seq++;
                }

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

                doc        = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, docID, true, 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_free(doc);

                doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, docID, true, err));
                LiteCoreBridge.Check(err => Native.c4doc_selectRevision(doc, revID2, true, err));
                doc->selectedRev.revID.CreateString().Should().Be(revID2);
                doc->selectedRev.body.CreateString().Should().Be(updatedBody);
                Native.c4doc_free(doc);

                LiteCoreBridge.Check(err => Native.c4db_compact(Db, err));
                doc = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, docID, true, 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_free(doc);

                // Check history again after compaction
                doc    = (C4Document *)LiteCoreBridge.Check(err => Native.c4doc_get(Db, docID, true, 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_free(doc);
            });
        }