diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-18 17:02:53 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-18 17:02:53 -0300 |
| commit | 959575a5ca598bf5f37fb5cebe7ed1d80d3d71f7 (patch) | |
| tree | acc8d60aedb12b70048e676e8a7349deb0010db8 /src/mongo/db/update | |
| parent | 76588293975fc059cf076779e4283e6ffaf8afff (diff) | |
New upstream version 6.0.20upstream
Diffstat (limited to 'src/mongo/db/update')
| -rw-r--r-- | src/mongo/db/update/addtoset_node.cpp | 5 | ||||
| -rw-r--r-- | src/mongo/db/update/addtoset_node_test.cpp | 17 | ||||
| -rw-r--r-- | src/mongo/db/update/document_diff_applier.cpp | 4 | ||||
| -rw-r--r-- | src/mongo/db/update/document_diff_calculator_test.cpp | 11 | ||||
| -rw-r--r-- | src/mongo/db/update/object_replace_executor.cpp | 17 | ||||
| -rw-r--r-- | src/mongo/db/update/object_replace_executor.h | 9 | ||||
| -rw-r--r-- | src/mongo/db/update/update_driver.cpp | 9 | ||||
| -rw-r--r-- | src/mongo/db/update/update_driver.h | 9 | ||||
| -rw-r--r-- | src/mongo/db/update/update_driver_test.cpp | 9 |
9 files changed, 63 insertions, 27 deletions
diff --git a/src/mongo/db/update/addtoset_node.cpp b/src/mongo/db/update/addtoset_node.cpp index f7b821815d1..824f6943c8e 100644 --- a/src/mongo/db/update/addtoset_node.cpp +++ b/src/mongo/db/update/addtoset_node.cpp @@ -85,7 +85,10 @@ Status AddToSetNode::init(BSONElement modExpr, str::stream() << "Found unexpected fields after $each in $addToSet: " << modExpr.Obj()); } - _elements = firstElement.Array(); + + // We call 'ArrayVerifyIndexes' to uassert in the event that 'firstElement' is a + // BSONArray with invalid indexes. + _elements = firstElement.ArrayVerifyIndexes(); } } diff --git a/src/mongo/db/update/addtoset_node_test.cpp b/src/mongo/db/update/addtoset_node_test.cpp index d2b7f90be65..f590c7140dc 100644 --- a/src/mongo/db/update/addtoset_node_test.cpp +++ b/src/mongo/db/update/addtoset_node_test.cpp @@ -99,6 +99,23 @@ TEST(AddToSetNodeTest, InitSucceedsWithArray) { ASSERT_OK(node.init(update["$addToSet"]["a"], expCtx)); } +TEST(AddToSetNodeTest, InitFailsWhenArgumentIsInvalidBSONArray) { + // Create our invalid array by creating a BSONObj with non contiguous array indexes that is then + // passed to the BSONArray ctor. + BSONObj updateArrAsObj = BSON("0" + << "foo" + << "2" + << "bar"); + BSONArray updateArr(updateArrAsObj); + + auto update = BSON("$addToSet" << BSON("fieldName" << BSON("$each" << updateArr))); + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + AddToSetNode node; + + ASSERT_THROWS(node.init(update["$addToSet"]["fieldName"], expCtx), + ExceptionFor<ErrorCodes::BadValue>); +} + TEST(AddToSetNodeTest, InitSucceedsWithScaler) { auto update = fromjson("{$addToSet: {a: 1}}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); diff --git a/src/mongo/db/update/document_diff_applier.cpp b/src/mongo/db/update/document_diff_applier.cpp index ec592ef3363..7620a756064 100644 --- a/src/mongo/db/update/document_diff_applier.cpp +++ b/src/mongo/db/update/document_diff_applier.cpp @@ -455,7 +455,7 @@ int32_t computeDamageOnArray(const BSONObj& preImageRoot, nextMod = reader->next(); } else { // This field is not mentioned in the diff so we pad the post image with null. - auto idxAsStr = std::to_string(idx); + const auto idxAsStr = std::to_string(idx); // The end of 'arrayPreImage' with the offset from the updates made already. auto targetOffset = targetOffsetInPostImage( arrayPreImage.end()->rawdata(), preImageRoot.objdata(), offsetRoot, diffSize); @@ -464,7 +464,7 @@ int32_t computeDamageOnArray(const BSONObj& preImageRoot, appendDamage(damages, bufBuilder->len(), sourceSize, targetOffset, 0); diffSize += sourceSize; appendTypeByte(bufBuilder, BSONType::jstNULL); - bufBuilder->appendStr(idxAsStr); + bufBuilder->appendCStr(idxAsStr); } } diff --git a/src/mongo/db/update/document_diff_calculator_test.cpp b/src/mongo/db/update/document_diff_calculator_test.cpp index 55a05051021..5c83ac027c8 100644 --- a/src/mongo/db/update/document_diff_calculator_test.cpp +++ b/src/mongo/db/update/document_diff_calculator_test.cpp @@ -32,6 +32,7 @@ #include <functional> #include "mongo/bson/bson_depth.h" +#include "mongo/bson/bson_validate.h" #include "mongo/bson/json.h" #include "mongo/db/update/document_diff_calculator.h" #include "mongo/unittest/unittest.h" @@ -295,7 +296,7 @@ TEST(DocumentDiffCalculatorTest, DeeplyNestObjectGenerateDiff) { preBob.append("largeField", largeValue); buildDeepObj(&preBob, "subObj", 0, maxDepth, functionToApply); auto preObj = preBob.done(); - ASSERT(preObj.valid()); + ASSERT(validateBSON(preObj).isOK()); BSONObjBuilder postBob; postBob.append("largeField", largeValue); @@ -312,7 +313,7 @@ TEST(DocumentDiffCalculatorTest, DeeplyNestObjectGenerateDiff) { // Deleting the deepest field should give the post object. diffOutput = doc_diff::computeDiff(preObj, postBob2.done(), 0, nullptr); ASSERT(diffOutput); - ASSERT(diffOutput->diff.valid()); + ASSERT_OK(validateBSON(diffOutput->diff)); BSONObjBuilder expectedOutputBuilder; buildDeepObj(&expectedOutputBuilder, @@ -341,17 +342,17 @@ TEST(DocumentDiffCalculatorTest, DeepestObjectSubDiff) { value = 1; buildDeepObj(&bob1, "subObj", 0, BSONDepth::getMaxDepthForUserStorage(), functionToApply); auto preObj = bob1.done(); - ASSERT(preObj.valid()); + ASSERT_OK(validateBSON(preObj)); BSONObjBuilder postBob; value = 2; buildDeepObj(&postBob, "subObj", 0, BSONDepth::getMaxDepthForUserStorage(), functionToApply); auto postObj = postBob.done(); - ASSERT(postObj.valid()); + ASSERT_OK(validateBSON(postObj)); auto diffOutput = doc_diff::computeDiff(preObj, postObj, 0, nullptr); ASSERT(diffOutput); - ASSERT(diffOutput->diff.valid()); + ASSERT_OK(validateBSON(diffOutput->diff)); BSONObjBuilder expectedOutputBuilder; buildDeepObj(&expectedOutputBuilder, diff --git a/src/mongo/db/update/object_replace_executor.cpp b/src/mongo/db/update/object_replace_executor.cpp index e2d9262e001..205b270a8ca 100644 --- a/src/mongo/db/update/object_replace_executor.cpp +++ b/src/mongo/db/update/object_replace_executor.cpp @@ -45,19 +45,24 @@ namespace { constexpr StringData kIdFieldName = "_id"_sd; } // namespace -ObjectReplaceExecutor::ObjectReplaceExecutor(BSONObj replacement) - : _replacementDoc(replacement.getOwned()), _containsId(false) { - - // Replace all zero-valued timestamps with the current time and check for the existence of _id. +ObjectReplaceExecutor::ObjectReplaceExecutor(BSONObj replacement, bool bypassEmptyTsReplacement) + : _replacementDoc(replacement.getOwned()), + _containsId(false), + _bypassEmptyTsReplacement(bypassEmptyTsReplacement) { + // Check for the existence of the "_id" field, and if approrpriate replace all zero-valued + // timestamps with the current time. for (auto&& elem : _replacementDoc) { - // Do not change the _id field. if (elem.fieldNameStringData() == kIdFieldName) { _containsId = true; continue; } - if (elem.type() == BSONType::bsonTimestamp) { + // For updates that originated from the oplog, we're required to apply the update + // exactly as it was recorded (even if it contains zero-valued timestamps). Therefore, + // we should only replace zero-valued timestamps with the current time when + // '_bypassEmptyTsReplacement' is false. + if (!_bypassEmptyTsReplacement && elem.type() == BSONType::bsonTimestamp) { auto timestampView = DataView(const_cast<char*>(elem.value())); // We don't need to do an endian-safe read here, because 0 is 0 either way. diff --git a/src/mongo/db/update/object_replace_executor.h b/src/mongo/db/update/object_replace_executor.h index f9c70bde919..3f4c787e588 100644 --- a/src/mongo/db/update/object_replace_executor.h +++ b/src/mongo/db/update/object_replace_executor.h @@ -65,10 +65,11 @@ public: bool allowTopLevelDollarPrefixedFields = false); /** - * Initializes the node with the document to replace with. Any zero-valued timestamps (except - * for the _id) are updated to the current time. + * Initializes the node with the document to replace with. If 'bypassEmptyTsReplacement' is + * false, any zero-valued timestamps (except for the _id) will be replaced with the current + * time. */ - explicit ObjectReplaceExecutor(BSONObj replacement); + explicit ObjectReplaceExecutor(BSONObj replacement, bool bypassEmptyTsReplacement = false); /** * Replaces the document that 'applyParams.element' belongs to with 'val'. If 'val' does not @@ -95,6 +96,8 @@ private: // True if '_replacementDoc' contains an _id. bool _containsId; + + bool _bypassEmptyTsReplacement = false; }; } // namespace mongo diff --git a/src/mongo/db/update/update_driver.cpp b/src/mongo/db/update/update_driver.cpp index 5ce9dc8aa84..9839487da9d 100644 --- a/src/mongo/db/update/update_driver.cpp +++ b/src/mongo/db/update/update_driver.cpp @@ -159,7 +159,14 @@ void UpdateDriver::parse( "multi update is not supported for replacement-style update", !multi); - _updateExecutor = std::make_unique<ObjectReplaceExecutor>(updateMod.getUpdateReplacement()); + // For updates that originated from the oplog, we're required to apply the update + // exactly as it was recorded (even if it contains zero-valued timestamps). Therefore, + // we should only replace zero-valued timestamps with the current time when both + // '_bypassEmptyTsReplacement' and '_fromOplogApplication' are false. + const bool bypassEmptyTsReplacement = _bypassEmptyTsReplacement || _fromOplogApplication; + + _updateExecutor = std::make_unique<ObjectReplaceExecutor>(updateMod.getUpdateReplacement(), + bypassEmptyTsReplacement); // Register the fact that this driver will only do full object replacements. _updateType = UpdateType::kReplacement; diff --git a/src/mongo/db/update/update_driver.h b/src/mongo/db/update/update_driver.h index 94d1f73c8d4..9949f43787c 100644 --- a/src/mongo/db/update/update_driver.h +++ b/src/mongo/db/update/update_driver.h @@ -195,6 +195,13 @@ public: _containsDotsAndDollarsField = containsDotsAndDollarsField; } + bool bypassEmptyTsReplacement() const { + return _bypassEmptyTsReplacement; + } + void setBypassEmptyTsReplacement(bool bypassEmptyTsReplacement) { + _bypassEmptyTsReplacement = bypassEmptyTsReplacement; + } + /** * Serialize the update expression to Value. Output of this method is expected to, when parsed, * produce a logically equivalent update expression. @@ -238,6 +245,8 @@ private: // True if this update comes from an oplog application. bool _fromOplogApplication = false; + bool _bypassEmptyTsReplacement = false; + // True if this update is guaranteed not to contain dots or dollars fields and should skip the // check. bool _skipDotsDollarsCheck = false; diff --git a/src/mongo/db/update/update_driver_test.cpp b/src/mongo/db/update/update_driver_test.cpp index 2d03e4b7886..86f6ae492bf 100644 --- a/src/mongo/db/update/update_driver_test.cpp +++ b/src/mongo/db/update/update_driver_test.cpp @@ -46,15 +46,6 @@ #include "mongo/db/update_index_data.h" #include "mongo/unittest/unittest.h" -#define ASSERT_DOES_NOT_THROW(EXPRESSION) \ - try { \ - EXPRESSION; \ - } catch (const AssertionException& e) { \ - ::mongo::str::stream err; \ - err << "Threw an exception incorrectly: " << e.toString(); \ - ::mongo::unittest::TestAssertionFailure(__FILE__, __LINE__, err).stream(); \ - } - namespace mongo { namespace { |
