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); }); }
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)); }); } }
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); } } }
public static void Check(C4TryLogicDelegate3 block) { NativeHandler.Create().Execute(block); }
public static void *Check(C4TryLogicDelegate2 block) { return(NativeHandler.Create().Execute(block)); }
public static byte[] Check(C4TryLogicDelegate4 block) { return(NativeHandler.Create().Execute(block)); }