Skip to content

Instantly share code, notes, and snippets.

@tleyden
Created January 21, 2015 20:38
Show Gist options
  • Save tleyden/4b0f84e3edc41c2d0f78 to your computer and use it in GitHub Desktop.
Save tleyden/4b0f84e3edc41c2d0f78 to your computer and use it in GitHub Desktop.
possible approach - move validation out of transaction
public void forceInsert(RevisionInternal rev, List<String> revHistory, URL source) throws CouchbaseLiteException {
// TODO: in the iOS version, it is passed an immutable RevisionInternal and then
// TODO: creates a mutable copy. We should do the same here.
// TODO: see github.com/couchbase/couchbase-lite-java-core/issues/206#issuecomment-44364624
RevisionInternal winningRev = null;
boolean inConflict = false;
String docId = rev.getDocId();
String revId = rev.getRevId();
if(!isValidDocumentId(docId) || (revId == null)) {
throw new CouchbaseLiteException(Status.BAD_REQUEST);
}
int historyCount = 0;
if (revHistory != null) {
historyCount = revHistory.size();
}
if(historyCount == 0) {
revHistory = new ArrayList<String>();
revHistory.add(revId);
historyCount = 1;
} else if(!revHistory.get(0).equals(rev.getRevId())) {
throw new CouchbaseLiteException(Status.BAD_REQUEST);
}
// First look up all locally-known revisions of this document:
long docNumericID = getOrInsertDocNumericID(docId);
RevisionList localRevs = getAllRevisionsOfDocumentID(docId, docNumericID, false);
if(localRevs == null) {
throw new CouchbaseLiteException(Status.INTERNAL_SERVER_ERROR);
}
// Validate against the latest common ancestor:
if(validations != null && validations.size() > 0) {
RevisionInternal oldRev = null;
for (int i = 1; i < historyCount; i++) {
oldRev = localRevs.revWithDocIdAndRevId(docId, revHistory.get(i));
if (oldRev != null) {
break;
}
}
String parentRevId = (historyCount > 1) ? revHistory.get(1) : null;
validateRevision(rev, oldRev, parentRevId);
}
boolean success = false;
beginTransaction();
try {
AtomicBoolean outIsDeleted = new AtomicBoolean(false);
AtomicBoolean outIsConflict = new AtomicBoolean(false);
boolean oldWinnerWasDeletion = false;
String oldWinningRevID = winningRevIDOfDoc(docNumericID, outIsDeleted, outIsConflict);
if (outIsDeleted.get()) {
oldWinnerWasDeletion = true;
}
if (outIsConflict.get()) {
inConflict = true;
}
// Walk through the remote history in chronological order, matching each revision ID to
// a local revision. When the list diverges, start creating blank local revisions to fill
// in the local history:
long sequence = 0;
long localParentSequence = 0;
String localParentRevID = null;
for(int i = revHistory.size() - 1; i >= 0; --i) {
revId = revHistory.get(i);
RevisionInternal localRev = localRevs.revWithDocIdAndRevId(docId, revId);
if(localRev != null) {
// This revision is known locally. Remember its sequence as the parent of the next one:
sequence = localRev.getSequence();
assert(sequence > 0);
localParentSequence = sequence;
localParentRevID = revId;
}
else {
// This revision isn't known, so add it:
RevisionInternal newRev;
byte[] data = null;
boolean current = false;
if(i == 0) {
// Hey, this is the leaf revision we're inserting:
newRev = rev;
if(!rev.isDeleted()) {
data = encodeDocumentJSON(rev);
if(data == null) {
throw new CouchbaseLiteException(Status.BAD_REQUEST);
}
}
current = true;
}
else {
// It's an intermediate parent, so insert a stub:
newRev = new RevisionInternal(docId, revId, false, this);
}
// Insert it:
sequence = insertRevision(newRev, docNumericID, sequence, current, (newRev.getAttachments().size() > 0), data);
if(sequence <= 0) {
throw new CouchbaseLiteException(Status.INTERNAL_SERVER_ERROR);
}
if(i == 0) {
// Write any changed attachments for the new revision. As the parent sequence use
// the latest local revision (this is to copy attachments from):
Map<String, AttachmentInternal> attachments = getAttachmentsFromRevision(rev);
if (attachments != null) {
processAttachmentsForRevision(attachments, rev, localParentSequence);
stubOutAttachmentsInRevision(attachments, rev);
}
}
}
}
// Mark the latest local rev as no longer current:
if(localParentSequence > 0 && localParentSequence != sequence) {
ContentValues args = new ContentValues();
args.put("current", 0);
String[] whereArgs = { Long.toString(localParentSequence) };
int numRowsChanged = 0;
try {
numRowsChanged = database.update("revs", args, "sequence=? AND current!=0", whereArgs);
if (numRowsChanged == 0) {
inConflict = true; // local parent wasn't a leaf, ergo we just created a branch
}
} catch (SQLException e) {
throw new CouchbaseLiteException(Status.INTERNAL_SERVER_ERROR);
}
}
winningRev = winner(docNumericID, oldWinningRevID, oldWinnerWasDeletion, rev);
success = true;
// Notify and return:
notifyChange(rev, winningRev, source, inConflict);
} catch(SQLException e) {
throw new CouchbaseLiteException(Status.INTERNAL_SERVER_ERROR);
} finally {
endTransaction(success);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment