예제 #1
0
        private void Open()
        {
            if (_c4db != null)
            {
                return;
            }

            Directory.CreateDirectory(Config.Directory);
            var path   = DatabasePath(Name, Config.Directory);
            var config = DBConfig;

            var encrypted = "";

            #if COUCHBASE_ENTERPRISE
            if (Config.EncryptionKey != null)
            {
                var key = Config.EncryptionKey;
                var i   = 0;
                config.encryptionKey.algorithm = C4EncryptionAlgorithm.AES256;
                foreach (var b in key.KeyData)
                {
                    config.encryptionKey.bytes[i++] = b;
                }

                encrypted = "encrypted ";
            }
            #endif

            Log.To.Database.I(Tag, $"Opening {encrypted}database at {path}");
            var localConfig1 = config;
            ThreadSafety.DoLocked(() =>
            {
                _c4db = (C4Database *)NativeHandler.Create()
                        .AllowError((int)C4ErrorCode.NotADatabaseFile, C4ErrorDomain.LiteCoreDomain).Execute(err =>
                {
                    var localConfig2 = localConfig1;
                    return(Native.c4db_open(path, &localConfig2, err));
                });

                if (_c4db == null)
                {
                    throw new CouchbaseLiteException(StatusCode.Unauthorized);
                }

                _obs = Native.c4dbobs_create(_c4db, _DbObserverCallback, this);
            });
        }
예제 #2
0
        private void Save([NotNull] Document doc, C4Document **outDoc, C4Document *baseDoc, bool deletion)
        {
            var revFlags = (C4RevisionFlags)0;

            if (deletion)
            {
                revFlags = C4RevisionFlags.Deleted;
            }

            byte[] body = null;
            if (!deletion && !doc.IsEmpty)
            {
                body = doc.Encode();
                var root = Native.FLValue_FromTrustedData(body);
                if (root == null)
                {
                    Log.To.Database.E(Tag, "Failed to encode document body properly.  Aborting save of document!");
                    return;
                }

                var rootDict = Native.FLValue_AsDict(root);
                if (rootDict == null)
                {
                    Log.To.Database.E(Tag, "Failed to encode document body properly.  Aborting save of document!");
                    return;
                }

                ThreadSafety.DoLocked(() =>
                {
                    if (Native.c4doc_dictContainsBlobs(rootDict, SharedStrings.SharedKeys))
                    {
                        revFlags |= C4RevisionFlags.HasAttachments;
                    }
                });
            }
            else if (doc.IsEmpty)
            {
                FLEncoder *encoder = SharedEncoder;
                Native.FLEncoder_BeginDict(encoder, 0);
                Native.FLEncoder_EndDict(encoder);
                body = Native.FLEncoder_Finish(encoder, null);
                Native.FLEncoder_Reset(encoder);
            }

            var rawDoc = baseDoc != null ? baseDoc :
                         doc.c4Doc?.HasValue == true ? doc.c4Doc.RawDoc : null;

            if (rawDoc != null)
            {
                doc.ThreadSafety.DoLocked(() =>
                {
                    ThreadSafety.DoLocked(() =>
                    {
                        *outDoc = (C4Document *)NativeHandler.Create()
                                  .AllowError((int)C4ErrorCode.Conflict, C4ErrorDomain.LiteCoreDomain).Execute(
                            err => Native.c4doc_update(rawDoc, body, revFlags, err));
                    });
                });
            }
            else
            {
                ThreadSafety.DoLocked(() =>
                {
                    *outDoc = (C4Document *)NativeHandler.Create()
                              .AllowError((int)C4ErrorCode.Conflict, C4ErrorDomain.LiteCoreDomain).Execute(
                        err => Native.c4doc_create(_c4db, doc.Id, body, revFlags, err));
                });
            }
        }
예제 #3
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);
                }
            }
        }
예제 #4
0
 public static void Check(C4TryLogicDelegate3 block)
 {
     NativeHandler.Create().Execute(block);
 }
예제 #5
0
 public static void *Check(C4TryLogicDelegate2 block)
 {
     return(NativeHandler.Create().Execute(block));
 }
예제 #6
0
 public static byte[] Check(C4TryLogicDelegate4 block)
 {
     return(NativeHandler.Create().Execute(block));
 }