summaryrefslogtreecommitdiff
path: root/src/mongo/db/update
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-18 17:02:53 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-18 17:02:53 -0300
commit959575a5ca598bf5f37fb5cebe7ed1d80d3d71f7 (patch)
treeacc8d60aedb12b70048e676e8a7349deb0010db8 /src/mongo/db/update
parent76588293975fc059cf076779e4283e6ffaf8afff (diff)
New upstream version 6.0.20upstream
Diffstat (limited to 'src/mongo/db/update')
-rw-r--r--src/mongo/db/update/addtoset_node.cpp5
-rw-r--r--src/mongo/db/update/addtoset_node_test.cpp17
-rw-r--r--src/mongo/db/update/document_diff_applier.cpp4
-rw-r--r--src/mongo/db/update/document_diff_calculator_test.cpp11
-rw-r--r--src/mongo/db/update/object_replace_executor.cpp17
-rw-r--r--src/mongo/db/update/object_replace_executor.h9
-rw-r--r--src/mongo/db/update/update_driver.cpp9
-rw-r--r--src/mongo/db/update/update_driver.h9
-rw-r--r--src/mongo/db/update/update_driver_test.cpp9
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 {