public RevisionInternal PutLocalRevision(RevisionInternal revision, RevisionID prevRevId, bool obeyMVCC) { var docId = revision.DocID; if(!docId.StartsWith("_local/")) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadId, TAG, "Invalid document ID ({0}) in write operation, it must start with _local/", new SecureLogString(docId, LogMessageSensitivity.PotentiallyInsecure)); } if(revision.Deleted) { DeleteLocalRevision(docId, prevRevId, obeyMVCC); return revision; } var result = default(RevisionInternal); RunInTransaction(() => { var json = Manager.GetObjectMapper().WriteValueAsString(revision.GetProperties(), true); WithC4Raw(docId, "_local", doc => { var generation = prevRevId == null ? 0 : prevRevId.Generation; if(obeyMVCC) { var currentRevId = (doc != null ? doc->meta.AsRevID() : null); if(prevRevId != null) { if(prevRevId != currentRevId) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.Conflict, TAG, "Attempt to write new revision on {0} of {1} when a newer revision ({2}) exists", prevRevId, new SecureLogString(docId, LogMessageSensitivity.PotentiallyInsecure), currentRevId); } if(generation == 0) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.BadId, TAG, "Attempt to write new revision on invalid revision ID ({0}) for document {1}", prevRevId, new SecureLogString(docId, LogMessageSensitivity.PotentiallyInsecure)); } } else if(doc != null) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.Conflict, TAG, "Revision ID not specified, but document {0} already exists (current rev: {1})", new SecureLogString(docId, LogMessageSensitivity.PotentiallyInsecure), currentRevId); } } var newRevId = String.Format("{0}-local", ++generation).AsRevID(); ForestDBBridge.Check(err => Native.c4raw_put(Forest, "_local", docId, newRevId.ToString(), json, err)); result = revision.Copy(docId, newRevId); }); return true; }); return result; }
// Uploads the revision as JSON instead of multipart. private void UploadJsonRevision(RevisionInternal originalRev) { // Expand all attachments inline: var rev = originalRev.Copy(originalRev.DocID, originalRev.RevID); try { LocalDatabase.ExpandAttachments(rev, 0, false, false); } catch(Exception e) { LastError = e; RevisionFailed(); return; } var path = string.Format("/{0}?new_edits=false", Uri.EscapeUriString(rev.DocID)); _remoteSession.SendAsyncRequest(HttpMethod.Put, path, rev.GetProperties(), (result, e) => { if (e != null) { LastError = e; RevisionFailed(); } else { Log.To.Sync.V(TAG, "{0} sent {1} (JSON), response={2}", this, rev, new LogJsonString(result)); SafeIncrementCompletedChangesCount(); RemovePending (rev); } }); }
// This is used by the listener internal RevisionInternal RevisionByLoadingBody(RevisionInternal rev, Status outStatus) { // First check for no-op -- if we just need the default properties and already have them: if(rev.Sequence != 0) { var props = rev.GetProperties(); if(props != null && props.ContainsKey("_rev") && props.ContainsKey("_id")) { if(outStatus != null) { outStatus.Code = StatusCode.Ok; } return rev; } } RevisionInternal nuRev = rev.Copy(rev.DocID, rev.RevID); try { LoadRevisionBody(nuRev); } catch(CouchbaseLiteException e) { if(outStatus != null) { outStatus.Code = e.CBLStatus.Code; } nuRev = null; } return nuRev; }