Skip to content

Commit

Permalink
CBL-6239: Fixed c4doc_getRevisionBody in a 2.x db
Browse files Browse the repository at this point in the history
VectorRecord didn't properly manage the `body` column of a doc from a
2.x db that doesn't have the `extra` column. In such a db the body is
the encoded rev tree, not just the current rev. VectorRecord was
returning that entire slice from its currentRevisionData method.

I added a new member _bodyDocRange, a slice that's just the current
rev within the _bodyDoc. Normally it's the same as _bodyDoc.data(),
but in a 2.x doc it encompasses just the current rev body.

Fixes CBL-6239
  • Loading branch information
snej committed Sep 12, 2024
1 parent abe9759 commit 6c932f2
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 13 deletions.
15 changes: 10 additions & 5 deletions C/tests/c4DatabaseTest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1115,13 +1115,18 @@ static void testOpeningOlderDBFixture(const string& dbPath, C4DatabaseFlags with
for ( unsigned i = 1; i <= 100; i++ ) {
snprintf(docID, bufSize, "doc-%03u", i);
INFO("Checking docID " << docID);
auto defaultColl = c4db_getDefaultCollection(db, nullptr);
C4Document* doc = c4coll_getDoc(defaultColl, slice(docID), true, kDocGetCurrentRev, ERROR_INFO());
auto defaultColl = c4db_getDefaultCollection(db, nullptr);
c4::ref<C4Document> doc = c4coll_getDoc(defaultColl, slice(docID), true, kDocGetCurrentRev, ERROR_INFO());
REQUIRE(doc);
CHECK(((doc->flags & kDocDeleted) != 0) == (i > 50));
Dict body = c4doc_getProperties(doc);
CHECK(body["n"].asInt() == i);
c4doc_release(doc);
Dict root = c4doc_getProperties(doc);
CHECK(root["n"].asInt() == i);
// Test getting doc body from data [CBL-6239]:
slice body = c4doc_getRevisionBody(doc);
REQUIRE(body);
FLValue rootVal = FLValue_FromData(body, kFLUntrusted);
CHECK(rootVal);
CHECK(FLValue_GetType(rootVal) == kFLDict);
}

// Verify enumerating documents:
Expand Down
20 changes: 13 additions & 7 deletions LiteCore/RevTrees/VectorRecord.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,7 @@ namespace litecore {
static constexpr slice kRevIDKey = "@";
static constexpr slice kRevFlagsKey = "&";

Version Revision::version() const { return VersionVector::readCurrentVersionFromBinary(revID); }

VersionVector Revision::versionVector() const { return VersionVector::fromBinary(revID); }
#pragma mark - INITIALIZATION:

VectorRecord::VectorRecord(KeyStore& store, const Record& rec)
: _store(store)
Expand Down Expand Up @@ -130,6 +128,7 @@ namespace litecore {
} else {
if ( body ) {
_bodyDoc = newLinkedFleeceDoc(body, kFLTrusted);
_bodyDocRange = _bodyDoc.data();
_current.properties = _bodyDoc.asDict();
if ( !_current.properties )
error::_throw(error::CorruptRevisionData, "VectorRecord reading properties error");
Expand Down Expand Up @@ -178,10 +177,13 @@ namespace litecore {
if ( _docFlags & DocumentFlags::kSynced ) revTree.setLatestRevisionOnRemote(1, curRev);

if ( !extra ) {
// This is a v2.x document with body & rev-tree in `body`, and no `extra`:
// This is a v2.x document with body & rev-tree in `body`, and no `extra`.
// That means _bodyDoc's data is the entire rev-tree, not just the current rev data.
// This is why we have _bodyDocRange! Set it to the current rev itself:
Assert(!_bodyDoc);
_bodyDoc = newLinkedFleeceDoc(body, kFLTrustedDontParse);
FLValue bodyProps = FLValue_FromData(curRev->body(), kFLTrusted);
_bodyDocRange = curRev->body();
FLValue bodyProps = FLValue_FromData(_bodyDocRange, kFLTrusted);
_current.properties = Value(bodyProps).asDict();
if ( !_current.properties )
error::_throw(error::CorruptRevisionData, "VectorRecord reading 2.x properties error");
Expand Down Expand Up @@ -409,9 +411,13 @@ namespace litecore {

#pragma mark - CURRENT REVISION:

Version Revision::version() const { return VersionVector::readCurrentVersionFromBinary(revID); }

VersionVector Revision::versionVector() const { return VersionVector::fromBinary(revID); }

slice VectorRecord::currentRevisionData() const {
requireBody();
return _bodyDoc.data();
return _bodyDocRange;
}

void VectorRecord::setCurrentRevision(const Revision& rev) {
Expand All @@ -422,7 +428,7 @@ namespace litecore {

Dict VectorRecord::originalProperties() const {
requireBody();
return _bodyDoc.asDict();
return Value(FLValue_FromData(_bodyDocRange, kFLTrusted)).asDict();
}

MutableDict VectorRecord::mutableProperties() {
Expand Down
3 changes: 2 additions & 1 deletion LiteCore/RevTrees/VectorRecord.hh
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ namespace litecore {
alloc_slice _revID; // Current revision ID backing store
Revision _current; // Current revision
fleece::RetainedValue _currentProperties; // Retains local properties
fleece::Doc _bodyDoc; // If saved, a Doc of the Fleece body
fleece::Doc _bodyDoc; // If saved, a Doc of the `body` column
slice _bodyDocRange; // Fleece data within _bodyDoc
fleece::Doc _extraDoc; // Fleece Doc holding record `extra`
fleece::Array _revisions; // Top-level parsed body; stores revs
mutable fleece::MutableArray _mutatedRevisions; // Mutable version of `_revisions`
Expand Down

0 comments on commit 6c932f2

Please sign in to comment.