summaryrefslogtreecommitdiff
path: root/src/mongo/db/update
diff options
context:
space:
mode:
authorJustin Seyster <justin.seyster@mongodb.com>2017-12-14 15:21:10 -0500
committerJustin Seyster <justin.seyster@mongodb.com>2017-12-14 15:37:19 -0500
commit3b116b0dc632a0533c8b76ddbf02186e4bf6774e (patch)
tree2f57de2f0df1acac0e0c9cae85b492b7b8ec3216 /src/mongo/db/update
parentc0ebce4abf9b0cfd4271767fa062ea2d5b486554 (diff)
downloadmongo-3b116b0dc632a0533c8b76ddbf02186e4bf6774e.tar.gz
SERVER-30854 Remove ModifierInterface update code.
We left the deleted update system in 3.6 to support upgrades from 3.4, but newer versions will always use the new UpdateNode update system. Fun fact: this commit deletes more lines than were inserted by the previous 100 commits.
Diffstat (limited to 'src/mongo/db/update')
-rw-r--r--src/mongo/db/update/SConscript3
-rw-r--r--src/mongo/db/update/log_builder.h13
-rw-r--r--src/mongo/db/update/modifier_table.cpp49
-rw-r--r--src/mongo/db/update/modifier_table.h7
-rw-r--r--src/mongo/db/update/modifier_table_test.cpp12
-rw-r--r--src/mongo/db/update/storage_validation.cpp20
-rw-r--r--src/mongo/db/update/storage_validation.h8
-rw-r--r--src/mongo/db/update/update_driver.cpp356
-rw-r--r--src/mongo/db/update/update_driver.h68
-rw-r--r--src/mongo/db/update/update_driver_test.cpp51
10 files changed, 84 insertions, 503 deletions
diff --git a/src/mongo/db/update/SConscript b/src/mongo/db/update/SConscript
index 082ae597b66..37895498200 100644
--- a/src/mongo/db/update/SConscript
+++ b/src/mongo/db/update/SConscript
@@ -90,7 +90,7 @@ env.Library(
'update_object_node.cpp',
],
LIBDEPS=[
- '$BUILD_DIR/mongo/db/ops/update',
+ '$BUILD_DIR/mongo/db/logical_clock',
'$BUILD_DIR/mongo/db/update_index_data',
'update_common',
],
@@ -141,7 +141,6 @@ env.Library(
LIBDEPS=[
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/db/common',
- '$BUILD_DIR/mongo/db/ops/update',
'$BUILD_DIR/mongo/db/server_options_core',
'$BUILD_DIR/mongo/db/query/query_planner',
'update',
diff --git a/src/mongo/db/update/log_builder.h b/src/mongo/db/update/log_builder.h
index 9d7b1954365..60287ecfa92 100644
--- a/src/mongo/db/update/log_builder.h
+++ b/src/mongo/db/update/log_builder.h
@@ -34,17 +34,12 @@
namespace mongo {
/**
- * There are two update subsystems in MongoDB with slightly different semantics.
+ * Previously, there were multiple supported versions of the update language.
*/
enum class UpdateSemantics {
- // The update system that was in use up until v3.4, which is implemented in ModifierInterface
- // and its subclasses. When a single update adds multiple fields, those fields are added in the
- // same order as they are specified in the update document.
- kModifierInterface = 0,
-
- // The update system introduced in v3.6, which is implemented in UpdateNode and its subclassees.
- // When a single update adds multiple fields, those fields are added in lexicographic order by
- // field name. This system introduces support for arrayFilters and $[] syntax.
+ // The update system introduced in v3.6, and is the only supported system. When a single update
+ // adds multiple fields, those fields are added in lexicographic order by field name. This
+ // system introduces support for arrayFilters and $[] syntax.
kUpdateNode = 1,
// Must be last.
diff --git a/src/mongo/db/update/modifier_table.cpp b/src/mongo/db/update/modifier_table.cpp
index da5a8e9d8e0..95dfe4146f1 100644
--- a/src/mongo/db/update/modifier_table.cpp
+++ b/src/mongo/db/update/modifier_table.cpp
@@ -34,18 +34,6 @@
#include "mongo/base/init.h"
#include "mongo/base/simple_string_data_comparator.h"
#include "mongo/base/status.h"
-#include "mongo/db/ops/modifier_add_to_set.h"
-#include "mongo/db/ops/modifier_bit.h"
-#include "mongo/db/ops/modifier_compare.h"
-#include "mongo/db/ops/modifier_current_date.h"
-#include "mongo/db/ops/modifier_inc.h"
-#include "mongo/db/ops/modifier_pop.h"
-#include "mongo/db/ops/modifier_pull.h"
-#include "mongo/db/ops/modifier_pull_all.h"
-#include "mongo/db/ops/modifier_push.h"
-#include "mongo/db/ops/modifier_rename.h"
-#include "mongo/db/ops/modifier_set.h"
-#include "mongo/db/ops/modifier_unset.h"
#include "mongo/db/update/addtoset_node.h"
#include "mongo/db/update/arithmetic_node.h"
#include "mongo/db/update/bit_node.h"
@@ -147,43 +135,6 @@ ModifierType getType(StringData typeStr) {
return it->second->type;
}
-ModifierInterface* makeUpdateMod(ModifierType modType) {
- switch (modType) {
- case MOD_ADD_TO_SET:
- return new ModifierAddToSet;
- case MOD_BIT:
- return new ModifierBit;
- case MOD_CURRENTDATE:
- return new ModifierCurrentDate;
- case MOD_INC:
- return new ModifierInc(ModifierInc::MODE_INC);
- case MOD_MAX:
- return new ModifierCompare(ModifierCompare::MAX);
- case MOD_MIN:
- return new ModifierCompare(ModifierCompare::MIN);
- case MOD_MUL:
- return new ModifierInc(ModifierInc::MODE_MUL);
- case MOD_POP:
- return new ModifierPop;
- case MOD_PULL:
- return new ModifierPull;
- case MOD_PULL_ALL:
- return new ModifierPullAll;
- case MOD_PUSH:
- return new ModifierPush;
- case MOD_SET:
- return new ModifierSet(ModifierSet::SET_NORMAL);
- case MOD_SET_ON_INSERT:
- return new ModifierSet(ModifierSet::SET_ON_INSERT);
- case MOD_RENAME:
- return new ModifierRename;
- case MOD_UNSET:
- return new ModifierUnset;
- default:
- return NULL;
- }
-}
-
std::unique_ptr<UpdateLeafNode> makeUpdateLeafNode(ModifierType modType) {
switch (modType) {
case MOD_ADD_TO_SET:
diff --git a/src/mongo/db/update/modifier_table.h b/src/mongo/db/update/modifier_table.h
index 048c58b4fb8..86b43620586 100644
--- a/src/mongo/db/update/modifier_table.h
+++ b/src/mongo/db/update/modifier_table.h
@@ -28,7 +28,6 @@
#pragma once
-#include "mongo/db/ops/modifier_interface.h"
#include "mongo/db/update/update_leaf_node.h"
namespace mongo {
@@ -62,12 +61,6 @@ enum ModifierType {
ModifierType getType(StringData typeStr);
/**
- * Instantiate an update mod that corresponds to 'modType' or NULL if 'modType' is not
- * valid. The ownership of the new object is the caller's.
- */
-ModifierInterface* makeUpdateMod(ModifierType modType);
-
-/**
* Instantiate an UpdateLeafNode that corresponds to 'modType' or nullptr if 'modType' is not valid.
*/
std::unique_ptr<UpdateLeafNode> makeUpdateLeafNode(ModifierType modType);
diff --git a/src/mongo/db/update/modifier_table_test.cpp b/src/mongo/db/update/modifier_table_test.cpp
index ca3f82cc1c5..c2e168134dd 100644
--- a/src/mongo/db/update/modifier_table_test.cpp
+++ b/src/mongo/db/update/modifier_table_test.cpp
@@ -30,14 +30,12 @@
#include <memory>
-#include "mongo/db/ops/modifier_interface.h"
#include "mongo/unittest/unittest.h"
namespace {
using namespace mongo::modifiertable;
-using mongo::ModifierInterface;
using std::unique_ptr;
TEST(getType, Normal) {
@@ -46,14 +44,4 @@ TEST(getType, Normal) {
ASSERT_EQUALS(getType("NotAModExpression"), MOD_UNKNOWN);
}
-TEST(makeUpdateMod, Normal) {
- unique_ptr<ModifierInterface> mod;
-
- mod.reset(makeUpdateMod(MOD_SET));
- ASSERT_NOT_EQUALS(mod.get(), static_cast<ModifierInterface*>(0));
-
- mod.reset(makeUpdateMod(MOD_UNKNOWN));
- ASSERT_EQUALS(mod.get(), static_cast<ModifierInterface*>(0));
-}
-
} // unnamed namespace
diff --git a/src/mongo/db/update/storage_validation.cpp b/src/mongo/db/update/storage_validation.cpp
index 8b5be0d95e5..1ee11881029 100644
--- a/src/mongo/db/update/storage_validation.cpp
+++ b/src/mongo/db/update/storage_validation.cpp
@@ -164,25 +164,5 @@ void storageValid(mutablebson::ConstElement elem, const bool deep, std::uint32_t
}
}
-std::uint32_t storageValidParents(mutablebson::ConstElement elem, std::uint32_t recursionLevel) {
- uassert(ErrorCodes::Overflow,
- str::stream() << "Document exceeds maximum nesting depth of "
- << BSONDepth::getMaxDepthForUserStorage(),
- recursionLevel <= BSONDepth::getMaxDepthForUserStorage());
-
- auto root = elem.getDocument().root();
- if (elem != root) {
- auto parent = elem.parent();
- if (parent.ok() && parent != root) {
- const bool doRecursiveCheck = false;
- const uint32_t parentsRecursionLevel = 0;
- storageValid(parent, doRecursiveCheck, parentsRecursionLevel);
- return storageValidParents(parent, recursionLevel + 1);
- }
- return recursionLevel + 1;
- }
- return recursionLevel;
-}
-
} // namespace storage_validation
} // namespace mongo
diff --git a/src/mongo/db/update/storage_validation.h b/src/mongo/db/update/storage_validation.h
index d47197e8d17..33f0a514a77 100644
--- a/src/mongo/db/update/storage_validation.h
+++ b/src/mongo/db/update/storage_validation.h
@@ -48,14 +48,6 @@ void storageValid(const mutablebson::Document& doc);
*/
void storageValid(mutablebson::ConstElement elem, const bool deep, std::uint32_t recursionLevel);
-/**
- * Checks that all of the parents of the MutableBSON element 'elem' are valid for storage. Note that
- * 'elem' must be in a valid state when using this function. Uasserts if the validation fails, or if
- * 'recursionLevel' exceeds the maximum allowable depth. On success, an integer is returned that
- * represents the number of steps from this element to the root through ancestor nodes.
- */
-std::uint32_t storageValidParents(mutablebson::ConstElement elem, std::uint32_t recursionLevel);
-
} // namespace storage_validation
} // namespace mongo
diff --git a/src/mongo/db/update/update_driver.cpp b/src/mongo/db/update/update_driver.cpp
index 88d80341981..ee480f5e5d1 100644
--- a/src/mongo/db/update/update_driver.cpp
+++ b/src/mongo/db/update/update_driver.cpp
@@ -67,9 +67,9 @@ StatusWith<UpdateSemantics> updateSemanticsFromElement(BSONElement element) {
auto updateSemantics = element.numberLong();
- if (updateSemantics < 0 ||
- updateSemantics >= static_cast<int>(UpdateSemantics::kNumUpdateSemantics)) {
- return {ErrorCodes::BadValue,
+ // As of 3.7, we only support one version of the update language.
+ if (updateSemantics != static_cast<int>(UpdateSemantics::kUpdateNode)) {
+ return {ErrorCodes::Error(50659),
str::stream() << "Unrecognized value for '$v' (UpdateSemantics) field: "
<< updateSemantics};
}
@@ -147,23 +147,14 @@ bool parseUpdateExpression(
} // namespace
-UpdateDriver::UpdateDriver(const Options& opts)
- : _replacementMode(false),
- _indexedFields(NULL),
- _logOp(opts.logOp),
- _modOptions(opts.modOptions),
- _affectIndices(false),
- _positional(false) {}
-
-UpdateDriver::~UpdateDriver() {
- clear();
-}
+UpdateDriver::UpdateDriver(const boost::intrusive_ptr<ExpressionContext>& expCtx)
+ : _expCtx(expCtx) {}
Status UpdateDriver::parse(
const BSONObj& updateExpr,
const std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>>& arrayFilters,
const bool multi) {
- clear();
+ invariant(!_root && !_replacementMode, "Multiple calls to parse() on same UpdateDriver");
// Check if the update expression is a full object replacement.
if (isDocReplacement(updateExpr)) {
@@ -182,99 +173,24 @@ Status UpdateDriver::parse(
// Register the fact that this driver is not doing a full object replacement.
_replacementMode = false;
- // Decide which update semantics to used, using the criteria outlined in the comment above this
- // function's declaration.
+ // Some versions of mongod support more than one version of the update language and look for a
+ // $v "UpdateSemantics" field when applying an oplog entry, in order to know which version of
+ // the update language to apply with. We currently only support the 'kUpdateNode' version, but
+ // we parse $v and check its value for compatibility.
BSONElement updateSemanticsElement = updateExpr[LogBuilder::kUpdateSemanticsFieldName];
- UpdateSemantics updateSemantics;
if (updateSemanticsElement) {
- if (!_modOptions.fromOplogApplication) {
+ if (!_fromOplogApplication) {
return {ErrorCodes::FailedToParse, "The $v update field is only recognized internally"};
}
auto statusWithUpdateSemantics = updateSemanticsFromElement(updateSemanticsElement);
if (!statusWithUpdateSemantics.isOK()) {
return statusWithUpdateSemantics.getStatus();
}
-
- updateSemantics = statusWithUpdateSemantics.getValue();
- } else if (_modOptions.fromOplogApplication) {
- updateSemantics = UpdateSemantics::kModifierInterface;
- } else {
- updateSemantics = (serverGlobalParams.featureCompatibility.getVersion() !=
- ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo36)
- ? UpdateSemantics::kModifierInterface
- : UpdateSemantics::kUpdateNode;
- }
-
- switch (updateSemantics) {
- case UpdateSemantics::kModifierInterface: {
- uassert(ErrorCodes::InvalidOptions,
- str::stream()
- << "The featureCompatibilityVersion must be 3.6 to use arrayFilters. See "
- << feature_compatibility_version::kDochubLink
- << ".",
- arrayFilters.empty());
- bool foundUpdateSemanticsField = false;
- for (auto&& mod : updateExpr) {
- // If there is a "$v" field among the modifiers, we have already used it to
- // determine that this is the correct parsing code.
- if (mod.fieldNameStringData() == LogBuilder::kUpdateSemanticsFieldName) {
- uassert(ErrorCodes::BadValue,
- "Duplicate $v in oplog update document",
- !foundUpdateSemanticsField);
- foundUpdateSemanticsField = true;
- invariant(mod.numberLong() ==
- static_cast<long long>(UpdateSemantics::kModifierInterface));
- continue;
- }
-
- auto modType = validateMod(mod);
- for (auto&& field : mod.Obj()) {
- auto status = addAndParse(modType, field);
- if (!status.isOK()) {
- return status;
- }
- }
- }
- break;
- }
- case UpdateSemantics::kUpdateNode: {
- auto root = stdx::make_unique<UpdateObjectNode>();
- _positional =
- parseUpdateExpression(updateExpr, root.get(), _modOptions.expCtx, arrayFilters);
- _root = std::move(root);
- break;
- }
- default:
- MONGO_UNREACHABLE;
- }
-
- return Status::OK();
-}
-
-inline Status UpdateDriver::addAndParse(const modifiertable::ModifierType type,
- const BSONElement& elem) {
- if (elem.eoo()) {
- return Status(ErrorCodes::FailedToParse,
- str::stream() << "'" << elem.fieldName() << "' has no value in : " << elem
- << " which is not allowed for any $"
- << type
- << " mod.");
}
- unique_ptr<ModifierInterface> mod(modifiertable::makeUpdateMod(type));
- dassert(mod.get());
-
- bool positional = false;
- Status status = mod->init(elem, _modOptions, &positional);
- if (!status.isOK()) {
- return status;
- }
-
- // If any modifier indicates that it requires a positional match, toggle the
- // _positional flag to true.
- _positional = _positional || positional;
-
- _mods.push_back(mod.release());
+ auto root = stdx::make_unique<UpdateObjectNode>();
+ _positional = parseUpdateExpression(updateExpr, root.get(), _expCtx, arrayFilters);
+ _root = std::move(root);
return Status::OK();
}
@@ -330,7 +246,6 @@ Status UpdateDriver::populateDocumentWithQueryFields(const CanonicalQuery& query
}
Status UpdateDriver::update(StringData matchedField,
- BSONObj original,
mutablebson::Document* doc,
bool validateForStorage,
const FieldRefSet& immutablePaths,
@@ -343,192 +258,35 @@ Status UpdateDriver::update(StringData matchedField,
_logDoc.reset();
LogBuilder logBuilder(_logDoc.root());
- if (_root) {
-
- // We parsed using the new UpdateNode implementation.
- UpdateNode::ApplyParams applyParams(doc->root(), immutablePaths);
- applyParams.matchedField = matchedField;
- applyParams.insert = _insert;
- applyParams.fromOplogApplication = _modOptions.fromOplogApplication;
- applyParams.validateForStorage = validateForStorage;
- applyParams.indexData = _indexedFields;
- if (_logOp && logOpRec) {
- applyParams.logBuilder = &logBuilder;
- }
- auto applyResult = _root->apply(applyParams);
- if (applyResult.indexesAffected) {
- _affectIndices = true;
- doc->disableInPlaceUpdates();
- }
- if (docWasModified) {
- *docWasModified = !applyResult.noop;
- }
- if (!_replacementMode && _logOp && logOpRec) {
- // When using kUpdateNode update semantics on the primary, we must include a "$v" field
- // in the update document so that the secondary knows to apply the update with
- // kUpdateNode semantics. If this is a full document replacement, we don't need to
- // specify the semantics (and there would be no place to put a "$v" field in the update
- // document).
- invariantOK(logBuilder.setUpdateSemantics(UpdateSemantics::kUpdateNode));
- }
-
- } else {
-
- // We parsed using the old ModifierInterface implementation.
- // Ask each of the mods to type check whether they can operate over the current document
- // and, if so, to change that document accordingly.
- FieldRefSet updatedPaths;
-
- for (vector<ModifierInterface*>::iterator it = _mods.begin(); it != _mods.end(); ++it) {
- ModifierInterface::ExecInfo execInfo;
- Status status = (*it)->prepare(doc->root(), matchedField, &execInfo);
- if (!status.isOK()) {
- return status;
- }
-
- if (execInfo.context == ModifierInterface::ExecInfo::INSERT_CONTEXT && !_insert) {
- continue;
- }
-
-
- // Gather which fields this mod is interested on and whether these fields were
- // "taken" by previous mods. Note that not all mods are multi-field mods. When we
- // see an empty field, we may stop looking for others.
- for (int i = 0; i < ModifierInterface::ExecInfo::MAX_NUM_FIELDS; i++) {
- if (execInfo.fieldRef[i] == 0) {
- break;
- }
-
- // Record each field being updated but check for conflicts first
- const FieldRef* other;
- if (!updatedPaths.insert(execInfo.fieldRef[i], &other)) {
- return Status(ErrorCodes::ConflictingUpdateOperators,
- str::stream() << "Cannot update '" << other->dottedField()
- << "' and '"
- << execInfo.fieldRef[i]->dottedField()
- << "' at the same time");
- }
-
- // We start with the expectation that a mod will be in-place. But if the mod
- // touched an indexed field and the mod will indeed be executed -- that is, it
- // is not a no-op and it is in a valid context -- then we switch back to a
- // non-in-place mode.
- //
- // To determine if indexes are affected: If we did not create a new element in an
- // array, check whether the full path affects indexes. If we did create a new
- // element in an array, check whether the array itself might affect any indexes.
- // This is necessary because if there is an index {"a.b": 1}, and we set "a.1.c" and
- // implicitly create an array element in "a", then we may need to add a null key to
- // the index {"a.b": 1}, even though "a.1.c" does not appear to affect the index.
- if (!_affectIndices && !execInfo.noOp && _indexedFields) {
- auto pathLengthForIndexCheck = execInfo.indexOfArrayWithNewElement[i]
- ? *execInfo.indexOfArrayWithNewElement[i] + 1
- : execInfo.fieldRef[i]->numParts();
- if (_indexedFields->mightBeIndexed(
- execInfo.fieldRef[i]->dottedSubstring(0, pathLengthForIndexCheck))) {
- _affectIndices = true;
- doc->disableInPlaceUpdates();
- }
- }
- }
-
- if (!execInfo.noOp) {
- status = (*it)->apply();
-
- if (docWasModified)
- *docWasModified = true;
-
- if (!status.isOK()) {
- return status;
- }
- }
-
- // If we require a replication oplog entry for this update, go ahead and generate one.
- if (!execInfo.noOp && _logOp && logOpRec) {
- status = (*it)->log(&logBuilder);
- if (!status.isOK()) {
- return status;
- }
- }
- }
-
- // Check for BSON depth and DBRef constraint violations.
- if (validateForStorage) {
- for (auto path = updatedPaths.begin(); path != updatedPaths.end(); ++path) {
-
- // Find the updated field in the updated document.
- auto newElem = doc->root();
- for (size_t i = 0; i < (*path)->numParts(); ++i) {
- if (newElem.getType() == BSONType::Array) {
- auto indexFromField = parseUnsignedBase10Integer((*path)->getPart(i));
- if (indexFromField) {
- newElem = newElem.findNthChild(*indexFromField);
- } else {
- newElem = newElem.getDocument().end();
- }
- } else {
- newElem = newElem[(*path)->getPart(i)];
- }
-
- if (!newElem.ok()) {
- break;
- }
- }
-
- // newElem might be missing if $unset/$renamed-away.
- if (newElem.ok()) {
-
- // Check parents.
- const std::uint32_t recursionLevel = 0;
- auto parentsDepth =
- storage_validation::storageValidParents(newElem, recursionLevel);
-
- // Check element and its children.
- const bool doRecursiveCheck = true;
- storage_validation::storageValid(newElem, doRecursiveCheck, parentsDepth);
- }
- }
- }
-
- for (auto path = immutablePaths.begin(); path != immutablePaths.end(); ++path) {
-
- if (!updatedPaths.findConflicts(*path, nullptr)) {
- continue;
- }
-
- // Find the updated field in the updated document.
- auto newElem = doc->root();
- for (size_t i = 0; i < (*path)->numParts(); ++i) {
- newElem = newElem[(*path)->getPart(i)];
- if (!newElem.ok()) {
- break;
- }
- uassert(ErrorCodes::NotSingleValueField,
- str::stream()
- << "After applying the update to the document, the (immutable) field '"
- << (*path)->dottedField()
- << "' was found to be an array or array descendant.",
- newElem.getType() != BSONType::Array);
- }
-
- auto oldElem =
- dotted_path_support::extractElementAtPath(original, (*path)->dottedField());
-
- uassert(ErrorCodes::ImmutableField,
- str::stream() << "After applying the update, the '" << (*path)->dottedField()
- << "' (required and immutable) field was "
- "found to have been removed --"
- << original,
- newElem.ok() || !oldElem.ok());
- if (newElem.ok() && oldElem.ok()) {
- uassert(ErrorCodes::ImmutableField,
- str::stream() << "After applying the update, the (immutable) field '"
- << (*path)->dottedField()
- << "' was found to have been altered to "
- << newElem.toString(),
- newElem.compareWithBSONElement(oldElem, nullptr, false) == 0);
- }
- }
+ UpdateNode::ApplyParams applyParams(doc->root(), immutablePaths);
+ applyParams.matchedField = matchedField;
+ applyParams.insert = _insert;
+ applyParams.fromOplogApplication = _fromOplogApplication;
+ applyParams.validateForStorage = validateForStorage;
+ applyParams.indexData = _indexedFields;
+ if (_logOp && logOpRec) {
+ applyParams.logBuilder = &logBuilder;
+ }
+ auto applyResult = _root->apply(applyParams);
+ if (applyResult.indexesAffected) {
+ _affectIndices = true;
+ doc->disableInPlaceUpdates();
+ }
+ if (docWasModified) {
+ *docWasModified = !applyResult.noop;
+ }
+ if (!_replacementMode && _logOp && logOpRec) {
+ // If there are binVersion=3.6 mongod nodes in the replica set, they need to be told that
+ // this update is using the "kUpdateNode" version of the update semantics and not the older
+ // update semantics that could be used by a featureCompatibilityVersion=3.4 node.
+ //
+ // TODO (SERVER-32240): Once binVersion <= 3.6 nodes are not supported in a replica set, we
+ // can safely elide this "$v" UpdateSemantics field from oplog entries, because there will
+ // only one supported version, which all nodes will assume is in use.
+ //
+ // We also don't need to specify the semantics for a full document replacement (and there
+ // would be no place to put a "$v" field in the update document).
+ invariantOK(logBuilder.setUpdateSemantics(UpdateSemantics::kUpdateNode));
}
if (_logOp && logOpRec)
@@ -537,10 +295,6 @@ Status UpdateDriver::update(StringData matchedField,
return Status::OK();
}
-size_t UpdateDriver::numMods() const {
- return _mods.size();
-}
-
bool UpdateDriver::isDocReplacement() const {
return _replacementMode;
}
@@ -561,12 +315,12 @@ void UpdateDriver::setLogOp(bool logOp) {
_logOp = logOp;
}
-ModifierInterface::Options UpdateDriver::modOptions() const {
- return _modOptions;
+bool UpdateDriver::fromOplogApplication() const {
+ return _fromOplogApplication;
}
-void UpdateDriver::setModOptions(ModifierInterface::Options modOpts) {
- _modOptions = modOpts;
+void UpdateDriver::setFromOplogApplication(bool fromOplogApplication) {
+ _fromOplogApplication = fromOplogApplication;
}
void UpdateDriver::setCollator(const CollatorInterface* collator) {
@@ -574,21 +328,7 @@ void UpdateDriver::setCollator(const CollatorInterface* collator) {
_root->setCollator(collator);
}
- for (auto&& mod : _mods) {
- mod->setCollator(collator);
- }
-
- _modOptions.expCtx->setCollator(collator);
-}
-
-void UpdateDriver::clear() {
- for (vector<ModifierInterface*>::iterator it = _mods.begin(); it != _mods.end(); ++it) {
- delete *it;
- }
- _mods.clear();
- _indexedFields = NULL;
- _replacementMode = false;
- _positional = false;
+ _expCtx->setCollator(collator);
}
bool UpdateDriver::isDocReplacement(const BSONObj& updateExpr) {
diff --git a/src/mongo/db/update/update_driver.h b/src/mongo/db/update/update_driver.h
index e551d5fa2e8..d754a4f58f4 100644
--- a/src/mongo/db/update/update_driver.h
+++ b/src/mongo/db/update/update_driver.h
@@ -36,7 +36,6 @@
#include "mongo/bson/mutable/document.h"
#include "mongo/db/field_ref_set.h"
#include "mongo/db/jsobj.h"
-#include "mongo/db/ops/modifier_interface.h"
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/update/modifier_table.h"
#include "mongo/db/update/update_object_node.h"
@@ -49,25 +48,11 @@ class OperationContext;
class UpdateDriver {
public:
- struct Options;
- UpdateDriver(const Options& opts);
-
- ~UpdateDriver();
+ UpdateDriver(const boost::intrusive_ptr<ExpressionContext>& expCtx);
/**
- * Parses the 'updateExpr' update expression. When parsing with the kUpdateNode update
- * semantics, 'updateExpr' is parsed into the '_root' member variable, and when parsing with the
- * kModifierInterface update semantics, 'updateExpr' is parsed into the '_mods' member variable.
- * It is important that the secondary applies updates with the same semantics that the primary
- * used.
- * - The primary can add a "$v" field with an integer value (storing on UpdateSemantics enum
- * value) that this function will use to determine which update semantics to use.
- * - When applying an oplog entry that has no "$v" field, this function assumes
- * kModifierInterface semantics.
- * - When applying an update on the primary, this function uses the feature compatibility
- * version to determine which update semantics to use.
- * Uasserts if the featureCompatibilityVersion is 3.4 and 'arrayFilters' is non-empty. Uasserts
- * or returns a non-ok status if 'updateExpr' fails to parse.
+ * Parses the 'updateExpr' update expression into the '_root' member variable. Uasserts or
+ * returns a non-ok status if 'updateExpr' fails to parse.
*/
Status parse(
const BSONObj& updateExpr,
@@ -108,11 +93,9 @@ public:
*
* If 'validateForStorage' is true, ensures that modified elements do not violate depth or DBRef
* constraints. Ensures that no paths in 'immutablePaths' are modified (though they may be
- * created, if they do not yet exist). The original document 'original' is needed for checking
- * immutable paths for the ModifierInterface implementation.
+ * created, if they do not yet exist).
*/
Status update(StringData matchedField,
- BSONObj original,
mutablebson::Document* doc,
bool validateForStorage,
const FieldRefSet& immutablePaths,
@@ -123,8 +106,6 @@ public:
// Accessors
//
- size_t numMods() const;
-
bool isDocReplacement() const;
static bool isDocReplacement(const BSONObj& updateExpr);
@@ -134,8 +115,8 @@ public:
bool logOp() const;
void setLogOp(bool logOp);
- ModifierInterface::Options modOptions() const;
- void setModOptions(ModifierInterface::Options modOpts);
+ bool fromOplogApplication() const;
+ void setFromOplogApplication(bool fromOplogApplication);
void setInsert(bool insert) {
_insert = insert;
@@ -161,9 +142,6 @@ public:
void setCollator(const CollatorInterface* collator);
private:
- /** Resets the state of the class associated with mods (not the error state) */
- void clear();
-
/** Create the modifier and add it to the back of the modifiers vector */
inline Status addAndParse(const modifiertable::ModifierType type, const BSONElement& elem);
@@ -171,39 +149,37 @@ private:
// immutable properties after parsing
//
- // Is there a list of $mod's on '_mods' or is it just full object replacement?
- bool _replacementMode;
+ // Is this a full object replacement or do we have update modifiers in the '_root' UpdateNode
+ // tree?
+ bool _replacementMode = false;
- // The root of the UpdateNode tree. If the featureCompatibilityVersion is 3.6, the update
- // expression is parsed into '_root'.
+ // The root of the UpdateNode tree.
std::unique_ptr<UpdateNode> _root;
- // Collection of update mod instances. Owned here. If the featureCompatibilityVersion is 3.4,
- // the update expression is parsed into '_mods'.
- std::vector<ModifierInterface*> _mods;
-
// What are the list of fields in the collection over which the update is going to be
// applied that participate in indices?
//
// NOTE: Owned by the collection's info cache!.
- const UpdateIndexData* _indexedFields;
+ const UpdateIndexData* _indexedFields = nullptr;
//
// mutable properties after parsing
//
// Should this driver generate an oplog record when it applies the update?
- bool _logOp;
+ bool _logOp = false;
- // The options to initiate the mods with
- ModifierInterface::Options _modOptions;
+ // True if this update comes from an oplog application.
+ bool _fromOplogApplication = false;
+
+ boost::intrusive_ptr<ExpressionContext> _expCtx;
// Are any of the fields mentioned in the mods participating in any index? Is set anew
// at each call to update.
- bool _affectIndices;
+ bool _affectIndices = false;
// Do any of the mods require positional match details when calling 'prepare'?
- bool _positional;
+ bool _positional = false;
// Is this update going to be an upsert?
bool _insert = false;
@@ -215,12 +191,4 @@ private:
mutablebson::Document _logDoc;
};
-struct UpdateDriver::Options {
- bool logOp;
- ModifierInterface::Options modOptions;
-
- explicit Options(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : logOp(false), modOptions(ModifierInterface::Options::normal(expCtx)) {}
-};
-
} // namespace mongo
diff --git a/src/mongo/db/update/update_driver_test.cpp b/src/mongo/db/update/update_driver_test.cpp
index f64c1e353d3..1911c417f0a 100644
--- a/src/mongo/db/update/update_driver_test.cpp
+++ b/src/mongo/db/update/update_driver_test.cpp
@@ -52,38 +52,31 @@ using mongoutils::str::stream;
TEST(Parse, Normal) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- UpdateDriver::Options opts(expCtx);
- UpdateDriver driver(opts);
+ UpdateDriver driver(expCtx);
std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
ASSERT_OK(driver.parse(fromjson("{$set:{a:1}}"), arrayFilters));
- ASSERT_EQUALS(driver.numMods(), 1U);
ASSERT_FALSE(driver.isDocReplacement());
}
TEST(Parse, MultiMods) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- UpdateDriver::Options opts(expCtx);
- UpdateDriver driver(opts);
+ UpdateDriver driver(expCtx);
std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
ASSERT_OK(driver.parse(fromjson("{$set:{a:1, b:1}}"), arrayFilters));
- ASSERT_EQUALS(driver.numMods(), 2U);
ASSERT_FALSE(driver.isDocReplacement());
}
TEST(Parse, MixingMods) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- UpdateDriver::Options opts(expCtx);
- UpdateDriver driver(opts);
+ UpdateDriver driver(expCtx);
std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
ASSERT_OK(driver.parse(fromjson("{$set:{a:1}, $unset:{b:1}}"), arrayFilters));
- ASSERT_EQUALS(driver.numMods(), 2U);
ASSERT_FALSE(driver.isDocReplacement());
}
TEST(Parse, ObjectReplacment) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- UpdateDriver::Options opts(expCtx);
- UpdateDriver driver(opts);
+ UpdateDriver driver(expCtx);
std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
ASSERT_OK(driver.parse(fromjson("{obj: \"obj replacement\"}"), arrayFilters));
ASSERT_TRUE(driver.isDocReplacement());
@@ -91,8 +84,7 @@ TEST(Parse, ObjectReplacment) {
TEST(Parse, EmptyMod) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- UpdateDriver::Options opts(expCtx);
- UpdateDriver driver(opts);
+ UpdateDriver driver(expCtx);
std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
ASSERT_THROWS_CODE_AND_WHAT(
driver.parse(fromjson("{$set:{}}"), arrayFilters).transitional_ignore(),
@@ -103,8 +95,7 @@ TEST(Parse, EmptyMod) {
TEST(Parse, WrongMod) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- UpdateDriver::Options opts(expCtx);
- UpdateDriver driver(opts);
+ UpdateDriver driver(expCtx);
std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
ASSERT_THROWS_CODE_AND_WHAT(
driver.parse(fromjson("{$xyz:{a:1}}"), arrayFilters).transitional_ignore(),
@@ -115,8 +106,7 @@ TEST(Parse, WrongMod) {
TEST(Parse, WrongType) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- UpdateDriver::Options opts(expCtx);
- UpdateDriver driver(opts);
+ UpdateDriver driver(expCtx);
std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
ASSERT_THROWS_CODE_AND_WHAT(
driver.parse(fromjson("{$set:[{a:1}]}"), arrayFilters).transitional_ignore(),
@@ -128,8 +118,7 @@ TEST(Parse, WrongType) {
TEST(Parse, ModsWithLaterObjReplacement) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- UpdateDriver::Options opts(expCtx);
- UpdateDriver driver(opts);
+ UpdateDriver driver(expCtx);
std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
ASSERT_THROWS_CODE_AND_WHAT(
driver.parse(fromjson("{$set:{a:1}, obj: \"obj replacement\"}"), arrayFilters)
@@ -141,11 +130,9 @@ TEST(Parse, ModsWithLaterObjReplacement) {
TEST(Parse, SetOnInsert) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- UpdateDriver::Options opts(expCtx);
- UpdateDriver driver(opts);
+ UpdateDriver driver(expCtx);
std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
ASSERT_OK(driver.parse(fromjson("{$setOnInsert:{a:1}}"), arrayFilters));
- ASSERT_EQUALS(driver.numMods(), 1U);
ASSERT_FALSE(driver.isDocReplacement());
}
@@ -153,27 +140,17 @@ TEST(Collator, SetCollationUpdatesModifierInterfaces) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
CollatorInterfaceMock reverseStringCollator(CollatorInterfaceMock::MockType::kReverseString);
BSONObj updateDocument = fromjson("{$max: {a: 'abd'}}");
- UpdateDriver::Options opts(expCtx);
- UpdateDriver driver(opts);
+ UpdateDriver driver(expCtx);
std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
ASSERT_OK(driver.parse(updateDocument, arrayFilters));
- ASSERT_EQUALS(driver.numMods(), 1U);
- const BSONObj original;
const bool validateForStorage = true;
const FieldRefSet emptyImmutablePaths;
bool modified = false;
mutablebson::Document doc(fromjson("{a: 'cba'}"));
driver.setCollator(&reverseStringCollator);
- driver
- .update(StringData(),
- original,
- &doc,
- validateForStorage,
- emptyImmutablePaths,
- nullptr,
- &modified)
+ driver.update(StringData(), &doc, validateForStorage, emptyImmutablePaths, nullptr, &modified)
.transitional_ignore();
ASSERT_TRUE(modified);
@@ -190,10 +167,8 @@ class CreateFromQueryFixture : public mongo::unittest::Test {
public:
CreateFromQueryFixture()
: _opCtx(_serviceContext.makeOperationContext()),
- _driverOps(new UpdateDriver(
- UpdateDriver::Options(new ExpressionContext(_opCtx.get(), nullptr)))),
- _driverRepl(new UpdateDriver(
- UpdateDriver::Options(new ExpressionContext(_opCtx.get(), nullptr)))) {
+ _driverOps(new UpdateDriver(new ExpressionContext(_opCtx.get(), nullptr))),
+ _driverRepl(new UpdateDriver(new ExpressionContext(_opCtx.get(), nullptr))) {
_driverOps->parse(fromjson("{$set:{'_':1}}"), _arrayFilters).transitional_ignore();
_driverRepl->parse(fromjson("{}"), _arrayFilters).transitional_ignore();
}