public void ForceInsert(RevisionInternal inRev, IList <string> revHistory, StoreValidation validationBlock, Uri source) { if (_config.HasFlag(C4DatabaseFlags.ReadOnly)) { throw new CouchbaseLiteException("Attempting to write to a readonly database", StatusCode.Forbidden); } var json = Manager.GetObjectMapper().WriteValueAsString(inRev.GetProperties(), true); var change = default(DocumentChange); RunInTransaction(() => { // First get the CBForest doc: WithC4Document(inRev.GetDocId(), null, false, true, doc => { ForestDBBridge.Check(err => Native.c4doc_insertRevisionWithHistory(doc, json, inRev.IsDeleted(), inRev.GetAttachments() != null, revHistory.ToArray(), err)); // Save updated doc back to the database: var isWinner = SaveDocument(doc, revHistory[0], inRev.GetProperties()); inRev.SetSequence((long)doc->sequence); change = ChangeWithNewRevision(inRev, isWinner, doc, source); }); return(true); }); if (change != null && Delegate != null) { Delegate.DatabaseStorageChanged(change); } }
public void OnCompletion(object result, Exception e) { IDictionary <string, object> res = (IDictionary <string, object>)result; if (e != null) { this._enclosing.SetError(e); this._enclosing.RevisionFailed(); this._enclosing.completedChangesCount.AddAndGet(bulkRevs.Count); } else { // Process the resulting rows' documents. // We only add a document if it doesn't have attachments, and if its // revID matches the one we asked for. IList <IDictionary <string, object> > rows = (IList <IDictionary <string, object> >)res .Get("rows"); Log.V(Log.TagSync, "%s checking %d bulk-fetched remote revisions", this, rows.Count ); foreach (IDictionary <string, object> row in rows) { IDictionary <string, object> doc = (IDictionary <string, object>)row.Get("doc"); if (doc != null && doc.Get("_attachments") == null) { RevisionInternal rev = new RevisionInternal(doc, this._enclosing.db); int pos = remainingRevs.IndexOf(rev); if (pos > -1) { rev.SetSequence(remainingRevs[pos].GetSequence()); remainingRevs.Remove(pos); this._enclosing.QueueDownloadedRevision(rev); } } } } // Any leftover revisions that didn't get matched will be fetched individually: if (remainingRevs.Count > 0) { Log.V(Log.TagSync, "%s bulk-fetch didn't work for %d of %d revs; getting individually" , this, remainingRevs.Count, bulkRevs.Count); foreach (RevisionInternal rev in remainingRevs) { this._enclosing.QueueRemoteRevision(rev); } this._enclosing.PullRemoteRevisions(); } // Note that we've finished this task: Log.V(Log.TagSync, "%s | %s: pullBulkWithAllDocs() calling asyncTaskFinished()", this, Sharpen.Thread.CurrentThread()); this._enclosing.AsyncTaskFinished(1); --this._enclosing.httpConnectionCount; // Start another task if there are still revisions waiting to be pulled: this._enclosing.PullRemoteRevisions(); }
public RevisionInternal GetParentRevision(RevisionInternal rev) { var retVal = default(RevisionInternal); WithC4Document(rev.GetDocId(), rev.GetRevId(), false, false, doc => { if (!Native.c4doc_selectParentRevision(doc)) { return; } ForestDBBridge.Check(err => Native.c4doc_loadRevisionBody(doc, err)); retVal = new RevisionInternal((string)doc->docID, (string)doc->selectedRev.revID, doc->selectedRev.IsDeleted); retVal.SetSequence((long)doc->selectedRev.sequence); retVal.SetBody(new Body(doc->selectedRev.body)); }); return(retVal); }
public RevisionInternal PutRevision(string inDocId, string inPrevRevId, IDictionary <string, object> properties, bool deleting, bool allowConflict, StoreValidation validationBlock) { if (_config.HasFlag(C4DatabaseFlags.ReadOnly)) { throw new CouchbaseLiteException("Attempting to write to a readonly database", StatusCode.Forbidden); } var json = default(string); if (properties != null) { json = Manager.GetObjectMapper().WriteValueAsString(Database.StripDocumentJSON(properties), true); } else { json = "{}"; } if (inDocId == null) { inDocId = Misc.CreateGUID(); } var putRev = default(RevisionInternal); var change = default(DocumentChange); var success = RunInTransaction(() => { var docId = inDocId; var prevRevId = inPrevRevId; var transactionSuccess = false; WithC4Document(docId, null, false, true, doc => { if (prevRevId != null) { // Updating an existing revision; make sure it exists and is a leaf: ForestDBBridge.Check(err => Native.c4doc_selectRevision(doc, prevRevId, false, err)); if (!allowConflict && !doc->selectedRev.IsLeaf) { throw new CouchbaseLiteException(StatusCode.Conflict); } } else { // No parent revision given: if (deleting) { // Didn't specify a revision to delete: NotFound or a Conflict, depending throw new CouchbaseLiteException(doc->Exists ? StatusCode.Conflict : StatusCode.NotFound); } // If doc exists, current rev must be in a deleted state or there will be a conflict: if (Native.c4doc_selectCurrentRevision(doc)) { if (doc->selectedRev.IsDeleted) { // New rev will be child of the tombstone: prevRevId = (string)doc->revID; } else { throw new CouchbaseLiteException(StatusCode.Conflict); } } } // Compute the new revID. (Can't be done earlier because prevRevID may have changed.) var newRevID = Delegate != null ? Delegate.GenerateRevID(Encoding.UTF8.GetBytes(json), deleting, prevRevId) : null; if (newRevID == null) { throw new CouchbaseLiteException(StatusCode.BadId); } putRev = new RevisionInternal(docId, newRevID, deleting); if (properties != null) { properties["_id"] = docId; properties["_rev"] = newRevID; putRev.SetProperties(properties); } // Run any validation blocks: if (validationBlock != null) { var prevRev = default(RevisionInternal); if (prevRevId != null) { prevRev = new RevisionInternal(docId, prevRevId, doc->selectedRev.IsDeleted); } var status = validationBlock(putRev, prevRev, prevRevId); if (status.IsError) { throw new CouchbaseLiteException(String.Format("{0} failed validation", putRev), status.Code); } } // Add the revision to the database: ForestDBBridge.Check(err => Native.c4doc_insertRevision(doc, newRevID, json, deleting, putRev.GetAttachments() != null, allowConflict, err)); var isWinner = SaveDocument(doc, newRevID, properties); putRev.SetSequence((long)doc->sequence); change = ChangeWithNewRevision(putRev, isWinner, doc, null); transactionSuccess = true; }); return(transactionSuccess); }); if (!success) { return(null); } if (Delegate != null && change != null) { Delegate.DatabaseStorageChanged(change); } return(putRev); }
// Get as many revisions as possible in one _all_docs request. // This is compatible with CouchDB, but it only works for revs of generation 1 without attachments. internal void PullBulkWithAllDocs(IList <RevisionInternal> bulkRevs) { // http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API Log.V(Tag, "PullBulkWithAllDocs() calling AsyncTaskStarted()"); AsyncTaskStarted(); ++httpConnectionCount; var remainingRevs = new List <RevisionInternal>(bulkRevs); var keys = bulkRevs.Select(rev => rev.GetDocId()).ToArray(); var body = new Dictionary <string, object>(); body.Put("keys", keys); SendAsyncRequest(HttpMethod.Post, "/_all_docs?include_docs=true", body, (result, e) => { var res = result.AsDictionary <string, object>(); if (e != null) { SetLastError(e); RevisionFailed(); SafeAddToCompletedChangesCount(bulkRevs.Count); } else { // Process the resulting rows' documents. // We only add a document if it doesn't have attachments, and if its // revID matches the one we asked for. var rows = res.Get("rows").AsList <IDictionary <string, object> >(); Log.V(Tag, "Checking {0} bulk-fetched remote revisions", rows.Count); foreach (var row in rows) { var doc = row.Get("doc").AsDictionary <string, object>(); if (doc != null && doc.Get("_attachments") == null) { var rev = new RevisionInternal(doc); var pos = remainingRevs.IndexOf(rev); if (pos > -1) { rev.SetSequence(remainingRevs[pos].GetSequence()); remainingRevs.Remove(pos); QueueDownloadedRevision(rev); } } } } // Any leftover revisions that didn't get matched will be fetched individually: if (remainingRevs.Count > 0) { Log.V(Tag, "Bulk-fetch didn't work for {0} of {1} revs; getting individually", remainingRevs.Count, bulkRevs.Count); foreach (var rev in remainingRevs) { QueueRemoteRevision(rev); } PullRemoteRevisions(); } // Note that we've finished this task: Log.V(Tag, "PullBulkWithAllDocs() calling AsyncTaskFinished()"); AsyncTaskFinished(1); --httpConnectionCount; // Start another task if there are still revisions waiting to be pulled: PullRemoteRevisions(); }); }