diff options
22 files changed, 193 insertions, 23 deletions
diff --git a/src/mongo/db/update/addtoset_node.h b/src/mongo/db/update/addtoset_node.h index 6ab894e031a..d634691011f 100644 --- a/src/mongo/db/update/addtoset_node.h +++ b/src/mongo/db/update/addtoset_node.h @@ -51,6 +51,10 @@ public: void setCollator(const CollatorInterface* collator) final; + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + protected: ModifyResult updateExistingElement(mutablebson::Element* element, std::shared_ptr<FieldRef> elementPath) const final; diff --git a/src/mongo/db/update/arithmetic_node.h b/src/mongo/db/update/arithmetic_node.h index 6e32a69e87e..b24c3391f4e 100644 --- a/src/mongo/db/update/arithmetic_node.h +++ b/src/mongo/db/update/arithmetic_node.h @@ -52,6 +52,10 @@ public: void setCollator(const CollatorInterface* collator) final {} + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + protected: ModifyResult updateExistingElement(mutablebson::Element* element, std::shared_ptr<FieldRef> elementPath) const final; diff --git a/src/mongo/db/update/bit_node.h b/src/mongo/db/update/bit_node.h index f558c688b14..96840fdec1e 100644 --- a/src/mongo/db/update/bit_node.h +++ b/src/mongo/db/update/bit_node.h @@ -49,6 +49,10 @@ public: void setCollator(const CollatorInterface* collator) final {} + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + protected: ModifyResult updateExistingElement(mutablebson::Element* element, std::shared_ptr<FieldRef> elementPath) const final; diff --git a/src/mongo/db/update/compare_node.h b/src/mongo/db/update/compare_node.h index 80fb7d26b8a..95bcd666d8f 100644 --- a/src/mongo/db/update/compare_node.h +++ b/src/mongo/db/update/compare_node.h @@ -52,6 +52,10 @@ public: void setCollator(const CollatorInterface* collator) final; + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + protected: ModifyResult updateExistingElement(mutablebson::Element* element, std::shared_ptr<FieldRef> elementPath) const final; diff --git a/src/mongo/db/update/conflict_placeholder_node.h b/src/mongo/db/update/conflict_placeholder_node.h index 88f2545d291..66d29e854c8 100644 --- a/src/mongo/db/update/conflict_placeholder_node.h +++ b/src/mongo/db/update/conflict_placeholder_node.h @@ -71,6 +71,10 @@ public: FieldRef* currentPath, std::map<std::string, std::vector<std::pair<std::string, BSONObj>>>* operatorOrientedUpdates) const final {} + + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } }; } // namespace mongo diff --git a/src/mongo/db/update/current_date_node.h b/src/mongo/db/update/current_date_node.h index b2e1963c632..7872dadce9a 100644 --- a/src/mongo/db/update/current_date_node.h +++ b/src/mongo/db/update/current_date_node.h @@ -48,6 +48,10 @@ public: void setCollator(const CollatorInterface* collator) final {} + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + protected: ModifyResult updateExistingElement(mutablebson::Element* element, std::shared_ptr<FieldRef> elementPath) const final; diff --git a/src/mongo/db/update/object_replace_node.cpp b/src/mongo/db/update/object_replace_node.cpp index 414a49c41a0..88d836d0303 100644 --- a/src/mongo/db/update/object_replace_node.cpp +++ b/src/mongo/db/update/object_replace_node.cpp @@ -44,11 +44,11 @@ namespace { constexpr StringData kIdFieldName = "_id"_sd; } // namespace -ObjectReplaceNode::ObjectReplaceNode(BSONObj val) - : UpdateNode(Type::Replacement), _val(val.getOwned()), _containsId(false) { +ObjectReplaceNode::ObjectReplaceNode(BSONObj value) + : UpdateNode(Type::Replacement), val(value.getOwned()), _containsId(false) { // Replace all zero-valued timestamps with the current time and check for the existence of _id. - for (auto&& elem : _val) { + for (auto&& elem : val) { // Do not change the _id field. if (elem.fieldNameStringData() == kIdFieldName) { @@ -77,7 +77,7 @@ UpdateNode::ApplyResult ObjectReplaceNode::apply(ApplyParams applyParams) const auto original = applyParams.element.getDocument().getObject(); // Check for noop. - if (original.binaryEqual(_val)) { + if (original.binaryEqual(val)) { return ApplyResult::noopResult(); } @@ -97,7 +97,7 @@ UpdateNode::ApplyResult ObjectReplaceNode::apply(ApplyParams applyParams) const } // Insert the provided contents instead. - for (auto&& elem : _val) { + for (auto&& elem : val) { invariant(applyParams.element.appendElement(elem)); } diff --git a/src/mongo/db/update/object_replace_node.h b/src/mongo/db/update/object_replace_node.h index 4f243d876ab..340f0ec6c0b 100644 --- a/src/mongo/db/update/object_replace_node.h +++ b/src/mongo/db/update/object_replace_node.h @@ -49,7 +49,7 @@ public: * Initializes the node with the document to replace with. Any zero-valued timestamps (except * for the _id) are updated to the current time. */ - explicit ObjectReplaceNode(BSONObj val); + explicit ObjectReplaceNode(BSONObj value); std::unique_ptr<UpdateNode> clone() const final { return stdx::make_unique<ObjectReplaceNode>(*this); @@ -58,7 +58,7 @@ public: void setCollator(const CollatorInterface* collator) final {} /** - * Replaces the document that 'applyParams.element' belongs to with '_val'. If '_val' does not + * Replaces the document that 'applyParams.element' belongs to with 'val'. If 'val' does not * contain an _id, the _id from the original document is preserved. 'applyParams.element' must * be the root of the document. 'applyParams.pathToCreate' and 'applyParams.pathTaken' must be * empty. Always returns a result stating that indexes are affected when the replacement is not @@ -67,7 +67,7 @@ public: ApplyResult apply(ApplyParams applyParams) const final; BSONObj serialize() const { - return _val; + return val; } /** @@ -80,11 +80,15 @@ public: MONGO_UNREACHABLE; } -private: + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + // Object to replace with. - BSONObj _val; + BSONObj val; - // True if _val contains an _id. +private: + // True if val contains an _id. bool _containsId; }; diff --git a/src/mongo/db/update/pop_node.h b/src/mongo/db/update/pop_node.h index 86b5e6b64ee..bf15cb2731a 100644 --- a/src/mongo/db/update/pop_node.h +++ b/src/mongo/db/update/pop_node.h @@ -62,6 +62,10 @@ public: return _popFromFront; } + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + private: StringData operatorName() const final { return "$pop"; diff --git a/src/mongo/db/update/pull_node.h b/src/mongo/db/update/pull_node.h index 98e17a16b83..71ecf81bae7 100644 --- a/src/mongo/db/update/pull_node.h +++ b/src/mongo/db/update/pull_node.h @@ -50,6 +50,10 @@ public: return stdx::make_unique<PullNode>(*this); } + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + private: StringData operatorName() const final { return "$pull"; diff --git a/src/mongo/db/update/pullall_node.h b/src/mongo/db/update/pullall_node.h index d22dfcd9d6a..e529123e5ef 100644 --- a/src/mongo/db/update/pullall_node.h +++ b/src/mongo/db/update/pullall_node.h @@ -50,6 +50,10 @@ public: return stdx::make_unique<PullAllNode>(*this); } + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + private: StringData operatorName() const final { return "$pullAll"; diff --git a/src/mongo/db/update/push_node.h b/src/mongo/db/update/push_node.h index e716c8e5c31..beab21950cc 100644 --- a/src/mongo/db/update/push_node.h +++ b/src/mongo/db/update/push_node.h @@ -55,6 +55,10 @@ public: } } + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + protected: ModifyResult updateExistingElement(mutablebson::Element* element, std::shared_ptr<FieldRef> elementPath) const final; diff --git a/src/mongo/db/update/rename_node.cpp b/src/mongo/db/update/rename_node.cpp index 24f9c8329ce..75815485982 100644 --- a/src/mongo/db/update/rename_node.cpp +++ b/src/mongo/db/update/rename_node.cpp @@ -41,8 +41,6 @@ namespace mongo { -namespace { - /** * The SetElementNode class provides the $set functionality for $rename. A $rename from a source * field to a destination field behaves logically like a $set on the destination followed by a @@ -78,6 +76,10 @@ public: std::map<std::string, std::vector<std::pair<std::string, BSONObj>>>* operatorOrientedUpdates) const final {} + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + protected: ModifierNode::ModifyResult updateExistingElement( mutablebson::Element* element, std::shared_ptr<FieldRef> elementPath) const final { @@ -109,8 +111,6 @@ private: mutablebson::Element _elemToSet; }; -} // namespace - Status RenameNode::init(BSONElement modExpr, const boost::intrusive_ptr<ExpressionContext>& expCtx) { invariant(modExpr.ok()); diff --git a/src/mongo/db/update/rename_node.h b/src/mongo/db/update/rename_node.h index 3631fc8d7b2..285f63f6c5e 100644 --- a/src/mongo/db/update/rename_node.h +++ b/src/mongo/db/update/rename_node.h @@ -72,6 +72,10 @@ public: BSON("" << currentPath->dottedField())); } + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + private: BSONElement _val; }; diff --git a/src/mongo/db/update/set_node.cpp b/src/mongo/db/update/set_node.cpp index 00a2d855e3f..a2ee3562dfe 100644 --- a/src/mongo/db/update/set_node.cpp +++ b/src/mongo/db/update/set_node.cpp @@ -38,7 +38,7 @@ namespace mongo { Status SetNode::init(BSONElement modExpr, const boost::intrusive_ptr<ExpressionContext>& expCtx) { invariant(modExpr.ok()); - _val = modExpr; + val = modExpr; return Status::OK(); } @@ -46,17 +46,17 @@ Status SetNode::init(BSONElement modExpr, const boost::intrusive_ptr<ExpressionC ModifierNode::ModifyResult SetNode::updateExistingElement( mutablebson::Element* element, std::shared_ptr<FieldRef> elementPath) const { // If 'element' is deserialized, then element.getValue() will be EOO, which will never equal - // _val. - if (element->getValue().binaryEqualValues(_val)) { + // val. + if (element->getValue().binaryEqualValues(val)) { return ModifyResult::kNoOp; } else { - invariant(element->setValueBSONElement(_val)); + invariant(element->setValueBSONElement(val)); return ModifyResult::kNormalUpdate; } } void SetNode::setValueForNewElement(mutablebson::Element* element) const { - invariant(element->setValueBSONElement(_val)); + invariant(element->setValueBSONElement(val)); } } // namespace mongo diff --git a/src/mongo/db/update/set_node.h b/src/mongo/db/update/set_node.h index 673e42b3402..f16c752f84d 100644 --- a/src/mongo/db/update/set_node.h +++ b/src/mongo/db/update/set_node.h @@ -50,6 +50,12 @@ public: void setCollator(const CollatorInterface* collator) final {} + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + + BSONElement val; + protected: ModifyResult updateExistingElement(mutablebson::Element* element, std::shared_ptr<FieldRef> elementPath) const final; @@ -69,10 +75,8 @@ private: } BSONObj operatorValue() const final { - return BSON("" << _val); + return BSON("" << val); } - - BSONElement _val; }; } // namespace mongo diff --git a/src/mongo/db/update/unset_node.h b/src/mongo/db/update/unset_node.h index 7cdae9f8dac..7a773a821aa 100644 --- a/src/mongo/db/update/unset_node.h +++ b/src/mongo/db/update/unset_node.h @@ -66,6 +66,10 @@ public: return true; } + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + private: StringData operatorName() const final { return "$unset"; diff --git a/src/mongo/db/update/update_array_node.h b/src/mongo/db/update/update_array_node.h index 61e3dd6a443..88f3996afa1 100644 --- a/src/mongo/db/update/update_array_node.h +++ b/src/mongo/db/update/update_array_node.h @@ -91,6 +91,10 @@ public: } } + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + private: const std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>>& _arrayFilters; std::map<std::string, clonable_ptr<UpdateNode>> _children; diff --git a/src/mongo/db/update/update_driver.h b/src/mongo/db/update/update_driver.h index 40224a80a21..50469e056cd 100644 --- a/src/mongo/db/update/update_driver.h +++ b/src/mongo/db/update/update_driver.h @@ -40,6 +40,7 @@ #include "mongo/db/query/canonical_query.h" #include "mongo/db/update/modifier_table.h" #include "mongo/db/update/object_replace_node.h" +#include "mongo/db/update/update_node_visitor.h" #include "mongo/db/update/update_object_node.h" #include "mongo/db/update_index_data.h" @@ -115,6 +116,14 @@ public: bool* docWasModified = nullptr, FieldRefSetWithStorage* modifiedPaths = nullptr); + /** + * Passes the visitor through to the root of the update tree. The visitor is responsible for + * implementing methods that operate on the nodes of the tree. + */ + void visitRoot(UpdateNodeVisitor* visitor) { + _root->acceptVisitor(visitor); + } + // // Accessors // diff --git a/src/mongo/db/update/update_node.h b/src/mongo/db/update/update_node.h index 183b8eeb4fa..be4a1b09155 100644 --- a/src/mongo/db/update/update_node.h +++ b/src/mongo/db/update/update_node.h @@ -39,6 +39,7 @@ #include "mongo/bson/mutable/element.h" #include "mongo/db/field_ref_set.h" #include "mongo/db/update/log_builder.h" +#include "mongo/db/update/update_node_visitor.h" #include "mongo/db/update_index_data.h" #include "mongo/util/assert_util.h" @@ -179,6 +180,12 @@ public: std::map<std::string, std::vector<std::pair<std::string, BSONObj>>>* operatorOrientedUpdates) const = 0; + /** + * This allows an arbitrary class to implement logic which gets dispatched to at runtime + * depending on the type of the UpdateNode. + */ + virtual void acceptVisitor(UpdateNodeVisitor* visitor) = 0; + public: const Context context; const Type type; diff --git a/src/mongo/db/update/update_node_visitor.h b/src/mongo/db/update/update_node_visitor.h new file mode 100644 index 00000000000..886ce6e112f --- /dev/null +++ b/src/mongo/db/update/update_node_visitor.h @@ -0,0 +1,86 @@ +/** + * Copyright (C) 2019-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include "mongo/platform/basic.h" + +namespace mongo { + +class AddToSetNode; +class ArithmeticNode; +class BitNode; +class CompareNode; +class ConflictPlaceholderNode; +class CurrentDateNode; +class ObjectReplaceNode; +class PopNode; +class PullAllNode; +class PullNode; +class PushNode; +class RenameNode; +class SetElementNode; +class SetNode; +class UnsetNode; +class UpdateArrayNode; +class UpdateObjectNode; + +/** + * This is a base class to allow for traversal of an update tree. It implements the visitor + * pattern, in which every derived class from update node implements an acceptVisitor() method, + * which simply calls the appropriate visit() method on the derived UpdateNodeVisitor class. The + * derived class can do whatever it needs to do for each specific node type in the corresponding + * visit() method. + * Derived classes are responsible for making the recursive calls to visit() if they wish + * to visit all the nodes in the update node tree. UpdateNodeVisitor's purpose is not actually to + * ensure that every node in the tree is visited, but rather to handle dynamic dispatch without + * having to add virtual methods to the UpdateNode interface itself. + */ +class UpdateNodeVisitor { +public: + virtual ~UpdateNodeVisitor() = default; + virtual void visit(AddToSetNode*) = 0; + virtual void visit(ArithmeticNode*) = 0; + virtual void visit(BitNode*) = 0; + virtual void visit(CompareNode*) = 0; + virtual void visit(ConflictPlaceholderNode*) = 0; + virtual void visit(CurrentDateNode*) = 0; + virtual void visit(ObjectReplaceNode*) = 0; + virtual void visit(PopNode*) = 0; + virtual void visit(PullAllNode*) = 0; + virtual void visit(PullNode*) = 0; + virtual void visit(PushNode*) = 0; + virtual void visit(RenameNode*) = 0; + virtual void visit(SetElementNode*) = 0; + virtual void visit(SetNode*) = 0; + virtual void visit(UnsetNode*) = 0; + virtual void visit(UpdateArrayNode*) = 0; + virtual void visit(UpdateObjectNode*) = 0; +}; +} // namespace mongo diff --git a/src/mongo/db/update/update_object_node.h b/src/mongo/db/update/update_object_node.h index 37322a191fa..2eee5c4d8f3 100644 --- a/src/mongo/db/update/update_object_node.h +++ b/src/mongo/db/update/update_object_node.h @@ -121,6 +121,14 @@ public: } } + void acceptVisitor(UpdateNodeVisitor* visitor) final { + visitor->visit(this); + } + + const std::map<std::string, clonable_ptr<UpdateNode>>& getChildren() const { + return _children; + } + private: std::map<std::string, clonable_ptr<UpdateNode>> _children; clonable_ptr<UpdateNode> _positionalChild; |