diff options
author | Ian Boros <ian.boros@mongodb.com> | 2020-09-03 14:14:48 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-09-16 18:31:07 +0000 |
commit | 01ed0776388cc8cff83a82e3671d313b00d5096d (patch) | |
tree | 523737fcd604917af6cd48a685c0746c3405c100 /src/mongo/db/update | |
parent | cc248c4286d45eee06e2d66cdd52cd7cbae5b593 (diff) | |
download | mongo-01ed0776388cc8cff83a82e3671d313b00d5096d.tar.gz |
SERVER-50300 Change modifier style updates to use the $v:2 oplog format
Diffstat (limited to 'src/mongo/db/update')
21 files changed, 926 insertions, 404 deletions
diff --git a/src/mongo/db/update/addtoset_node_test.cpp b/src/mongo/db/update/addtoset_node_test.cpp index c9b691126ee..d2b7f90be65 100644 --- a/src/mongo/db/update/addtoset_node_test.cpp +++ b/src/mongo/db/update/addtoset_node_test.cpp @@ -135,7 +135,9 @@ TEST_F(AddToSetNodeTest, ApplyNonEach) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [0, 1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [0, 1]}}"), fromjson("{$v: 2, diff: {u: {a: [0, 1]}}}")); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -153,7 +155,9 @@ TEST_F(AddToSetNodeTest, ApplyNonEachArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, [1]]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [0, [1]]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [0, [1]]}}"), + fromjson("{$v: 2, diff: {u: {a: [0, [1]]}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -171,7 +175,9 @@ TEST_F(AddToSetNodeTest, ApplyEach) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [0, 1, 2]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [0, 1, 2]}}"), + fromjson("{$v: 2, diff: {u: {a: [0, 1, 2]}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -189,7 +195,8 @@ TEST_F(AddToSetNodeTest, ApplyToEmptyArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1, 2]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1, 2]}}"), fromjson("{$v: 2, diff: {u: {a: [1, 2]}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -207,7 +214,9 @@ TEST_F(AddToSetNodeTest, ApplyDeduplicateElementsToAdd) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [0, 1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [0, 1]}}"), fromjson("{$v: 2, diff: {u: {a: [0, 1]}}}")); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -225,7 +234,9 @@ TEST_F(AddToSetNodeTest, ApplyDoNotAddExistingElements) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [0, 1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [0, 1]}}"), fromjson("{$v: 2, diff: {u: {a: [0, 1]}}}")); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -243,7 +254,9 @@ TEST_F(AddToSetNodeTest, ApplyDoNotDeduplicateExistingElements) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [0, 0, 1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [0, 0, 1]}}"), + fromjson("{$v: 2, diff: {u: {a: [0, 0, 1]}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -261,7 +274,8 @@ TEST_F(AddToSetNodeTest, ApplyNoElementsToAdd) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -279,7 +293,8 @@ TEST_F(AddToSetNodeTest, ApplyNoNonDuplicateElementsToAdd) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -297,7 +312,8 @@ TEST_F(AddToSetNodeTest, ApplyCreateArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [0, 1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [0, 1]}}"), fromjson("{$v: 2, diff: {i: {a: [0, 1]}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -315,7 +331,8 @@ TEST_F(AddToSetNodeTest, ApplyCreateEmptyArrayIsNotNoop) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: []}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: []}}"), fromjson("{$v: 2, diff: {i: {a: []}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -336,7 +353,9 @@ TEST_F(AddToSetNodeTest, ApplyDeduplicationOfElementsToAddRespectsCollation) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: ['abc', 'def']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: ['abc', 'def']}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: ['abc', 'def']}}"), + fromjson("{$v: 2, diff: {u: {a: ['abc', 'def']}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -357,7 +376,9 @@ TEST_F(AddToSetNodeTest, ApplyComparisonToExistingElementsRespectsCollation) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: ['ABC', 'def']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: ['ABC', 'def']}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: ['ABC', 'def']}}"), + fromjson("{$v: 2, diff: {u: {a: ['ABC', 'def']}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -379,7 +400,10 @@ TEST_F(AddToSetNodeTest, ApplyRespectsCollationFromSetCollator) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: ['abc', 'def']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: ['abc', 'def']}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: ['abc', 'def']}}"), + fromjson("{$v: 2, diff: {u: {a: ['abc', 'def']}}}")); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -414,15 +438,18 @@ TEST_F(AddToSetNodeTest, ApplyNestedArray) { AddToSetNode node; ASSERT_OK(node.init(update["$addToSet"]["a.1"], expCtx)); - mutablebson::Document doc(fromjson("{ _id : 1, a : [ 1, [ ] ] }")); + mutablebson::Document doc(fromjson("{ _id : 1, a : [1, []] }")); setPathTaken(makeRuntimeUpdatePathForTest("a.1")); addIndexedPath("a"); auto result = node.apply(getApplyParams(doc.root()["a"][1]), getUpdateNodeApplyParams()); ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); - ASSERT_EQUALS(fromjson("{ _id : 1, a : [ 1, [ 1 ] ] }"), doc); + ASSERT_EQUALS(fromjson("{ _id : 1, a : [1, [1]] }"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ $set : { 'a.1' : [ 1 ] } }"), getLogDoc()); + + assertOplogEntry(fromjson("{ $set : { 'a.1' : [1] } }"), + fromjson("{$v: 2, diff: {sa: {a: true, u1: [1]}}}")); + ASSERT_EQUALS(getModifiedPaths(), "{a.1}"); } @@ -439,7 +466,8 @@ TEST_F(AddToSetNodeTest, ApplyIndexesNotAffected) { ASSERT_FALSE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [0, 1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [0, 1]}}"), fromjson("{$v: 2, diff: {u: {a: [0, 1]}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } diff --git a/src/mongo/db/update/arithmetic_node_test.cpp b/src/mongo/db/update/arithmetic_node_test.cpp index 03941282e4a..3cb73d8f8e7 100644 --- a/src/mongo/db/update/arithmetic_node_test.cpp +++ b/src/mongo/db/update/arithmetic_node_test.cpp @@ -127,7 +127,8 @@ TEST_F(ArithmeticNodeTest, ApplyIncNoOp) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -145,7 +146,8 @@ TEST_F(ArithmeticNodeTest, ApplyMulNoOp) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -163,7 +165,8 @@ TEST_F(ArithmeticNodeTest, ApplyRoundingNoOp) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 6.022e23}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -181,7 +184,8 @@ TEST_F(ArithmeticNodeTest, ApplyEmptyPathToCreate) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 11}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 11}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 11}}"), fromjson("{$v: 2, diff: {u: {a: 11}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -200,7 +204,9 @@ TEST_F(ArithmeticNodeTest, ApplyCreatePath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {d: 5, b: {c: 6}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.b.c': 6}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b.c': 6}}"), + fromjson("{$v: 2, diff: {sa: {i: {b: {c: 6}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.b.c}"); } @@ -236,7 +242,8 @@ TEST_F(ArithmeticNodeTest, ApplyCreatePathFromRoot) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{c: 5, a: {b: 6}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.b': 6}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': 6}}"), fromjson("{$v: 2, diff: {i: {a: {b: 6}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } @@ -255,7 +262,9 @@ TEST_F(ArithmeticNodeTest, ApplyPositional) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 7, 2]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.1': 7}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.1': 7}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u1: 7}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.1}"); } @@ -292,7 +301,8 @@ TEST_F(ArithmeticNodeTest, ApplyNonViablePathToCreateFromReplicationIsNoOp) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } @@ -378,7 +388,9 @@ TEST_F(ArithmeticNodeTest, TypePromotionFromIntToDecimalIsNotANoOp) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"5.0\")}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: NumberDecimal(\"5.0\")}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: NumberDecimal('5.0')}}"), + fromjson("{$v: 2, diff: {u: {a: NumberDecimal('5.0')}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -396,7 +408,9 @@ TEST_F(ArithmeticNodeTest, TypePromotionFromLongToDecimalIsNotANoOp) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"5.0\")}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: NumberDecimal(\"5.0\")}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: NumberDecimal('5.0')}}"), + fromjson("{$v: 2, diff: {u: {a: NumberDecimal('5.0')}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -414,7 +428,13 @@ TEST_F(ArithmeticNodeTest, TypePromotionFromDoubleToDecimalIsNotANoOp) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"5.25\")}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: NumberDecimal(\"5.25\")}}"), getLogDoc()); + + assertOplogEntry( + fromjson("{$set: {a: NumberDecimal('5.25')}}"), + fromjson("{$v: 2, diff: {u: {a: NumberDecimal('5.25')}}}"), + false // Not checking binary equality because the NumberDecimal in the expected output may + // not be bitwise identical to the result produced by the update system. + ); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -449,7 +469,13 @@ TEST_F(ArithmeticNodeTest, IncrementedDecimalStaysDecimal) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"11.5\")}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: NumberDecimal(\"11.5\")}}"), getLogDoc()); + + assertOplogEntry( + fromjson("{$set: {a: NumberDecimal('11.5')}}"), + fromjson("{$v: 2, diff: {u: {a: NumberDecimal('11.5')}}}"), + false // Not checking binary equality because the NumberDecimal in the expected output may + // not be bitwise identical to the result produced by the update system. + ); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -669,8 +695,8 @@ TEST_F(ArithmeticNodeTest, ApplyEmptyIndexData) { node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams()); ASSERT_EQUALS(fromjson("{a: 3}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: 3}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 3}}"), fromjson("{$v: 2, diff: {u: {a: 3}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -1002,8 +1028,9 @@ TEST_F(ArithmeticNodeTest, ApplyLogDottedPath) { node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams()); ASSERT_EQUALS(fromjson("{a: [{b:0}, {b:1}, {b:2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.2.b': 2}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u2: {b: 2}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -1019,8 +1046,9 @@ TEST_F(ArithmeticNodeTest, LogEmptyArray) { node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams()); ASSERT_EQUALS(fromjson("{a: [null, null, {b:2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.2.b': 2}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u2: {b: 2}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -1036,8 +1064,9 @@ TEST_F(ArithmeticNodeTest, LogEmptyObject) { node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams()); ASSERT_EQUALS(fromjson("{a: {'2': {b: 2}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.2.b': 2}}"), + fromjson("{$v: 2, diff: {sa: {i: {'2': {b: 2}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}"); } @@ -1057,7 +1086,8 @@ TEST_F(ArithmeticNodeTest, ApplyDeserializedDocNotNoOp) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 1, b: NumberInt(0)}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {b: NumberInt(0)}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {b: NumberInt(0)}}"), fromjson("{$v: 2, diff: {i: {b: 0}}}")); ASSERT_EQUALS(getModifiedPaths(), "{b}"); } @@ -1077,7 +1107,8 @@ TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNoOp) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: NumberInt(2)}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -1097,7 +1128,8 @@ TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNestedNoop) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: NumberInt(1)}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } @@ -1117,7 +1149,8 @@ TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNestedNotNoop) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 3}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.b': 3}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': 3}}"), fromjson("{$v: 2, diff: {sa: {u: {b: 3}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } diff --git a/src/mongo/db/update/bit_node_test.cpp b/src/mongo/db/update/bit_node_test.cpp index b8e652b1acf..e3903c6a301 100644 --- a/src/mongo/db/update/bit_node_test.cpp +++ b/src/mongo/db/update/bit_node_test.cpp @@ -163,7 +163,8 @@ TEST_F(BitNodeTest, ApplyAndLogEmptyDocumentAnd) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: 0}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 0}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 0}}"), fromjson("{$v: 2, diff: {i: {a: 0}}}")); } TEST_F(BitNodeTest, ApplyAndLogEmptyDocumentOr) { @@ -178,7 +179,8 @@ TEST_F(BitNodeTest, ApplyAndLogEmptyDocumentOr) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: 1}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 1}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 1}}"), fromjson("{$v: 2, diff: {i: {a: 1}}}")); } TEST_F(BitNodeTest, ApplyAndLogEmptyDocumentXor) { @@ -193,7 +195,8 @@ TEST_F(BitNodeTest, ApplyAndLogEmptyDocumentXor) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: 1}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 1}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 1}}"), fromjson("{$v: 2, diff: {i: {a: 1}}}")); } TEST_F(BitNodeTest, ApplyAndLogSimpleDocumentAnd) { @@ -208,7 +211,9 @@ TEST_F(BitNodeTest, ApplyAndLogSimpleDocumentAnd) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(BSON("a" << 0b0100), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(BSON("$set" << BSON("a" << 0b0100)), getLogDoc()); + + assertOplogEntry(BSON("$set" << BSON("a" << 0b0100)), + BSON("$v" << 2 << "diff" << BSON("u" << BSON("a" << 0b0100)))); } TEST_F(BitNodeTest, ApplyAndLogSimpleDocumentOr) { @@ -223,7 +228,9 @@ TEST_F(BitNodeTest, ApplyAndLogSimpleDocumentOr) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(BSON("a" << 0b0111), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(BSON("$set" << BSON("a" << 0b0111)), getLogDoc()); + + assertOplogEntry(BSON("$set" << BSON("a" << 0b0111)), + BSON("$v" << 2 << "diff" << BSON("u" << BSON("a" << 0b0111)))); } TEST_F(BitNodeTest, ApplyAndLogSimpleDocumentXor) { @@ -238,7 +245,9 @@ TEST_F(BitNodeTest, ApplyAndLogSimpleDocumentXor) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(BSON("a" << 0b0011), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(BSON("$set" << BSON("a" << 0b0011)), getLogDoc()); + + assertOplogEntry(BSON("$set" << BSON("a" << 0b0011)), + BSON("$v" << 2 << "diff" << BSON("u" << BSON("a" << 0b0011)))); } TEST_F(BitNodeTest, ApplyShouldReportNoOp) { @@ -253,7 +262,8 @@ TEST_F(BitNodeTest, ApplyShouldReportNoOp) { ASSERT_TRUE(result.noop); ASSERT_EQUALS(BSON("a" << static_cast<int>(1)), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(BitNodeTest, ApplyMultipleBitOps) { @@ -273,7 +283,9 @@ TEST_F(BitNodeTest, ApplyMultipleBitOps) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(BSON("a" << 0b0101011001100110), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(BSON("$set" << BSON("a" << 0b0101011001100110)), getLogDoc()); + + assertOplogEntry(BSON("$set" << BSON("a" << 0b0101011001100110)), + BSON("$v" << 2 << "diff" << BSON("u" << BSON("a" << 0b0101011001100110)))); } TEST_F(BitNodeTest, ApplyRepeatedBitOps) { @@ -288,7 +300,9 @@ TEST_F(BitNodeTest, ApplyRepeatedBitOps) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(BSON("a" << 0b10010110), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(BSON("$set" << BSON("a" << 0b10010110)), getLogDoc()); + + assertOplogEntry(BSON("$set" << BSON("a" << 0b10010110)), + BSON("$v" << 2 << "diff" << BSON("u" << BSON("a" << 0b10010110)))); } } // namespace diff --git a/src/mongo/db/update/compare_node_test.cpp b/src/mongo/db/update/compare_node_test.cpp index a04c84f852f..e3a608ea535 100644 --- a/src/mongo/db/update/compare_node_test.cpp +++ b/src/mongo/db/update/compare_node_test.cpp @@ -70,7 +70,8 @@ TEST_F(CompareNodeTest, ApplyMaxSameNumber) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 1}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(CompareNodeTest, ApplyMinSameNumber) { @@ -87,7 +88,8 @@ TEST_F(CompareNodeTest, ApplyMinSameNumber) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 1}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(CompareNodeTest, ApplyMaxNumberIsLess) { @@ -104,7 +106,8 @@ TEST_F(CompareNodeTest, ApplyMaxNumberIsLess) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 1}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(CompareNodeTest, ApplyMinNumberIsMore) { @@ -121,7 +124,8 @@ TEST_F(CompareNodeTest, ApplyMinNumberIsMore) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 1}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(CompareNodeTest, ApplyMaxSameValInt) { @@ -138,7 +142,8 @@ TEST_F(CompareNodeTest, ApplyMaxSameValInt) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 1.0}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(CompareNodeTest, ApplyMaxSameValIntZero) { @@ -155,7 +160,8 @@ TEST_F(CompareNodeTest, ApplyMaxSameValIntZero) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 0.0}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(CompareNodeTest, ApplyMinSameValIntZero) { @@ -172,7 +178,8 @@ TEST_F(CompareNodeTest, ApplyMinSameValIntZero) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 0.0}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(CompareNodeTest, ApplyMissingFieldMinNumber) { @@ -189,7 +196,8 @@ TEST_F(CompareNodeTest, ApplyMissingFieldMinNumber) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 0}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 0}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 0}}"), fromjson("{$v:2, diff: {i: {a: 0}}}")); } TEST_F(CompareNodeTest, ApplyExistingNumberMinNumber) { @@ -206,7 +214,8 @@ TEST_F(CompareNodeTest, ApplyExistingNumberMinNumber) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 0}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 0}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 0}}"), fromjson("{$v:2, diff: {u: {a: 0}}}")); } TEST_F(CompareNodeTest, ApplyMissingFieldMaxNumber) { @@ -223,7 +232,8 @@ TEST_F(CompareNodeTest, ApplyMissingFieldMaxNumber) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 0}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 0}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 0}}"), fromjson("{$v:2, diff: {i: {a: 0}}}")); } TEST_F(CompareNodeTest, ApplyExistingNumberMaxNumber) { @@ -240,7 +250,8 @@ TEST_F(CompareNodeTest, ApplyExistingNumberMaxNumber) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 2}}"), fromjson("{$v:2, diff: {u: {a: 2}}}")); } TEST_F(CompareNodeTest, ApplyExistingDateMaxDate) { @@ -257,7 +268,9 @@ TEST_F(CompareNodeTest, ApplyExistingDateMaxDate) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {$date: 123123123}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: {$date: 123123123}}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: {$date: 123123123}}}"), + fromjson("{$v:2, diff: {u: {a: {$date: 123123123}}}}")); } TEST_F(CompareNodeTest, ApplyExistingEmbeddedDocMaxDoc) { @@ -274,7 +287,8 @@ TEST_F(CompareNodeTest, ApplyExistingEmbeddedDocMaxDoc) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 3}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: {b: 3}}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: {b: 3}}}"), fromjson("{$v:2, diff: {u: {a: {b: 3}}}}")); } TEST_F(CompareNodeTest, ApplyExistingEmbeddedDocMaxNumber) { @@ -291,7 +305,8 @@ TEST_F(CompareNodeTest, ApplyExistingEmbeddedDocMaxNumber) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(CompareNodeTest, ApplyMinRespectsCollation) { @@ -311,7 +326,8 @@ TEST_F(CompareNodeTest, ApplyMinRespectsCollation) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 'dba'}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 'dba'}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 'dba'}}"), fromjson("{$v:2, diff: {u: {a: 'dba'}}}")); } TEST_F(CompareNodeTest, ApplyMinRespectsCollationFromSetCollator) { @@ -332,7 +348,8 @@ TEST_F(CompareNodeTest, ApplyMinRespectsCollationFromSetCollator) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 'dba'}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 'dba'}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 'dba'}}"), fromjson("{$v:2, diff: {u: {a: 'dba'}}}")); } TEST_F(CompareNodeTest, ApplyMaxRespectsCollationFromSetCollator) { @@ -353,7 +370,8 @@ TEST_F(CompareNodeTest, ApplyMaxRespectsCollationFromSetCollator) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 'abd'}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 'abd'}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 'abd'}}"), fromjson("{$v:2, diff: {u: {a: 'abd'}}}")); } DEATH_TEST_REGEX(CompareNodeTest, @@ -396,7 +414,8 @@ TEST_F(CompareNodeTest, ApplyIndexesNotAffected) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 1}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 1}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 1}}"), fromjson("{$v:2, diff: {u: {a: 1}}}")); } TEST_F(CompareNodeTest, ApplyNoIndexDataOrLogBuilder) { diff --git a/src/mongo/db/update/current_date_node_test.cpp b/src/mongo/db/update/current_date_node_test.cpp index a466de92157..95a1fcb2b9e 100644 --- a/src/mongo/db/update/current_date_node_test.cpp +++ b/src/mongo/db/update/current_date_node_test.cpp @@ -42,6 +42,22 @@ namespace mongo { namespace { +void assertOplogEntryIsUpdateOfExpectedType(const BSONObj& obj, + bool v2LogBuilderUsed, + StringData fieldName, + BSONType expectedType = BSONType::Date) { + if (v2LogBuilderUsed) { + ASSERT_EQUALS(obj.nFields(), 2); + ASSERT_EQUALS(obj["$v"].numberInt(), 2); + ASSERT_EQUALS(obj["diff"]["u"][fieldName].type(), expectedType); + } else { + ASSERT_EQUALS(obj.nFields(), 1); + ASSERT_TRUE(obj["$set"].type() == BSONType::Object); + ASSERT_EQUALS(obj["$set"].embeddedObject().nFields(), 1U); + ASSERT_EQUALS(obj["$set"][fieldName].type(), expectedType); + } +} + using CurrentDateNodeTest = UpdateNodeTest; using mongo::mutablebson::countChildren; using mongo::mutablebson::Element; @@ -142,11 +158,7 @@ TEST_F(CurrentDateNodeTest, ApplyTrue) { ASSERT_TRUE(doc.root()["a"].ok()); ASSERT_EQUALS(doc.root()["a"].getType(), BSONType::Date); - ASSERT_EQUALS(getLogDoc().root().countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"].countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"]["a"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"]["a"].getType(), BSONType::Date); + assertOplogEntryIsUpdateOfExpectedType(getOplogEntry(), v2LogBuilderUsed(), "a"); } TEST_F(CurrentDateNodeTest, ApplyFalse) { @@ -166,11 +178,7 @@ TEST_F(CurrentDateNodeTest, ApplyFalse) { ASSERT_TRUE(doc.root()["a"].ok()); ASSERT_EQUALS(doc.root()["a"].getType(), BSONType::Date); - ASSERT_EQUALS(getLogDoc().root().countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"].countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"]["a"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"]["a"].getType(), BSONType::Date); + assertOplogEntryIsUpdateOfExpectedType(getOplogEntry(), v2LogBuilderUsed(), "a"); } TEST_F(CurrentDateNodeTest, ApplyDate) { @@ -190,11 +198,7 @@ TEST_F(CurrentDateNodeTest, ApplyDate) { ASSERT_TRUE(doc.root()["a"].ok()); ASSERT_EQUALS(doc.root()["a"].getType(), BSONType::Date); - ASSERT_EQUALS(getLogDoc().root().countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"].countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"]["a"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"]["a"].getType(), BSONType::Date); + assertOplogEntryIsUpdateOfExpectedType(getOplogEntry(), v2LogBuilderUsed(), "a"); } TEST_F(CurrentDateNodeTest, ApplyTimestamp) { @@ -214,11 +218,8 @@ TEST_F(CurrentDateNodeTest, ApplyTimestamp) { ASSERT_TRUE(doc.root()["a"].ok()); ASSERT_EQUALS(doc.root()["a"].getType(), BSONType::bsonTimestamp); - ASSERT_EQUALS(getLogDoc().root().countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"].countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"]["a"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"]["a"].getType(), BSONType::bsonTimestamp); + assertOplogEntryIsUpdateOfExpectedType( + getOplogEntry(), v2LogBuilderUsed(), "a", BSONType::bsonTimestamp); } TEST_F(CurrentDateNodeTest, ApplyFieldDoesNotExist) { @@ -238,11 +239,14 @@ TEST_F(CurrentDateNodeTest, ApplyFieldDoesNotExist) { ASSERT_TRUE(doc.root()["a"].ok()); ASSERT_EQUALS(doc.root()["a"].getType(), BSONType::Date); - ASSERT_EQUALS(getLogDoc().root().countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"].countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"]["a"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"]["a"].getType(), BSONType::Date); + if (v2LogBuilderUsed()) { + ASSERT_EQUALS(getOplogEntry().nFields(), 2); + ASSERT_EQUALS(getOplogEntry()["$v"].numberInt(), 2); + ASSERT_EQUALS(getOplogEntry()["diff"]["i"]["a"].type(), BSONType::Date); + } else { + ASSERT_EQUALS(getOplogEntry().nFields(), 1); + ASSERT_EQUALS(getOplogEntry()["$set"]["a"].type(), BSONType::Date); + } } TEST_F(CurrentDateNodeTest, ApplyIndexesNotAffected) { @@ -258,15 +262,7 @@ TEST_F(CurrentDateNodeTest, ApplyIndexesNotAffected) { ASSERT_FALSE(result.noop); ASSERT_FALSE(result.indexesAffected); - ASSERT_EQUALS(doc.root().countChildren(), 1U); - ASSERT_TRUE(doc.root()["a"].ok()); - ASSERT_EQUALS(doc.root()["a"].getType(), BSONType::Date); - - ASSERT_EQUALS(getLogDoc().root().countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"].countChildren(), 1U); - ASSERT_TRUE(getLogDoc().root()["$set"]["a"].ok()); - ASSERT_EQUALS(getLogDoc().root()["$set"]["a"].getType(), BSONType::Date); + assertOplogEntryIsUpdateOfExpectedType(getOplogEntry(), v2LogBuilderUsed(), "a"); } TEST_F(CurrentDateNodeTest, ApplyNoIndexDataOrLogBuilder) { diff --git a/src/mongo/db/update/delta_executor.cpp b/src/mongo/db/update/delta_executor.cpp index 466e66800f8..c1d4eeb9f1e 100644 --- a/src/mongo/db/update/delta_executor.cpp +++ b/src/mongo/db/update/delta_executor.cpp @@ -33,6 +33,7 @@ #include "mongo/bson/mutable/document.h" #include "mongo/db/update/object_replace_executor.h" +#include "mongo/db/update/update_oplog_entry_serialization.h" namespace mongo { @@ -47,6 +48,7 @@ DeltaExecutor::ApplyResult DeltaExecutor::applyUpdate( auto result = ObjectReplaceExecutor::applyReplacementUpdate( std::move(applyParams), postImage, postImageHasId); result.indexesAffected = applyDiffOutput.indexesAffected; + result.oplogEntry = _outputOplogEntry; return result; } -} // namespace mongo
\ No newline at end of file +} // namespace mongo diff --git a/src/mongo/db/update/delta_executor.h b/src/mongo/db/update/delta_executor.h index f5bc8c162ca..d3ba6a9c199 100644 --- a/src/mongo/db/update/delta_executor.h +++ b/src/mongo/db/update/delta_executor.h @@ -33,6 +33,7 @@ #include "mongo/db/update/document_diff_applier.h" #include "mongo/db/update/document_diff_serialization.h" +#include "mongo/db/update/update_oplog_entry_serialization.h" namespace mongo { @@ -45,7 +46,9 @@ public: /** * Initializes the executor with the diff to apply. */ - explicit DeltaExecutor(doc_diff::Diff diff) : _diff(std::move(diff)) {} + explicit DeltaExecutor(doc_diff::Diff diff) + : _diff(std::move(diff)), + _outputOplogEntry(update_oplog_entry::makeDeltaOplogEntry(_diff)) {} ApplyResult applyUpdate(ApplyParams applyParams) const final; @@ -57,6 +60,11 @@ public: private: doc_diff::Diff _diff; + + // Although the delta executor is only used for applying $v:2 oplog entries on secondaries, it + // still needs to produce an oplog entry from the applyUpdate() method so that OpObservers may + // handle the event appropriately. + BSONObj _outputOplogEntry; }; } // namespace mongo diff --git a/src/mongo/db/update/pop_node_test.cpp b/src/mongo/db/update/pop_node_test.cpp index 1349670365c..b17fe6b189b 100644 --- a/src/mongo/db/update/pop_node_test.cpp +++ b/src/mongo/db/update/pop_node_test.cpp @@ -115,7 +115,8 @@ TEST_F(PopNodeTest, NoopWhenFirstPathComponentDoesNotExist) { ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: [1, 2, 3]}"), doc); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -133,7 +134,8 @@ TEST_F(PopNodeTest, NoopWhenPathPartiallyExists) { ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {}}"), doc); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a.b.c}", getModifiedPaths()); } @@ -151,7 +153,8 @@ TEST_F(PopNodeTest, NoopWhenNumericalPathComponentExceedsArrayLength) { ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a.0}", getModifiedPaths()); } @@ -215,7 +218,7 @@ TEST_F(PopNodeTest, NoopWhenPathContainsAnEmptyArray) { ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: []}}"), doc); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -233,7 +236,9 @@ TEST_F(PopNodeTest, PopsSingleElementFromTheBack) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: []}}"), doc); - ASSERT_EQUALS(fromjson("{$set: {'a.b': []}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': []}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: []}}}}")); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -251,7 +256,9 @@ TEST_F(PopNodeTest, PopsSingleElementFromTheFront) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: []}}"), doc); - ASSERT_EQUALS(fromjson("{$set: {'a.b': []}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': []}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: []}}}}")); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -269,7 +276,9 @@ TEST_F(PopNodeTest, PopsFromTheBackOfMultiElementArray) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [1, 2]}}"), doc); - ASSERT_EQUALS(fromjson("{$set: {'a.b': [1, 2]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': [1, 2]}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: [1, 2]}}}}")); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -287,7 +296,9 @@ TEST_F(PopNodeTest, PopsFromTheFrontOfMultiElementArray) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [2, 3]}}"), doc); - ASSERT_EQUALS(fromjson("{$set: {'a.b': [2, 3]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': [2, 3]}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: [2, 3]}}}}")); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -305,7 +316,9 @@ TEST_F(PopNodeTest, PopsFromTheFrontOfMultiElementArrayWithoutAffectingIndexes) ASSERT_FALSE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [2, 3]}}"), doc); - ASSERT_EQUALS(fromjson("{$set: {'a.b': [2, 3]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': [2, 3]}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: [2, 3]}}}}")); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -322,7 +335,9 @@ TEST_F(PopNodeTest, SucceedsWithNullUpdateIndexData) { ASSERT_FALSE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [1, 2]}}"), doc); - ASSERT_EQUALS(fromjson("{$set: {'a.b': [1, 2]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': [1, 2]}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: [1, 2]}}}}")); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -414,7 +429,8 @@ TEST_F(PopNodeTest, NoopOnImmutablePathSucceeds) { ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: []}}"), doc); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } diff --git a/src/mongo/db/update/pull_node_test.cpp b/src/mongo/db/update/pull_node_test.cpp index 4a178b0709d..7231445ecab 100644 --- a/src/mongo/db/update/pull_node_test.cpp +++ b/src/mongo/db/update/pull_node_test.cpp @@ -144,7 +144,8 @@ TEST_F(PullNodeTest, TargetNotFound) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(PullNodeTest, ApplyToStringFails) { @@ -211,7 +212,8 @@ TEST_F(PullNodeTest, ApplyToMissingElement) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: {c: {}}}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(PullNodeTest, ApplyToEmptyArray) { @@ -228,7 +230,8 @@ TEST_F(PullNodeTest, ApplyToEmptyArray) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(PullNodeTest, ApplyToArrayMatchingNone) { @@ -245,7 +248,8 @@ TEST_F(PullNodeTest, ApplyToArrayMatchingNone) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [2, 3, 4, 5]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(PullNodeTest, ApplyToArrayMatchingOne) { @@ -262,7 +266,9 @@ TEST_F(PullNodeTest, ApplyToArrayMatchingOne) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1, 2, 3]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1, 2, 3]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1, 2, 3]}}"), + fromjson("{$v: 2, diff: {u: {a: [1, 2, 3]}}}")); } TEST_F(PullNodeTest, ApplyToArrayMatchingSeveral) { @@ -279,7 +285,9 @@ TEST_F(PullNodeTest, ApplyToArrayMatchingSeveral) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1, 2, 3, 4, 5]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1, 2, 3, 4, 5]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1, 2, 3, 4, 5]}}"), + fromjson("{$v: 2, diff: {u: {a: [1, 2, 3, 4, 5]}}}")); } TEST_F(PullNodeTest, ApplyToArrayMatchingAll) { @@ -296,7 +304,8 @@ TEST_F(PullNodeTest, ApplyToArrayMatchingAll) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: []}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: []}}"), fromjson("{$v: 2, diff: {u: {a: []}}}")); } TEST_F(PullNodeTest, ApplyNoIndexDataNoLogBuilder) { @@ -334,7 +343,9 @@ TEST_F(PullNodeTest, ApplyWithCollation) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: ['zaa', 'zbb']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: ['zaa', 'zbb']}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: ['zaa', 'zbb']}}"), + fromjson("{$v: 2, diff: {u: {a: ['zaa', 'zbb']}}}")); } TEST_F(PullNodeTest, ApplyWithCollationDoesNotAffectNonStringMatches) { @@ -354,7 +365,8 @@ TEST_F(PullNodeTest, ApplyWithCollationDoesNotAffectNonStringMatches) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [2, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [2, 1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [2, 1]}}"), fromjson("{$v: 2, diff: {u: {a: [2, 1]}}}")); } TEST_F(PullNodeTest, ApplyWithCollationDoesNotAffectRegexMatches) { @@ -374,7 +386,9 @@ TEST_F(PullNodeTest, ApplyWithCollationDoesNotAffectRegexMatches) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: ['b', 'cb']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: ['b', 'cb']}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: ['b', 'cb']}}"), + fromjson("{$v: 2, diff: {u: {a: ['b', 'cb']}}}")); } TEST_F(PullNodeTest, ApplyStringLiteralMatchWithCollation) { @@ -394,7 +408,8 @@ TEST_F(PullNodeTest, ApplyStringLiteralMatchWithCollation) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: []}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: []}}"), fromjson("{$v: 2, diff: {u: {a: []}}}")); } TEST_F(PullNodeTest, ApplyCollationDoesNotAffectNumberLiteralMatches) { @@ -414,7 +429,9 @@ TEST_F(PullNodeTest, ApplyCollationDoesNotAffectNumberLiteralMatches) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: ['a', 'b', 2, 'c', 'd']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: ['a', 'b', 2, 'c', 'd']}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: ['a', 'b', 2, 'c', 'd']}}"), + fromjson("{$v: 2, diff: {u: {a: ['a', 'b', 2, 'c', 'd']}}}")); } TEST_F(PullNodeTest, ApplyStringMatchAfterSetCollator) { @@ -550,7 +567,9 @@ TEST_F(PullNodeTest, ApplyComplexDocAndMatching1) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [{x: 1}, {x: 2}]}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.b': [{x: 1}, {x: 2}]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': [{x: 1}, {x: 2}]}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: [{x: 1}, {x: 2}]}}}}")); } TEST_F(PullNodeTest, ApplyComplexDocAndMatching2) { @@ -567,7 +586,9 @@ TEST_F(PullNodeTest, ApplyComplexDocAndMatching2) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [{x: 1}, {x: 2}, {z: 'z'}]}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.b': [{x: 1}, {x: 2}, {z: 'z'}]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': [{x: 1}, {x: 2}, {z: 'z'}]}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: [{x: 1}, {x: 2}, {z: 'z'}]}}}}")); } TEST_F(PullNodeTest, ApplyComplexDocAndMatching3) { @@ -584,7 +605,9 @@ TEST_F(PullNodeTest, ApplyComplexDocAndMatching3) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [{x: 2}, {z: 'z'}]}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.b': [{x: 2}, {z: 'z'}]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': [{x: 2}, {z: 'z'}]}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: [{x: 2}, {z: 'z'}]}}}}")); } TEST_F(PullNodeTest, ApplyFullPredicateWithCollation) { @@ -605,7 +628,9 @@ TEST_F(PullNodeTest, ApplyFullPredicateWithCollation) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: []}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.b': []}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': []}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: []}}}}")); } TEST_F(PullNodeTest, ApplyScalarValueMod) { @@ -622,7 +647,9 @@ TEST_F(PullNodeTest, ApplyScalarValueMod) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [2, 2, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [2, 2, 2]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [2, 2, 2]}}"), + fromjson("{$v: 2, diff: {u: {a: [2, 2, 2]}}}")); } TEST_F(PullNodeTest, ApplyObjectValueMod) { @@ -639,7 +666,9 @@ TEST_F(PullNodeTest, ApplyObjectValueMod) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{x: 1}, {x: 1}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [{x: 1}, {x: 1}]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [{x: 1}, {x: 1}]}}"), + fromjson("{$v: 2, diff: {u: {a: [{x: 1}, {x: 1}]}}}")); } TEST_F(PullNodeTest, DocumentationExample1) { @@ -657,8 +686,10 @@ TEST_F(PullNodeTest, DocumentationExample1) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{flags: ['vme', 'de', 'pse', 'tsc', 'pae', 'mce']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {flags: ['vme', 'de', 'pse', 'tsc', 'pae', 'mce']}}"), - getLogDoc()); + + assertOplogEntry( + fromjson("{$set: {flags: ['vme', 'de', 'pse', 'tsc', 'pae', 'mce']}}"), + fromjson("{$v: 2, diff: {u: {flags: ['vme', 'de', 'pse', 'tsc', 'pae', 'mce']}}}")); } TEST_F(PullNodeTest, DocumentationExample2a) { @@ -675,7 +706,9 @@ TEST_F(PullNodeTest, DocumentationExample2a) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{votes: [3, 5, 6, 8]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {votes: [3, 5, 6, 8]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {votes: [3, 5, 6, 8]}}"), + fromjson("{$v: 2, diff: {u: {votes: [3, 5, 6, 8]}}}")); } TEST_F(PullNodeTest, DocumentationExample2b) { @@ -692,7 +725,9 @@ TEST_F(PullNodeTest, DocumentationExample2b) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{votes: [3, 5, 6]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {votes: [3, 5, 6]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {votes: [3, 5, 6]}}"), + fromjson("{$v: 2, diff: {u: {votes: [3, 5, 6]}}}")); } TEST_F(PullNodeTest, ApplyPullWithObjectValueToArrayWithNonObjectValue) { @@ -709,7 +744,8 @@ TEST_F(PullNodeTest, ApplyPullWithObjectValueToArrayWithNonObjectValue) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [2]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [2]}}"), fromjson("{$v: 2, diff: {u: {a: [2]}}}")); } TEST_F(PullNodeTest, CannotModifyImmutableField) { @@ -742,7 +778,9 @@ TEST_F(PullNodeTest, SERVER_3988) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{x: 1, y: [2, 3, 4, 'abc']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {y: [2, 3, 4, 'abc']}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {y: [2, 3, 4, 'abc']}}"), + fromjson("{$v: 2, diff: {u: {y: [2, 3, 4, 'abc']}}}")); } } // namespace diff --git a/src/mongo/db/update/pullall_node_test.cpp b/src/mongo/db/update/pullall_node_test.cpp index 8309b96e8aa..f0879fff9cb 100644 --- a/src/mongo/db/update/pullall_node_test.cpp +++ b/src/mongo/db/update/pullall_node_test.cpp @@ -97,7 +97,8 @@ TEST_F(PullAllNodeTest, TargetNotFound) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1, 'a', {r: 1, b: 2}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(PullAllNodeTest, TargetArrayElementNotFound) { @@ -115,7 +116,8 @@ TEST_F(PullAllNodeTest, TargetArrayElementNotFound) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1, 2]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(PullAllNodeTest, ApplyToNonArrayFails) { @@ -148,7 +150,9 @@ TEST_F(PullAllNodeTest, ApplyWithSingleNumber) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: ['a', {r: 1, b: 2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: ['a', {r: 1, b: 2}]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: ['a', {r: 1, b: 2}]}}"), + fromjson("{$v: 2, diff: {u: {a: [\"a\", {r: 1, b: 2}]}}}")); } TEST_F(PullAllNodeTest, ApplyNoIndexDataNoLogBuilder) { @@ -181,7 +185,8 @@ TEST_F(PullAllNodeTest, ApplyWithElementNotPresentInArray) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1, 'a', {r: 1, b: 2}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); } TEST_F(PullAllNodeTest, ApplyWithWithTwoElements) { @@ -198,7 +203,9 @@ TEST_F(PullAllNodeTest, ApplyWithWithTwoElements) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{r: 1, b: 2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [{r: 1, b: 2}]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [{r: 1, b: 2}]}}"), + fromjson("{$v: 2, diff: {u: {a: [{r: 1, b: 2}]}}}")); } TEST_F(PullAllNodeTest, ApplyWithAllArrayElements) { @@ -215,7 +222,8 @@ TEST_F(PullAllNodeTest, ApplyWithAllArrayElements) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: []}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: []}}"), fromjson("{$v: 2, diff: {u: {a: []}}}")); } TEST_F(PullAllNodeTest, ApplyWithAllArrayElementsButOutOfOrder) { @@ -232,7 +240,8 @@ TEST_F(PullAllNodeTest, ApplyWithAllArrayElementsButOutOfOrder) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: []}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: []}}"), fromjson("{$v: 2, diff: {u: {a: []}}}")); } TEST_F(PullAllNodeTest, ApplyWithAllArrayElementsAndThenSome) { @@ -249,7 +258,8 @@ TEST_F(PullAllNodeTest, ApplyWithAllArrayElementsAndThenSome) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: []}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: []}}"), fromjson("{$v: 2, diff: {u: {a: []}}}")); } TEST_F(PullAllNodeTest, ApplyWithCollator) { @@ -269,7 +279,9 @@ TEST_F(PullAllNodeTest, ApplyWithCollator) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: ['baz']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: ['baz']}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: ['baz']}}"), + fromjson("{$v: 2, diff: {u: {a: ['baz']}}}")); } TEST_F(PullAllNodeTest, ApplyAfterSetCollator) { diff --git a/src/mongo/db/update/push_node_test.cpp b/src/mongo/db/update/push_node_test.cpp index 92e81014846..1810610c302 100644 --- a/src/mongo/db/update/push_node_test.cpp +++ b/src/mongo/db/update/push_node_test.cpp @@ -287,7 +287,8 @@ TEST_F(PushNodeTest, ApplyToEmptyArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1]}}"), fromjson("{$v: 2, diff: {u: {a: [1]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -305,7 +306,8 @@ TEST_F(PushNodeTest, ApplyToEmptyDocument) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1]}}"), fromjson("{$v: 2, diff: {i: {a: [1]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -323,7 +325,9 @@ TEST_F(PushNodeTest, ApplyToArrayWithOneElement) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.1': 1}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.1': 1}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u1: 1}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -353,7 +357,9 @@ TEST_F(PushNodeTest, ApplyToDottedPathElement) { "}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'choices.first.votes': [1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'choices.first.votes': [1]}}"), + fromjson("{$v: 2, diff: {schoices: {sfirst: {i: {votes: [1]}}}}}")); ASSERT_EQUALS("{choices.first.votes}", getModifiedPaths()); } @@ -371,7 +377,8 @@ TEST_F(PushNodeTest, ApplySimpleEachToEmptyArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1]}}"), fromjson("{$v: 2, diff: {u: {a: [1]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -389,7 +396,8 @@ TEST_F(PushNodeTest, ApplySimpleEachToEmptyDocument) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1]}}"), fromjson("{$v: 2, diff: {i: {a: [1]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -407,7 +415,8 @@ TEST_F(PushNodeTest, ApplyMultipleEachToEmptyDocument) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1, 2]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1, 2]}}"), fromjson("{$v: 2, diff: {i: {a: [1, 2]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -425,7 +434,9 @@ TEST_F(PushNodeTest, ApplySimpleEachToArrayWithOneElement) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.1': 1}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.1': 1}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u1: 1}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -443,7 +454,9 @@ TEST_F(PushNodeTest, ApplyMultipleEachToArrayWithOneElement) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.1': 1, 'a.2': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.1': 1, 'a.2': 2}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u1: 1, u2: 2}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -461,7 +474,8 @@ TEST_F(PushNodeTest, ApplyEmptyEachToEmptyArray) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -479,7 +493,8 @@ TEST_F(PushNodeTest, ApplyEmptyEachToEmptyDocument) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: []}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: []}}"), fromjson("{$v: 2, diff: {i: {a: []}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -497,7 +512,8 @@ TEST_F(PushNodeTest, ApplyEmptyEachToArrayWithOneElement) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -515,7 +531,8 @@ TEST_F(PushNodeTest, ApplyToArrayWithSlice) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [3]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [3]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [3]}}"), fromjson("{$v: 2, diff: {u: {a: [3]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -533,7 +550,9 @@ TEST_F(PushNodeTest, ApplyWithNumericSort) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [-1, 2, 3]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [-1, 2, 3]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [-1, 2, 3]}}"), + fromjson("{$v: 2, diff: {u: {a: [-1, 2, 3]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -551,7 +570,9 @@ TEST_F(PushNodeTest, ApplyWithReverseNumericSort) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [4, 3, -1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [4, 3, -1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [4, 3, -1]}}"), + fromjson("{$v: 2, diff: {u: {a: [4, 3, -1]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -569,7 +590,9 @@ TEST_F(PushNodeTest, ApplyWithMixedSort) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [-1, 3, 4, 't', {a: 1}, {b: 1}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [-1, 3, 4, 't', {a: 1}, {b: 1}]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [-1, 3, 4, 't', {a: 1}, {b: 1}]}}"), + fromjson("{$v: 2, diff: {u: {a: [-1, 3, 4, 't', {a: 1}, {b: 1}]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -587,7 +610,9 @@ TEST_F(PushNodeTest, ApplyWithReverseMixedSort) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 1}, {a: 1}, 't', 4, 3, -1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [{b: 1}, {a: 1}, 't', 4, 3, -1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [{b: 1}, {a: 1}, 't', 4, 3, -1]}}"), + fromjson("{$v: 2, diff: {u: {a: [{b: 1}, {a: 1}, 't', 4, 3, -1]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -605,7 +630,9 @@ TEST_F(PushNodeTest, ApplyWithEmbeddedFieldSort) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [3, 't', {b: 1}, 4, -1, {a: 1}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [3, 't', {b: 1}, 4, -1, {a: 1}]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [3, 't', {b: 1}, 4, -1, {a: 1}]}}"), + fromjson("{$v: 2, diff: {u: {a: [3, 't', {b: 1}, 4, -1, {a: 1}]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -626,7 +653,9 @@ TEST_F(PushNodeTest, ApplySortWithCollator) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: ['ha', 'gb', 'fc', 'dd']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: ['ha', 'gb', 'fc', 'dd']}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: ['ha', 'gb', 'fc', 'dd']}}"), + fromjson("{$v: 2, diff: {u: {a: ['ha', 'gb', 'fc', 'dd']}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -855,7 +884,8 @@ TEST_F(PushNodeTest, ApplyToEmptyArrayWithPositionZero) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1]}}"), fromjson("{$v: 2, diff: {u: {a: [1]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -873,7 +903,8 @@ TEST_F(PushNodeTest, ApplyToEmptyArrayWithPositionOne) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1]}}"), fromjson("{$v: 2, diff: {u: {a: [1]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -891,7 +922,8 @@ TEST_F(PushNodeTest, ApplyToEmptyArrayWithLargePosition) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1]}}"), fromjson("{$v: 2, diff: {u: {a: [1]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -909,7 +941,8 @@ TEST_F(PushNodeTest, ApplyToSingletonArrayWithPositionZero) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1, 0]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1, 0]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1, 0]}}"), fromjson("{$v: 2, diff: {u: {a: [1, 0]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -927,7 +960,9 @@ TEST_F(PushNodeTest, ApplyToSingletonArrayWithLargePosition) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.1': 1}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.1': 1}}"), + fromjson(" {$v: 2, diff: {sa: {a: true, u1: 1}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -945,7 +980,8 @@ TEST_F(PushNodeTest, ApplyToEmptyArrayWithNegativePosition) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1]}}"), fromjson("{$v: 2, diff: {u: {a: [1]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -963,7 +999,8 @@ TEST_F(PushNodeTest, ApplyToSingletonArrayWithNegativePosition) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [1, 0]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1, 0]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1, 0]}}"), fromjson("{$v: 2, diff: {u: {a: [1, 0]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -981,7 +1018,9 @@ TEST_F(PushNodeTest, ApplyToPopulatedArrayWithNegativePosition) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1, 2, 5, 3, 4]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [0, 1, 2, 5, 3, 4]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [0, 1, 2, 5, 3, 4]}}"), + fromjson("{$v: 2, diff: {u: {a: [0, 1, 2, 5, 3, 4]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -999,7 +1038,9 @@ TEST_F(PushNodeTest, ApplyToPopulatedArrayWithOutOfBoundsNegativePosition) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [5, 0, 1, 2, 3, 4]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [5, 0, 1, 2, 3, 4]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [5, 0, 1, 2, 3, 4]}}"), + fromjson("{$v: 2, diff: {u: {a: [5, 0, 1, 2, 3, 4]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -1017,7 +1058,9 @@ TEST_F(PushNodeTest, ApplyMultipleElementsPushWithNegativePosition) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1, 2, 5, 6, 7, 3, 4]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [0, 1, 2, 5, 6, 7, 3, 4]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [0, 1, 2, 5, 6, 7, 3, 4]}}"), + fromjson("{$v: 2, diff: {u: {a: [0, 1, 2, 5, 6, 7, 3, 4]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -1037,7 +1080,9 @@ TEST_F(PushNodeTest, PushWithMinIntAsPosition) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [5, 0, 1, 2, 3, 4]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [5, 0, 1, 2, 3, 4]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [5, 0, 1, 2, 3, 4]}}"), + fromjson("{$v: 2, diff: {u: {a: [5, 0, 1, 2, 3, 4]}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } diff --git a/src/mongo/db/update/rename_node_test.cpp b/src/mongo/db/update/rename_node_test.cpp index bc52481b036..1ac12e6254d 100644 --- a/src/mongo/db/update/rename_node_test.cpp +++ b/src/mongo/db/update/rename_node_test.cpp @@ -123,7 +123,9 @@ TEST_F(RenameNodeTest, SimpleNumberAtRoot) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 2}"), doc); - ASSERT_EQUALS(fromjson("{$set: {b: 2}, $unset: {a: true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {b: 2}, $unset: {a: true}}"), + fromjson("{$v: 2, diff: {d: {a: false}, i: {b: 2}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } @@ -140,7 +142,9 @@ TEST_F(RenameNodeTest, ToExistsAtSameLevel) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 2}"), doc); - ASSERT_EQUALS(fromjson("{$set: {b: 2}, $unset: {a: true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {b: 2}, $unset: {a: true}}"), + fromjson("{$v: 2, diff: {d: {a: false}, u: {b: 2}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } @@ -157,7 +161,9 @@ TEST_F(RenameNodeTest, ToAndFromHaveSameValue) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 2}"), doc); - ASSERT_EQUALS(fromjson("{$set: {b: 2}, $unset: {a: true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {b: 2}, $unset: {a: true}}"), + fromjson("{$v: 2, diff: {d: {a: false}, u: {b: 2}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } @@ -174,7 +180,9 @@ TEST_F(RenameNodeTest, RenameToFieldWithSameValueButDifferentType) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 1}"), doc); - ASSERT_EQUALS(fromjson("{$set: {b: 1}, $unset: {a: true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {b: 1}, $unset: {a: true}}"), + fromjson("{$v: 2, diff: {d: {a: false}, u: {b: 1}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } @@ -191,7 +199,9 @@ TEST_F(RenameNodeTest, FromDottedElement) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {}, b: {d: 6}}"), doc); - ASSERT_EQUALS(fromjson("{$set: {b: {d: 6}}, $unset: {'a.c': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {b: {d: 6}}, $unset: {'a.c': true}}"), + fromjson("{$v: 2, diff: {u: {b: {d: 6}}, sa: {d: {c: false}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.c, b}"); } @@ -208,7 +218,9 @@ TEST_F(RenameNodeTest, RenameToExistingNestedFieldDoesNotReorderFields) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: {c: 4, d: 2}}, b: 3, c: {}}"), doc); - ASSERT_EQUALS(fromjson("{$set: {'a.b.c': 4}, $unset: {'c.d': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b.c': 4}, $unset: {'c.d': true}}"), + fromjson("{$v: 2, diff: {sa: {sb: {u: {c: 4}}}, sc: {d: {d: false}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.b.c, c.d}"); } @@ -226,7 +238,9 @@ TEST_F(RenameNodeTest, MissingCompleteTo) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 1, c: {r: {d: 2}}}"), doc); - ASSERT_EQUALS(fromjson("{$set: {'c.r.d': 2}, $unset: {'a': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'c.r.d': 2}, $unset: {'a': true}}"), + fromjson("{$v: 2, diff: {d: {a: false}, sc: {i: {r: {d: 2}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, c.r.d}"); } @@ -243,7 +257,9 @@ TEST_F(RenameNodeTest, ToIsCompletelyMissing) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: {c: {d: 2}}}"), doc); - ASSERT_EQUALS(fromjson("{$set: {'b.c.d': 2}, $unset: {'a': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'b.c.d': 2}, $unset: {'a': true}}"), + fromjson("{$v: 2, diff: {d: {a: false}, i: {b: {c: {d: 2}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, b.c.d}"); } @@ -260,7 +276,9 @@ TEST_F(RenameNodeTest, ToMissingDottedField) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: {c: {d: [{a:2, b:1}]}}}"), doc); - ASSERT_EQUALS(fromjson("{$set: {'b.c.d': [{a:2, b:1}]}, $unset: {'a': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'b.c.d': [{a:2, b:1}]}, $unset: {'a': true}}"), + fromjson("{$v: 2, diff: {d: {a: false}, i: {b: {c: {d: [{a: 2, b: 1}]}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, b.c.d}"); } @@ -378,7 +396,9 @@ TEST_F(RenameNodeTest, ReplaceArrayField) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 2}"), doc); - ASSERT_EQUALS(fromjson("{$set: {b: 2}, $unset: {a: true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {b: 2}, $unset: {a: true}}"), + fromjson("{$v: 2, diff: {d: {a: false}, u: {b: 2}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } @@ -395,7 +415,9 @@ TEST_F(RenameNodeTest, ReplaceWithArrayField) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: []}"), doc); - ASSERT_EQUALS(fromjson("{$set: {b: []}, $unset: {a: true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {b: []}, $unset: {a: true}}"), + fromjson("{$v: 2, diff: {d: {a: false}, u: {b: []}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } @@ -412,7 +434,9 @@ TEST_F(RenameNodeTest, CanRenameFromInvalidFieldName) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc); - ASSERT_EQUALS(fromjson("{$set: {a: 2}, $unset: {'$a': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 2}, $unset: {'$a': true}}"), + fromjson("{$v: 2, diff: {d: {$a: false}, i: {a: 2}}}")); ASSERT_EQUALS(getModifiedPaths(), "{$a, a}"); } @@ -443,7 +467,8 @@ TEST_F(RenameNodeTest, RenameFromNonExistentPathIsNoOp) { ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 2}"), doc); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } @@ -479,7 +504,9 @@ TEST_F(RenameNodeTest, ApplyCanRemoveRequiredPartOfDBRefIfValidateForStorageIsFa << "b" << 0); ASSERT_EQUALS(updated, doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'b': 0}, $unset: {'a.$id': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'b': 0}, $unset: {'a.$id': true}}"), + fromjson("{$v: 2, diff: {i: {b: 0}, sa: {d: {$id: false}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.$id, b}"); } @@ -546,7 +573,8 @@ TEST_F(RenameNodeTest, ApplyCanRemoveImmutablePathIfNoop) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: {}}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a.b.c, d}"); } diff --git a/src/mongo/db/update/set_node_test.cpp b/src/mongo/db/update/set_node_test.cpp index 45198f6e179..55a7f54027d 100644 --- a/src/mongo/db/update/set_node_test.cpp +++ b/src/mongo/db/update/set_node_test.cpp @@ -74,7 +74,8 @@ TEST_F(SetNodeTest, ApplyNoOp) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -92,7 +93,8 @@ TEST_F(SetNodeTest, ApplyEmptyPathToCreate) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 6}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 6}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 6}}"), fromjson("{$v: 2, diff: {u: {a: 6}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -111,7 +113,9 @@ TEST_F(SetNodeTest, ApplyCreatePath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {d: 5, b: {c: 6}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.b.c': 6}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b.c': 6}}"), + fromjson("{$v: 2, diff: {sa: {i: {b: {c: 6}}}}}")); ASSERT_EQUALS("{a.b.c}", getModifiedPaths()); } @@ -129,7 +133,8 @@ TEST_F(SetNodeTest, ApplyCreatePathFromRoot) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{c: 5, a: {b: 6}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.b': 6}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': 6}}"), fromjson("{$v: 2, diff: {i: {a: {b: 6}}}}")); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -148,7 +153,9 @@ TEST_F(SetNodeTest, ApplyPositional) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 6, 2]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.1': 6}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.1': 6}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u1: 6}}}")); ASSERT_EQUALS("{a.1}", getModifiedPaths()); } @@ -185,7 +192,8 @@ TEST_F(SetNodeTest, ApplyNonViablePathToCreateFromReplicationIsNoOp) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -357,8 +365,8 @@ TEST_F(SetNodeTest, ApplyLog) { node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams()); ASSERT_EQUALS(fromjson("{a: 2}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 2}}"), fromjson("{$v: 2, diff: {u: {a: 2}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -757,8 +765,9 @@ TEST_F(SetNodeTest, ApplyLogDottedPath) { node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams()); ASSERT_EQUALS(fromjson("{a: [{b:0}, {b:1}, {b:2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.2.b': 2}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u2: {b: 2}}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -774,8 +783,9 @@ TEST_F(SetNodeTest, LogEmptyArray) { node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams()); ASSERT_EQUALS(fromjson("{a: [null, null, {b:2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.2.b': 2}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u2: {b: 2}}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -791,8 +801,9 @@ TEST_F(SetNodeTest, LogEmptyObject) { node.apply(getApplyParams(doc.root()["a"]), getUpdateNodeApplyParams()); ASSERT_EQUALS(fromjson("{a: {'2': {b: 2}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.2.b': 2}}"), + fromjson("{$v: 2, diff: {sa: {i: {'2': {b: 2}}}}}")); ASSERT_EQUALS("{a.2.b}", getModifiedPaths()); } @@ -974,8 +985,8 @@ TEST_F(SetNodeTest, Set6) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{_id: 1, r: {a:2, b:2}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'r.a': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'r.a': 2}}"), fromjson("{$v: 2, diff: {sr: {u: {a: 2}}}}")); ASSERT_EQUALS("{r.a}", getModifiedPaths()); } @@ -994,8 +1005,8 @@ TEST_F(SetNodeTest, Set6FromRepl) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{_id: 1, r: {a:2, b:2} }"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'r.a': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'r.a': 2}}"), fromjson("{$v: 2, diff: {sr: {u: {a: 2}}}}")); ASSERT_EQUALS("{r.a}", getModifiedPaths()); } @@ -1098,8 +1109,8 @@ TEST_F(SetNodeTest, ApplyCanCreateDollarPrefixedFieldNameWhenValidateForStorageI ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{$bad: 1}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {$bad: 1}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {$bad: 1}}"), fromjson("{$v: 2, diff: {i: {$bad: 1}}}")); ASSERT_EQUALS("{$bad}", getModifiedPaths()); } @@ -1134,8 +1145,8 @@ TEST_F(SetNodeTest, ApplyCanPerformNoopOnImmutablePath) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 0u); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -1187,8 +1198,8 @@ TEST_F(SetNodeTest, ApplyCanPerformNoopOnPrefixOfImmutablePath) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 0u); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -1207,8 +1218,8 @@ TEST_F(SetNodeTest, ApplyCanOverwritePrefixToCreateImmutablePath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: {b: 2}}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: {b: 2}}}"), fromjson("{$v: 2, diff: {u: {a: {b: 2}}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -1227,8 +1238,9 @@ TEST_F(SetNodeTest, ApplyCanOverwritePrefixOfImmutablePathIfNoopOnImmutablePath) ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 2, c: 3}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: {b: 2, c: 3}}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: {b: 2, c: 3}}}"), + fromjson("{$v: 2, diff: {u: {a: {b: 2, c: 3}}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -1263,8 +1275,8 @@ TEST_F(SetNodeTest, ApplyCanPerformNoopOnSuffixOfImmutablePath) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: {c: 2}}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 0u); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a.b.c}", getModifiedPaths()); } @@ -1318,8 +1330,8 @@ TEST_F(SetNodeTest, ApplyCanCreateImmutablePath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.b': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.b': 2}}"), fromjson("{$v: 2, diff: {sa: {i: {b: 2}}}}")); ASSERT_EQUALS("{a.b}", getModifiedPaths()); } @@ -1338,8 +1350,8 @@ TEST_F(SetNodeTest, ApplyCanCreatePrefixOfImmutablePath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {a: 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 2}}"), fromjson("{$v: 2, diff: {i: {a: 2}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -1358,8 +1370,9 @@ TEST_F(SetNodeTest, ApplySetFieldInNonExistentArrayElementAffectsIndexOnSiblingF ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0}, {c: 2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.1.c': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.1.c': 2}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u1: {c: 2}}}}")); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -1378,8 +1391,9 @@ TEST_F(SetNodeTest, ApplySetFieldInExistingArrayElementDoesNotAffectIndexOnSibli ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0, c: 2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.0.c': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.0.c': 2}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {i: {c: 2}}}}}")); ASSERT_EQUALS("{a.0.c}", getModifiedPaths()); } @@ -1399,8 +1413,9 @@ TEST_F(SetNodeTest, ApplySetFieldInNonExistentNumericFieldDoesNotAffectIndexOnSi ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {'0': {b: 0}, '1': {c: 2}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); - ASSERT_EQUALS(fromjson("{$set: {'a.1.c': 2}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.1.c': 2}}"), + fromjson("{$v: 2, diff: {sa: {i: {'1': {c: 2}}}}}")); ASSERT_EQUALS("{a.1.c}", getModifiedPaths()); } @@ -1418,7 +1433,8 @@ TEST_F(SetNodeTest, ApplySetOnInsertIsNoopWhenInsertIsFalse) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a}", getModifiedPaths()); } diff --git a/src/mongo/db/update/unset_node.cpp b/src/mongo/db/update/unset_node.cpp index d49b5492cd3..c731833cfce 100644 --- a/src/mongo/db/update/unset_node.cpp +++ b/src/mongo/db/update/unset_node.cpp @@ -86,7 +86,14 @@ void UnsetNode::logUpdate(LogBuilderInterface* logBuilder, invariant(logBuilder); invariant(modifyResult == ModifyResult::kNormalUpdate); invariant(!createdFieldIdx); - uassertStatusOK(logBuilder->logDeletedField(pathTaken)); + + if (pathTaken.types().back() == RuntimeUpdatePath::ComponentType::kArrayIndex) { + // If $unset is applied to an array index, the value was set to null. + invariant(element.getType() == BSONType::jstNULL); + uassertStatusOK(logBuilder->logUpdatedField(pathTaken, element)); + } else { + uassertStatusOK(logBuilder->logDeletedField(pathTaken)); + } } } // namespace mongo diff --git a/src/mongo/db/update/unset_node_test.cpp b/src/mongo/db/update/unset_node_test.cpp index 67cb1140856..d45b2472a90 100644 --- a/src/mongo/db/update/unset_node_test.cpp +++ b/src/mongo/db/update/unset_node_test.cpp @@ -89,7 +89,8 @@ TEST_F(UnsetNodeTest, UnsetNoOp) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -108,7 +109,8 @@ TEST_F(UnsetNodeTest, UnsetNoOpDottedPath) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } @@ -127,7 +129,8 @@ TEST_F(UnsetNodeTest, UnsetNoOpThroughArray) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a:[{b:1}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } @@ -145,7 +148,8 @@ TEST_F(UnsetNodeTest, UnsetNoOpEmptyDoc) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -163,7 +167,8 @@ TEST_F(UnsetNodeTest, UnsetTopLevelPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$unset: {a: true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$unset: {a: true}}"), fromjson("{$v: 2, diff: {d: {a: false}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -181,7 +186,9 @@ TEST_F(UnsetNodeTest, UnsetNestedPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: {}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$unset: {'a.b.c': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$unset: {'a.b.c': true}}"), + fromjson("{$v: 2, diff: {sa: {sb: {d: {c: false}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.b.c}"); } @@ -199,7 +206,9 @@ TEST_F(UnsetNodeTest, UnsetObject) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$unset: {'a.b': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$unset: {'a.b': true}}"), + fromjson("{$v: 2, diff: {sa: {d: {b: false}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } @@ -217,7 +226,9 @@ TEST_F(UnsetNodeTest, UnsetArrayElement) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a:[null], b:1}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$unset: {'a.0': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.0': null}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u0: null}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.0}"); } @@ -236,7 +247,9 @@ TEST_F(UnsetNodeTest, UnsetPositional) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, null, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$unset: {'a.1': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.1': null}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u1: null}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.1}"); } @@ -254,7 +267,8 @@ TEST_F(UnsetNodeTest, UnsetEntireArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$unset: {a: true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$unset: {a: true}}"), fromjson("{$v: 2, diff: {d: {a: false}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -272,7 +286,9 @@ TEST_F(UnsetNodeTest, UnsetFromObjectInArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a:[{}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$unset: {'a.0.b': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$unset: {'a.0.b': true}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {d: {b: false}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.0.b}"); } @@ -290,7 +306,9 @@ TEST_F(UnsetNodeTest, CanUnsetInvalidField) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 1, a: [{}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$unset: {'a.0.$b': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$unset: {'a.0.$b': true}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {d: {$b: false}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.0.$b}"); } @@ -325,7 +343,8 @@ TEST_F(UnsetNodeTest, ApplyDoesNotAffectIndexes) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$unset: {a: true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$unset: {a: true}}"), fromjson("{$v: 2, diff: {d: {a: false}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -343,7 +362,9 @@ TEST_F(UnsetNodeTest, ApplyFieldWithDot) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{'a.b':4, a: {}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$unset: {'a.b': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$unset: {'a.b': true}}"), + fromjson("{$v: 2, diff: {sa: {d: {b: false}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } @@ -379,7 +400,9 @@ TEST_F(UnsetNodeTest, ApplyCanRemoveRequiredPartOfDBRefIfValidateForStorageIsFal << "c")); ASSERT_EQUALS(updated, doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$unset: {'a.$id': true}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$unset: {'a.$id': true}}"), + fromjson("{$v: 2, diff: {sa: {d: {$id: false}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.$id}"); } @@ -447,7 +470,8 @@ TEST_F(UnsetNodeTest, ApplyCanRemoveImmutablePathIfNoop) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 1}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a.b.c}"); } diff --git a/src/mongo/db/update/update_array_node_test.cpp b/src/mongo/db/update/update_array_node_test.cpp index d35a3a44633..95dbf2356a7 100644 --- a/src/mongo/db/update/update_array_node_test.cpp +++ b/src/mongo/db/update/update_array_node_test.cpp @@ -38,6 +38,7 @@ #include "mongo/db/pipeline/expression_context_for_test.h" #include "mongo/db/update/update_node_test_fixture.h" #include "mongo/db/update/update_object_node.h" +#include "mongo/unittest/bson_test_util.h" #include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" @@ -120,7 +121,9 @@ TEST_F(UpdateArrayNodeTest, UpdateIsAppliedToAllMatchingElements) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [2, 1, 2]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [2, 1, 2]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [2, 1, 2]}}"), + fromjson("{$v: 2, diff: {u: {a: [2, 1, 2]}}}")); ASSERT_EQUALS("{a.0, a.2}", getModifiedPaths()); } @@ -169,7 +172,9 @@ TEST_F(UpdateArrayNodeTest, UpdateForEmptyIdentifierIsAppliedToAllArrayElements) ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [1, 1, 1]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [1, 1, 1]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [1, 1, 1]}}"), + fromjson("{$v: 2, diff: {u: {a: [1, 1, 1]}}}")); ASSERT_EQUALS("{a.0, a.1, a.2}", getModifiedPaths()); } @@ -217,7 +222,9 @@ TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElement) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [{b: 1, c: 1, d: 1}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.0.b': 1, 'a.0.c': 1, 'a.0.d': 1}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.0.b': 1, 'a.0.c': 1, 'a.0.d': 1}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {u: {b: 1, c: 1, d: 1}}}}}")); ASSERT_EQUALS("{a.0.b, a.0.c, a.0.d}", getModifiedPaths()); } @@ -256,7 +263,9 @@ TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElementsUsingMergedChildr ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [{b: 1, c: 1}, {b: 1, c: 1}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [{b: 1, c: 1}, {b: 1, c: 1}]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [{b: 1, c: 1}, {b: 1, c: 1}]}}"), + fromjson("{$v: 2, diff: {u: {a: [{b: 1, c: 1}, {b: 1, c: 1}]}}}")); ASSERT_EQUALS("{a.0.b, a.0.c, a.1.b, a.1.c}", getModifiedPaths()); } @@ -304,7 +313,9 @@ TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElementsWithoutMergedChil ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [{b: 2, c: 2, d: 1}, {b: 1, c: 2, d: 2}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [{b: 2, c: 2, d: 1}, {b: 1, c: 2, d: 2}]}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: [{b: 2, c: 2, d: 1}, {b: 1, c: 2, d: 2}]}}"), + fromjson("{$v: 2, diff: {u: {a: [{b: 2, c: 2, d: 1}, {b: 1, c: 2, d: 2}]}}}")); ASSERT_EQUALS("{a.0.b, a.0.c, a.1.c, a.1.d}", getModifiedPaths()); } @@ -334,7 +345,9 @@ TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElementWithEmptyIdentifie ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [{b: 1, c: 1}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.0.b': 1, 'a.0.c': 1}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.0.b': 1, 'a.0.c': 1}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {u: {b: 1, c: 1}}}}}")); ASSERT_EQUALS("{a.0.b, a.0.c}", getModifiedPaths()); } @@ -379,7 +392,10 @@ TEST_F(UpdateArrayNodeTest, ApplyNestedArrayUpdates) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [{x: 0, b: [{c: 1, d: 1}]}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.0.b.0.c': 1, 'a.0.b.0.d': 1}}"), getLogDoc()); + + assertOplogEntry( + fromjson("{$set: {'a.0.b.0.c': 1, 'a.0.b.0.d': 1}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {sb: {a: true, s0: {u: {c: 1, d: 1}}}}}}}")); ASSERT_EQUALS("{a.0.b.0.c, a.0.b.0.d}", getModifiedPaths()); } @@ -520,7 +536,8 @@ TEST_F(UpdateArrayNodeTest, NoArrayElementsMatch) { ASSERT_TRUE(result.noop); ASSERT_EQUALS(fromjson("{a: [2, 2, 2]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -547,7 +564,8 @@ TEST_F(UpdateArrayNodeTest, UpdatesToAllArrayElementsAreNoops) { ASSERT_TRUE(result.noop); ASSERT_EQUALS(fromjson("{a: [1, 1, 1]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a.0, a.1, a.2}", getModifiedPaths()); } @@ -574,7 +592,10 @@ TEST_F(UpdateArrayNodeTest, NoArrayElementAffectsIndexes) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [{c: 0, b: 0}, {c: 0, b: 0}, {c: 0, b: 0}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: [{c: 0, b: 0}, {c: 0, b: 0}, {c: 0, b: 0}]}}"), getLogDoc()); + + assertOplogEntry( + fromjson("{$set: {a: [{c: 0, b: 0}, {c: 0, b: 0}, {c: 0, b: 0}]}}"), + fromjson("{$v: 2, diff: {u: {a: [{c: 0, b: 0}, {c: 0, b: 0}, {c: 0, b: 0}]}}}")); ASSERT_EQUALS("{a.0.b, a.1.b, a.2.b}", getModifiedPaths()); } @@ -601,7 +622,9 @@ TEST_F(UpdateArrayNodeTest, WhenOneElementIsMatchedLogElementUpdateDirectly) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [{c: 1}, {c: 0, b: 0}, {c: 1}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.1.b': 0}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.1.b': 0}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s1: {i: {b: 0}}}}}")); ASSERT_EQUALS("{a.1.b}", getModifiedPaths()); } @@ -628,7 +651,9 @@ TEST_F(UpdateArrayNodeTest, WhenOneElementIsModifiedLogElement) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [{c: 0, b: 0}, {c: 0, b: 0}, {c: 1}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.1': {c: 0, b: 0}}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.1': {c: 0, b: 0}}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u1: {c: 0, b: 0}}}}")); ASSERT_EQUALS("{a.0.b, a.1.b}", getModifiedPaths()); } @@ -652,7 +677,8 @@ TEST_F(UpdateArrayNodeTest, ArrayUpdateOnEmptyArrayIsANoop) { ASSERT_TRUE(result.noop); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a}", getModifiedPaths()); } @@ -680,7 +706,9 @@ TEST_F(UpdateArrayNodeTest, ApplyPositionalInsideArrayUpdate) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [{b: [0, 1], c: 0}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {'a.0.b.1': 1}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {'a.0.b.1': 1}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {sb: {a: true, u1: 1}}}}}")); ASSERT_EQUALS("{a.0.b.1}", getModifiedPaths()); } @@ -708,7 +736,8 @@ TEST_F(UpdateArrayNodeTest, ApplyArrayUpdateFromReplication) { ASSERT_TRUE(result.noop); ASSERT_EQUALS(fromjson("{a: [0]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS("{a.0.b}", getModifiedPaths()); } diff --git a/src/mongo/db/update/update_node_test_fixture.h b/src/mongo/db/update/update_node_test_fixture.h index aa4721894b4..aaa72fe2545 100644 --- a/src/mongo/db/update/update_node_test_fixture.h +++ b/src/mongo/db/update/update_node_test_fixture.h @@ -34,6 +34,7 @@ #include "mongo/db/service_context_test_fixture.h" #include "mongo/db/update/update_node.h" #include "mongo/db/update/v1_log_builder.h" +#include "mongo/db/update/v2_log_builder.h" #include "mongo/unittest/unittest.h" namespace mongo { @@ -42,6 +43,13 @@ class UpdateNodeTest : public ServiceContextTest { public: ~UpdateNodeTest() override = default; + void run() { + _useV2LogBuilder = false; + ServiceContextTest::run(); + _useV2LogBuilder = true; + ServiceContextTest::run(); + } + protected: // Creates a RuntimeUpdatePath from a string, assuming that all numeric path components are // array indexes. Tests which use numeric field names in objects must manually create a @@ -79,7 +87,11 @@ protected: _validateForStorage = true; _indexData.reset(); _logDoc.reset(); - _logBuilder = std::make_unique<V1LogBuilder>(_logDoc.root()); + if (_useV2LogBuilder) { + _logBuilder = std::make_unique<v2_log_builder::V2LogBuilder>(); + } else { + _logBuilder = std::make_unique<V1LogBuilder>(_logDoc.root()); + } _modifiedPaths.clear(); } @@ -145,14 +157,44 @@ protected: _logBuilder.reset(); } - const mutablebson::Document& getLogDoc() { - return _logDoc; - } - std::string getModifiedPaths() { return _modifiedPaths.toString(); } + bool v2LogBuilderUsed() const { + return _useV2LogBuilder; + } + + BSONObj getOplogEntry() const { + return _logBuilder->serialize(); + } + + void assertOplogEntryIsNoop() const { + if (v2LogBuilderUsed()) { + ASSERT_BSONOBJ_BINARY_EQ(getOplogEntry(), fromjson("{$v:2, diff: {}}")); + } else { + ASSERT_TRUE(getOplogEntry().isEmpty()); + } + } + + void assertOplogEntry(const BSONObj& expectedV1Entry, + const BSONObj& expectedV2Entry, + bool checkBinaryEquality = true) { + auto assertFn = [checkBinaryEquality](auto expected, auto given) { + if (checkBinaryEquality) { + ASSERT_BSONOBJ_BINARY_EQ(expected, given); + } else { + ASSERT_BSONOBJ_EQ(expected, given); + } + }; + + if (v2LogBuilderUsed()) { + assertFn(expectedV2Entry, getOplogEntry()); + } else { + assertFn(expectedV1Entry, getOplogEntry()); + } + } + private: std::vector<std::unique_ptr<FieldRef>> _immutablePathsVector; FieldRefSet _immutablePaths; @@ -166,6 +208,8 @@ private: mutablebson::Document _logDoc; std::unique_ptr<LogBuilderInterface> _logBuilder; FieldRefSetWithStorage _modifiedPaths; + + bool _useV2LogBuilder = false; }; } // namespace mongo diff --git a/src/mongo/db/update/update_object_node_test.cpp b/src/mongo/db/update/update_object_node_test.cpp index e1c0282eaab..a8d09f4f891 100644 --- a/src/mongo/db/update/update_object_node_test.cpp +++ b/src/mongo/db/update/update_object_node_test.cpp @@ -1774,7 +1774,8 @@ TEST_F(UpdateObjectNodeTest, ApplyCreateField) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: 5, b: 6}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {b: 6}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {b: 6}}"), fromjson("{$v: 2, diff: {i: {b: 6}}}")); ASSERT_EQUALS(getModifiedPaths(), "{b}"); } @@ -1798,7 +1799,8 @@ TEST_F(UpdateObjectNodeTest, ApplyExistingField) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: 6}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{$set: {a: 6}}"), getLogDoc()); + + assertOplogEntry(fromjson("{$set: {a: 6}}"), fromjson("{$v: 2, diff: {u: {a: 6}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -1840,7 +1842,9 @@ TEST_F(UpdateObjectNodeTest, ApplyExistingAndNonexistingFields) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: 5, c: 7, b: 6, d: 8}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {a: 5, b: 6, c: 7, d: 8}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {a: 5, b: 6, c: 7, d: 8}}"), + fromjson("{$v: 2, diff: {u: {a: 5, c: 7}, i: {b: 6, d: 8}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, b, c, d}"); } @@ -1882,8 +1886,9 @@ TEST_F(UpdateObjectNodeTest, ApplyExistingNestedPaths) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: {b: 6, c: 7}, b: {d: 8, e: 9}}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.b': 6, 'a.c': 7, 'b.d': 8, 'b.e': 9}}"), - getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.b': 6, 'a.c': 7, 'b.d': 8, 'b.e': 9}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: 6, c: 7}}, sb: {u: {d: 8, e: 9}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.b, a.c, b.d, b.e}"); } @@ -1925,8 +1930,9 @@ TEST_F(UpdateObjectNodeTest, ApplyCreateNestedPaths) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{z: 0, a: {b: 6, c: 7}, b: {d: 8, e: 9}}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.b': 6, 'a.c': 7, 'b.d': 8, 'b.e': 9}}"), - getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.b': 6, 'a.c': 7, 'b.d': 8, 'b.e': 9}}"), + fromjson("{$v: 2, diff: {i: {a: {b: 6, c: 7}, b: {d: 8, e: 9}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.b, a.c, b.d, b.e}"); } @@ -1962,8 +1968,9 @@ TEST_F(UpdateObjectNodeTest, ApplyCreateDeeplyNestedPaths) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{z: 0, a: {b: {c: {d: 6, e: 7}}, f: 8}}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.b.c.d': 6, 'a.b.c.e': 7, 'a.f': 8}}"), - getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.b.c.d': 6, 'a.b.c.e': 7, 'a.f': 8}}"), + fromjson("{$v: 2, diff: {i: {a: {b: {c: {d: 6, e: 7}}, f: 8}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.b.c.d, a.b.c.e, a.f}"); } @@ -2011,7 +2018,9 @@ TEST_F(UpdateObjectNodeTest, ChildrenShouldBeAppliedInAlphabeticalOrder) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{z: 9, a: 5, b: 8, c: 7, d: 6}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {a: 5, b: 8, c: 7, d: 6, z: 9}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {a: 5, b: 8, c: 7, d: 6, z: 9}}"), + fromjson("{$v: 2, diff: {u: {a: 5, z: 9}, i: {b: 8, c: 7, d: 6}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, b, c, d, z}"); } @@ -2044,7 +2053,9 @@ TEST_F(UpdateObjectNodeTest, CollatorShouldNotAffectUpdateOrder) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{abc: 5, cba: 6}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {abc: 5, cba: 6}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {abc: 5, cba: 6}}"), + fromjson("{$v: 2, diff: {i: {abc: 5, cba: 6}}}")); } TEST_F(UpdateObjectNodeTest, ApplyNoop) { @@ -2081,7 +2092,8 @@ TEST_F(UpdateObjectNodeTest, ApplyNoop) { ASSERT_TRUE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: 5, b: 6, c: 7}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{}"), getLogDoc().getObject()); + + assertOplogEntryIsNoop(); ASSERT_EQUALS(getModifiedPaths(), "{a, b, c}"); } @@ -2119,7 +2131,8 @@ TEST_F(UpdateObjectNodeTest, ApplySomeChildrenNoops) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: 5, b: 6, c: 7}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {b: 6}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {b: 6}}"), fromjson("{$v: 2, diff: {u: {b: 6}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a, b, c}"); } @@ -2172,7 +2185,8 @@ TEST_F(UpdateObjectNodeTest, ApplyBlockingElementFromReplication) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: 0, b: 6}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {b: 6}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {b: 6}}"), fromjson("{$v: 2, diff: {i: {b: 6}}}")); } TEST_F(UpdateObjectNodeTest, ApplyPositionalMissingMatchedField) { @@ -2225,7 +2239,9 @@ TEST_F(UpdateObjectNodeTest, ApplyMergePositionalChild) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: [{b: 5, c: 6}]}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {u: {b: 5, c: 6}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.0.b, a.0.c}"); } @@ -2268,8 +2284,9 @@ TEST_F(UpdateObjectNodeTest, ApplyOrderMergedPositionalChild) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: {'0': 7, '1': {b: 6, c: 8}, '2': 5}}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0': 7, 'a.1.b': 6, 'a.1.c': 8, 'a.2': 5}}"), - getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.0': 7, 'a.1.b': 6, 'a.1.c': 8, 'a.2': 5}}"), + fromjson("{$v: 2, diff: {i: {a: {'0': 7, '1': {b: 6, c: 8}, '2': 5}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.0, a.1.b, a.1.c, a.2}"); } @@ -2335,7 +2352,9 @@ TEST_F(UpdateObjectNodeTest, ApplyDoNotMergePositionalChild) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: {'0': 5, '1': 7, '2': 6}}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0': 5, 'a.1': 7, 'a.2': 6}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.0': 5, 'a.1': 7, 'a.2': 6}}"), + fromjson("{$v: 2, diff: {i: {a: {'0': 5, '1': 7, '2': 6}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.0, a.1, a.2}"); } @@ -2372,7 +2391,9 @@ TEST_F(UpdateObjectNodeTest, ApplyPositionalChildLast) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: {'0': 6, '1': 7, '2': 5}}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0': 6, 'a.1': 7, 'a.2': 5}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.0': 6, 'a.1': 7, 'a.2': 5}}"), + fromjson("{$v: 2, diff: {i: {a: {'0': 6, '1': 7, '2': 5}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.0, a.1, a.2}"); } @@ -2403,7 +2424,9 @@ TEST_F(UpdateObjectNodeTest, ApplyUseStoredMergedPositional) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: [{b: 5, c: 6}]}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {u: {b: 5, c: 6}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.0.b, a.0.c}"); mutablebson::Document doc2(fromjson("{a: [{b: 0, c: 0}]}")); @@ -2415,7 +2438,9 @@ TEST_F(UpdateObjectNodeTest, ApplyUseStoredMergedPositional) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: [{b: 5, c: 6}]}"), doc2.getObject()); ASSERT_TRUE(doc2.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {u: {b: 5, c: 6}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.0.b, a.0.c}"); } @@ -2452,8 +2477,10 @@ TEST_F(UpdateObjectNodeTest, ApplyDoNotUseStoredMergedPositional) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: [{b: 5, c: 6}, {c: 0, d: 7}]}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6, 'a.1.d': 7}}"), - getLogDoc().getObject()); + + assertOplogEntry( + fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6, 'a.1.d': 7}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {u: {b: 5, c: 6}}, s1: {u: {d: 7}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.0.b, a.0.c, a.1.d}"); mutablebson::Document doc2(fromjson("{a: [{b: 0, c: 0}, {c: 0, d: 0}]}")); @@ -2465,8 +2492,10 @@ TEST_F(UpdateObjectNodeTest, ApplyDoNotUseStoredMergedPositional) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: [{b: 5, c: 0}, {c: 6, d: 7}]}"), doc2.getObject()); ASSERT_TRUE(doc2.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0.b': 5, 'a.1.c': 6, 'a.1.d': 7}}"), - getLogDoc().getObject()); + + assertOplogEntry( + fromjson("{$set: {'a.0.b': 5, 'a.1.c': 6, 'a.1.d': 7}}"), + fromjson("{$v: 2, diff: {sa: {a: true, s0: {u: {b: 5}}, s1: {u: {c: 6, d: 7}}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.0.b, a.1.c, a.1.d}"); } @@ -2495,7 +2524,9 @@ TEST_F(UpdateObjectNodeTest, ApplyToArrayByIndexWithLeadingZero) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: [0, 0, 2, 0, 0]}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.02': 2}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.02': 2}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u2: 2}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.02}"); } @@ -2533,7 +2564,9 @@ TEST_F(UpdateObjectNodeTest, ApplyMultipleArrayUpdates) { fromjson("{a: [null, null, 2, null, null, null, null, null, null, null, 10]}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.10': 10, 'a.2': 2}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.10': 10, 'a.2': 2}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u2: 2, u10: 10}}}")); } TEST_F(UpdateObjectNodeTest, ApplyMultipleUpdatesToDocumentInArray) { @@ -2562,7 +2595,9 @@ TEST_F(UpdateObjectNodeTest, ApplyMultipleUpdatesToDocumentInArray) { ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: [null, null, {b: 1, c: 1}]}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.2.b': 1, 'a.2.c': 1}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.2.b': 1, 'a.2.c': 1}}"), + fromjson("{$v: 2, diff: {sa: {a: true, u2: {b: 1, c: 1}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a}"); } @@ -2612,7 +2647,9 @@ TEST_F(UpdateObjectNodeTest, SetAndPopModifiersWithCommonPrefixApplySuccessfully ASSERT_FALSE(result.noop); ASSERT_BSONOBJ_EQ(fromjson("{a: {b: 5, c: [2, 3, 4]}}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.b': 5, 'a.c': [2, 3, 4]}}"), getLogDoc().getObject()); + + assertOplogEntry(fromjson("{$set: {'a.b': 5, 'a.c': [2, 3, 4]}}"), + fromjson("{$v: 2, diff: {sa: {u: {b: 5, c: [ 2, 3, 4 ]}}}}")); ASSERT_EQUALS(getModifiedPaths(), "{a.b, a.c}"); } diff --git a/src/mongo/db/update/update_tree_executor.h b/src/mongo/db/update/update_tree_executor.h index fff31d4935f..e9f7dc93f99 100644 --- a/src/mongo/db/update/update_tree_executor.h +++ b/src/mongo/db/update/update_tree_executor.h @@ -34,6 +34,7 @@ #include "mongo/db/update/update_node.h" #include "mongo/db/update/update_object_node.h" #include "mongo/db/update/v1_log_builder.h" +#include "mongo/db/update/v2_log_builder.h" namespace mongo { @@ -44,11 +45,12 @@ public: ApplyResult applyUpdate(ApplyParams applyParams) const final { mutablebson::Document logDocument; - boost::optional<V1LogBuilder> optLogBuilder; + boost::optional<V1LogBuilder> optV1LogBuilder; + boost::optional<v2_log_builder::V2LogBuilder> optV2LogBuilder; UpdateNode::UpdateNodeApplyParams updateNodeApplyParams; - if (applyParams.logMode != ApplyParams::LogMode::kDoNotGenerateOplogEntry) { + if (applyParams.logMode == ApplyParams::LogMode::kGenerateOnlyV1OplogEntry) { // In versions since 3.6, the absence of a $v field indicates either a // replacement-style update or a "classic" modifier-style update. // @@ -61,8 +63,11 @@ public: // (b) It is easy to distinguish from $v: 2 delta-style oplog entries. const bool includeVersionField = true; - optLogBuilder.emplace(logDocument.root(), includeVersionField); - updateNodeApplyParams.logBuilder = optLogBuilder.get_ptr(); + optV1LogBuilder.emplace(logDocument.root(), includeVersionField); + updateNodeApplyParams.logBuilder = optV1LogBuilder.get_ptr(); + } else if (applyParams.logMode == ApplyParams::LogMode::kGenerateOplogEntry) { + optV2LogBuilder.emplace(); + updateNodeApplyParams.logBuilder = optV2LogBuilder.get_ptr(); } auto ret = _updateTree->apply(applyParams, updateNodeApplyParams); diff --git a/src/mongo/db/update/v2_log_builder.cpp b/src/mongo/db/update/v2_log_builder.cpp index 45b49e24a13..aec1148f22f 100644 --- a/src/mongo/db/update/v2_log_builder.cpp +++ b/src/mongo/db/update/v2_log_builder.cpp @@ -29,6 +29,8 @@ #include "mongo/db/update/v2_log_builder.h" +#include <stack> + #include "mongo/base/checked_cast.h" #include "mongo/db/field_ref.h" #include "mongo/db/update/update_oplog_entry_serialization.h" @@ -86,7 +88,7 @@ Status V2LogBuilder::logUpdatedField(const RuntimeUpdatePath& path, mutablebson: Status V2LogBuilder::logCreatedField(const RuntimeUpdatePath& path, int idxOfFirstNewComponent, mutablebson::Element elt) { - auto newNode = std::make_unique<InsertElement>(elt); + auto newNode = std::make_unique<InsertNode>(elt); addNodeAtPath(path, &_root, std::move(newNode), idxOfFirstNewComponent); return Status::OK(); } @@ -94,7 +96,7 @@ Status V2LogBuilder::logCreatedField(const RuntimeUpdatePath& path, Status V2LogBuilder::logCreatedField(const RuntimeUpdatePath& path, int idxOfFirstNewComponent, BSONElement elt) { - auto newNode = std::make_unique<InsertElement>(elt); + auto newNode = std::make_unique<InsertNode>(elt); addNodeAtPath(path, &_root, std::move(newNode), idxOfFirstNewComponent); return Status::OK(); } @@ -179,126 +181,245 @@ void appendElementToBuilder(stdx::variant<mutablebson::Element, BSONElement> ele [&](BSONElement element) { builder->appendAs(element, fieldName); }}, elem); } -void serializeNewlyCreatedDocument(DocumentNode* const node, BSONObjBuilder* out) { - for (auto&& [fieldName, child] : node->inserts) { - if (child->type() == NodeType::kInsert) { - appendElementToBuilder(checked_cast<InsertElement*>(child)->elt, fieldName, out); - continue; + +// Construction of the $v:2 diff needs to handle the same number of levels of recursion as the +// maximum permitted BSON depth. In order to avoid the possibility of stack overflow, which has +// been observed in configurations that use small stack sizes(such as 'dbg=on'), we use an explicit +// stack data structure stored on the heap instead. +// +// The Frame class represents one "frame" of this explicit stack. +class Frame { +public: + virtual ~Frame() {} + + // When execute() is called, the Frame may either return a new Frame to be placed on top of the + // stack, or return nullptr, indicating that the frame has finished and can be destroyed. + // + // If a Frame returns a new stack frame, it must be able to pick up where it left off when + // execute() is called on it again. + virtual std::unique_ptr<Frame> execute() = 0; +}; +using UniqueFrame = std::unique_ptr<Frame>; + +// Helper used for creating a new frame from a sub-diff node. Definition depends on some of the +// *Frame constructors. +UniqueFrame makeSubNodeFrameHelper(InternalNode* node, BSONObjBuilder builder); +// Given a 'node' stored in the 'inserts' section of an InternalNode, will either append that +// node's value to the given builder, or return a new stack frame which will build the object to be +// inserted. 'node' must be an InsertionNode or a DocumentInsertNode. +UniqueFrame handleInsertHelper(StringData fieldName, Node* node, BSONObjBuilder* bob); + +// Stack frame used to maintain state while serializing DocumentInsertionNodes. +class DocumentInsertFrame final : public Frame { +public: + DocumentInsertFrame(const DocumentInsertionNode& node, BSONObjBuilder bob) + : _node(node), _bob(std::move(bob)) {} + + UniqueFrame execute() override { + for (; _insertIdx < _node.children.size(); ++_insertIdx) { + auto&& [fieldName, child] = _node.inserts[_insertIdx]; + + if (auto newFrame = handleInsertHelper(fieldName, child, &_bob)) { + ++_insertIdx; + return newFrame; + } } - BSONObjBuilder childBuilder(out->subobjStart(fieldName)); - serializeNewlyCreatedDocument(checked_cast<DocumentNode* const>(child), &childBuilder); + return nullptr; } -} -// Mutually recursive with writeArrayDiff(). -void writeSubNodeHelper(InternalNode* node, BSONObjBuilder* builder); - -void writeArrayDiff(const ArrayNode& node, BSONObjBuilder* builder) { - builder->append(doc_diff::kArrayHeader, true); - for (auto&& [idx, child] : node.children) { - auto idxAsStr = std::to_string(idx); - switch (child->type()) { - case (NodeType::kUpdate): { - const auto& valueNode = checked_cast<const UpdateNode&>(*child); - appendElementToBuilder( - valueNode.elt, doc_diff::kUpdateSectionFieldName + idxAsStr, builder); - break; - } - case (NodeType::kInsert): { - const auto& valueNode = checked_cast<const InsertElement&>(*child); - appendElementToBuilder( - valueNode.elt, doc_diff::kUpdateSectionFieldName + idxAsStr, builder); - break; +private: + size_t _insertIdx = 0; + const DocumentInsertionNode& _node; + BSONObjBuilder _bob; +}; + +// Stack frame used to maintain state while serializing DocumentSubDiffNodes. +class DocumentSubDiffFrame final : public Frame { +public: + DocumentSubDiffFrame(const DocumentSubDiffNode& node, BSONObjBuilder bob) + : _node(node), _bob(std::move(bob)) {} + + UniqueFrame execute() override { + if (!_wroteDeletesAndUpdates) { + if (!_node.deletes.empty()) { + BSONObjBuilder subBob(_bob.subobjStart(doc_diff::kDeleteSectionFieldName)); + for (auto&& [fieldName, node] : _node.deletes) { + // The deletes are logged in the form {fieldName: false} in $v:2 format. + subBob.append(fieldName, false); + } } - case (NodeType::kDocumentInsert): { - // This represents that the array element is being created which has a sub-object. - // - // For example {$set: {"a.0.c": 1}} when the input document is {a: []}. Here we need - // to create the array element at '0', then sub document 'c'. - BSONObjBuilder childBuilder = - builder->subobjStart(doc_diff::kUpdateSectionFieldName + idxAsStr); - serializeNewlyCreatedDocument(checked_cast<DocumentNode*>(child.get()), - &childBuilder); - break; + if (!_node.updates.empty()) { + BSONObjBuilder subBob(_bob.subobjStart(doc_diff::kUpdateSectionFieldName)); + for (auto&& [fieldName, node] : _node.updates) { + appendElementToBuilder(node->elt, fieldName, &subBob); + } } - case (NodeType::kDocumentSubDiff): - case (NodeType::kArray): { - InternalNode* subNode = checked_cast<InternalNode*>(child.get()); - BSONObjBuilder childBuilder = builder->subobjStart( - std::string(1, doc_diff::kSubDiffSectionFieldPrefix) + idxAsStr); - writeSubNodeHelper(subNode, &childBuilder); - break; + _wroteDeletesAndUpdates = true; + } + + for (; _insertIdx < _node.inserts.size(); ++_insertIdx) { + if (!_insertBob) { + _insertBob.emplace(_bob.subobjStart(doc_diff::kInsertSectionFieldName)); } - case (NodeType::kDelete): { - MONGO_UNREACHABLE; + + auto&& [fieldName, child] = _node.inserts[_insertIdx]; + + if (auto newFrame = handleInsertHelper(fieldName, child, _insertBob.get_ptr())) { + ++_insertIdx; + return newFrame; } } - } -} -void writeDocumentDiff(const DocumentNode& node, BSONObjBuilder* builder) { - if (!node.deletes.empty()) { - BSONObjBuilder subBob(builder->subobjStart(doc_diff::kDeleteSectionFieldName)); - for (auto&& [fieldName, node] : node.deletes) { - // The deletes are logged in the form {fieldName: false} in $v:2 format. - subBob.append(fieldName, false); + if (_insertBob) { + // All inserts have been written so we destroy the insert builder now. + _insertBob = boost::none; } - } - if (!node.updates.empty()) { - BSONObjBuilder subBob(builder->subobjStart(doc_diff::kUpdateSectionFieldName)); - for (auto&& [fieldName, node] : node.updates) { - appendElementToBuilder(node->elt, fieldName, &subBob); + + if (_subDiffIdx != _node.subDiffs.size()) { + auto&& [fieldName, child] = _node.subDiffs[_subDiffIdx]; + + BSONObjBuilder childBuilder = + _bob.subobjStart(std::string(1, doc_diff::kSubDiffSectionFieldPrefix) + fieldName); + ++_subDiffIdx; + return makeSubNodeFrameHelper(child, std::move(childBuilder)); } + + return nullptr; + } + + BSONObjBuilder& bob() { + return _bob; } - if (!node.inserts.empty()) { - BSONObjBuilder insertBob(builder->subobjStart(doc_diff::kInsertSectionFieldName)); - for (auto&& [fieldName, child] : node.inserts) { - if (child->type() == NodeType::kInsert) { - appendElementToBuilder( - checked_cast<InsertElement*>(child)->elt, fieldName, &insertBob); - continue; +private: + const DocumentSubDiffNode& _node; + BSONObjBuilder _bob; + + // Indicates whether or not we've written deletes and updates yet. Since deletes and updates + // are always leaf nodes, they are always written in the first call to execute(). + bool _wroteDeletesAndUpdates = false; + + // Keeps track of which insertion or subDiff is being serialized. + size_t _insertIdx = 0; + size_t _subDiffIdx = 0; + + boost::optional<BSONObjBuilder> _insertBob; +}; + +// Stack frame used to maintain state while serializing ArrayNodes. +class ArrayFrame final : public Frame { +public: + ArrayFrame(const ArrayNode& node, BSONObjBuilder bob) + : _node(node), _bob(std::move(bob)), _childIt(node.children.begin()) {} + + UniqueFrame execute() override { + invariant(!_node.children.empty()); + if (_childIt == _node.children.begin()) { + _bob.append(doc_diff::kArrayHeader, true); + } + + for (; _childIt != _node.children.end(); ++_childIt) { + + auto&& [idx, child] = *_childIt; + auto idxAsStr = std::to_string(idx); + + switch (child->type()) { + case (NodeType::kUpdate): { + const auto& valueNode = checked_cast<const UpdateNode&>(*child); + appendElementToBuilder( + valueNode.elt, doc_diff::kUpdateSectionFieldName + idxAsStr, &_bob); + break; + } + case (NodeType::kInsert): { + const auto& valueNode = checked_cast<const InsertNode&>(*child); + appendElementToBuilder( + valueNode.elt, doc_diff::kUpdateSectionFieldName + idxAsStr, &_bob); + break; + } + case (NodeType::kDocumentInsert): { + // This represents an array element that is being created with a sub object. + // + // For example {$set: {"a.0.c": 1}} when the input document is {a: []}. Here we + // need to create the array element at '0', then sub document 'c'. + + ++_childIt; + return std::make_unique<DocumentInsertFrame>( + *checked_cast<DocumentInsertionNode*>(child.get()), + BSONObjBuilder( + _bob.subobjStart(doc_diff::kUpdateSectionFieldName + idxAsStr))); + } + case (NodeType::kDocumentSubDiff): + case (NodeType::kArray): { + InternalNode* subNode = checked_cast<InternalNode*>(child.get()); + BSONObjBuilder childBuilder = _bob.subobjStart( + std::string(1, doc_diff::kSubDiffSectionFieldPrefix) + idxAsStr); + + ++_childIt; + return makeSubNodeFrameHelper(subNode, std::move(childBuilder)); + } + case (NodeType::kDelete): { + MONGO_UNREACHABLE; + } } - // This represents a new document. While the modifier-style update system - // was capable of writing paths which would implicitly create new - // documents, there is no equivalent in $v: 2 updates. - // - // For example {$set: {"a.b.c": 1}} would create document 'a' and 'a.b' if - // necessary. - // - // Since $v:2 entries don't have this capability, we instead build the new - // object and log it as an insert. For example, applying the above $set on - // document {a: {}} will be logged as an insert of the value {b: {c: 1}} on - // document 'a'. - invariant(child->type() == NodeType::kDocumentInsert); - BSONObjBuilder subBob = insertBob.subobjStart(fieldName); - serializeNewlyCreatedDocument(checked_cast<DocumentNode*>(child), &subBob); } + + return nullptr; } - for (auto&& [fieldName, child] : node.subDiffs) { - BSONObjBuilder childBuilder = - builder->subobjStart(std::string(1, doc_diff::kSubDiffSectionFieldPrefix) + fieldName); - writeSubNodeHelper(child, &childBuilder); +private: + const ArrayNode& _node; + BSONObjBuilder _bob; + decltype(ArrayNode::children)::const_iterator _childIt; +}; + +BSONObj writeDiff(const DocumentSubDiffNode& root) { + std::stack<UniqueFrame> stack; + stack.push(std::make_unique<DocumentSubDiffFrame>(root, BSONObjBuilder{})); + + // Iterate until the stack size is one and there is no more work to be done. + while (true) { + auto nextFrame = stack.top()->execute(); + if (nextFrame) { + stack.push(std::move(nextFrame)); + } else if (stack.size() == 1) { + break; + } else { + stack.pop(); + } } + + invariant(stack.size() == 1); + + auto& topFrame = checked_cast<DocumentSubDiffFrame&>(*stack.top()); + return topFrame.bob().obj(); } -void writeSubNodeHelper(InternalNode* node, BSONObjBuilder* builder) { +UniqueFrame makeSubNodeFrameHelper(InternalNode* node, BSONObjBuilder builder) { if (node->type() == NodeType::kArray) { - writeArrayDiff(*checked_cast<ArrayNode*>(node), builder); + return std::make_unique<ArrayFrame>(*checked_cast<ArrayNode*>(node), std::move(builder)); } else { - invariant(node->type() == NodeType::kDocumentSubDiff || - node->type() == NodeType::kDocumentInsert); - writeDocumentDiff(*checked_cast<DocumentNode*>(node), builder); + // We never expect to see a DocumentInsertionNode under the 'subDiffs' section of an + // internal node. + invariant(node->type() == NodeType::kDocumentSubDiff); + return std::make_unique<DocumentSubDiffFrame>(*checked_cast<DocumentSubDiffNode*>(node), + std::move(builder)); } } -} // namespace +UniqueFrame handleInsertHelper(StringData fieldName, Node* node, BSONObjBuilder* bob) { + if (node->type() == NodeType::kInsert) { + appendElementToBuilder(checked_cast<InsertNode*>(node)->elt, fieldName, bob); + return nullptr; + } + invariant(node->type() == NodeType::kDocumentInsert); + return std::make_unique<DocumentInsertFrame>(*checked_cast<DocumentInsertionNode*>(node), + BSONObjBuilder(bob->subobjStart(fieldName))); +} +} // namespace BSONObj V2LogBuilder::serialize() const { - BSONObjBuilder topBuilder; - writeDocumentDiff(_root, &topBuilder); - return update_oplog_entry::makeDeltaOplogEntry(topBuilder.obj()); + auto diff = writeDiff(_root); + return update_oplog_entry::makeDeltaOplogEntry(diff); } -} // namespace mongo::v2_log_builder
\ No newline at end of file +} // namespace mongo::v2_log_builder diff --git a/src/mongo/db/update/v2_log_builder.h b/src/mongo/db/update/v2_log_builder.h index d0553cb54b3..a26d7b596eb 100644 --- a/src/mongo/db/update/v2_log_builder.h +++ b/src/mongo/db/update/v2_log_builder.h @@ -59,9 +59,9 @@ struct Node { * 'DocumentInsertionNode' also repesent an insert for the cases where an object is created * implicity. */ -struct InsertElement : public Node { - InsertElement(mutablebson::Element el) : elt(el) {} - InsertElement(BSONElement el) : elt(el) {} +struct InsertNode : public Node { + InsertNode(mutablebson::Element el) : elt(el) {} + InsertNode(BSONElement el) : elt(el) {} NodeType type() const override { return NodeType::kInsert; |