From aefde4addfdad0f110d10fdc62f8840406883cfd Mon Sep 17 00:00:00 2001 From: Pasin Suriyentrakorn Date: Tue, 4 Aug 2015 18:08:41 -0700 Subject: [PATCH] Fix process attachments when the parent rev is a _deleted rev When the intermediate parent rev is _deleted rev, the status returned when getting the parentAttachments will be kCBLStatusOK instead of kCBLStatusNotFound. So it should go to the same logic as when the stastus is kCBLStatusNotFound to get the attachment from the attachment revpos instead. #843 --- Source/CBLDatabase+Attachments.m | 9 +++--- Unit-Tests/DatabaseAttachment_Tests.m | 46 ++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/Source/CBLDatabase+Attachments.m b/Source/CBLDatabase+Attachments.m index 190f75318..215fc3ea7 100644 --- a/Source/CBLDatabase+Attachments.m +++ b/Source/CBLDatabase+Attachments.m @@ -340,7 +340,7 @@ - (BOOL) processAttachmentsForRevision: (CBL_MutableRevision*)rev parentAttachments = [self attachmentsForDocID: rev.docID revID: prevRevID status: &status]; if (!parentAttachments) { - if (status == kCBLStatusNotFound) { + if (status == kCBLStatusOK || status == kCBLStatusNotFound) { if ([_attachments hasBlobForKey: attachment.blobKey]) { // Parent revision's body isn't known (we are probably pulling a rev along // with its entire history) but it's OK, we have the attachment already @@ -352,11 +352,12 @@ - (BOOL) processAttachmentsForRevision: (CBL_MutableRevision*)rev revpos: attachment->revpos docID: rev.docID ancestry: ancestry]; - if (ancestorAttachment) + if (ancestorAttachment) { + *outStatus = kCBLStatusOK; return ancestorAttachment; + } else + status = kCBLStatusBadAttachment; } - if (status == kCBLStatusOK || status == kCBLStatusNotFound) - status = kCBLStatusBadAttachment; *outStatus = status; return nil; } diff --git a/Unit-Tests/DatabaseAttachment_Tests.m b/Unit-Tests/DatabaseAttachment_Tests.m index 6785628d2..78cb32ed0 100644 --- a/Unit-Tests/DatabaseAttachment_Tests.m +++ b/Unit-Tests/DatabaseAttachment_Tests.m @@ -526,7 +526,51 @@ - (void) test15_MissingIntermediateRevs { } -- (void) test16_FollowWithRevpos { +- (void) test16_IntermediateDeletedRevs { + // Put a revision that includes an _attachments dict: + NSData* attach1 = [@"This is the body of attach1" dataUsingEncoding: NSUTF8StringEncoding]; + NSString* base64 = [CBLBase64 encode: attach1]; + NSDictionary* attachmentDict = $dict({@"attach", $dict({@"content_type", @"text/plain"}, + {@"data", base64})}); + NSDictionary* props = $dict({@"_id", @"X"}, + {@"_attachments", attachmentDict}); + CBL_Revision* rev1; + CBLStatus status; + NSError* error; + rev1 = [db putRevision: [CBL_MutableRevision revisionWithProperties: props] + prevRevisionID: nil allowConflict: NO status: &status error: &error]; + AssertEq(status, kCBLStatusCreated); + AssertNil(error); + AssertEqual(rev1[@"_attachments"][@"attach"][@"revpos"], @1); + + // Insert a delete rev: + props = $dict({@"_id", rev1.docID}, + {@"_deleted", $true}); + CBL_Revision* rev2; + rev2 = [db putRevision: [CBL_MutableRevision revisionWithProperties: props] prevRevisionID: + rev1.revID allowConflict: NO status: &status error: &error] ; + AssertEq(status, kCBLStatusOK); + AssertNil(error); + Assert(rev2.deleted); + + // Insert a revision several generations advanced but which hasn't changed the attachment: + CBL_MutableRevision* rev3 = [rev1 mutableCopy]; + rev3[@"_rev"] = @"3-3333"; + rev3[@"foo"] = @"bar"; + [rev3 mutateAttachments: ^NSDictionary *(NSString *name, NSDictionary *att) { + NSMutableDictionary* nuAtt = [att mutableCopy]; + [nuAtt removeObjectForKey: @"data"]; + nuAtt[@"stub"] = @YES; + nuAtt[@"digest"] = @"md5-deadbeef"; // CouchDB adds MD5 digests! + return nuAtt; + }]; + NSArray* history = @[rev2.revID, rev1.revID]; + status = [db forceInsert: rev3 revisionHistory: history source: nil error: &error]; + AssertEq(status, 200); +} + + +- (void) test17_FollowWithRevpos { NSDictionary* attachInfo = $dict({@"content_type", @"text/plain"}, {@"digest", @"md5-DaUdFsLh8FKLbcBIDlU57g=="}, {@"follows", @YES},