/** * Copyright (C) 2018-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 * . * * 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 #include #include "mongo/base/owned_pointer_vector.h" #include "mongo/base/status.h" #include "mongo/bson/mutable/document.h" #include "mongo/db/exec/document_value/value.h" #include "mongo/db/field_ref_set.h" #include "mongo/db/jsobj.h" #include "mongo/db/ops/write_ops_parsers.h" #include "mongo/db/pipeline/pipeline.h" #include "mongo/db/query/canonical_query.h" #include "mongo/db/update/modifier_table.h" #include "mongo/db/update/object_replace_executor.h" #include "mongo/db/update/pipeline_executor.h" #include "mongo/db/update/update_node_visitor.h" #include "mongo/db/update/update_object_node.h" #include "mongo/db/update/update_tree_executor.h" #include "mongo/db/update_index_data.h" namespace mongo { class CollatorInterface; class OperationContext; class UpdateDriver { public: enum class UpdateType { kOperator, kReplacement, kPipeline }; UpdateDriver(const boost::intrusive_ptr& expCtx); /** * Parses the 'updateExpr' update expression into the '_updateExecutor' member variable. * Uasserts if 'updateExpr' fails to parse. */ void parse(const write_ops::UpdateModification& updateExpr, const std::map>& arrayFilters, boost::optional constants = boost::none, const bool multi = false); /** * Fills in document with any fields in the query which are valid. * * Valid fields include equality matches like "a":1, or "a.b":false * * Each valid field will be expanded (from dot notation) and conflicts will be * checked for all fields added to the underlying document. * * Returns Status::OK() if the document can be used. If there are any error or * conflicts along the way then those errors will be returned. * * If the current update is a document replacement, only the 'immutablePaths' are extracted from * 'query' and used to populate 'doc'. */ Status populateDocumentWithQueryFields(OperationContext* opCtx, const BSONObj& query, const FieldRefSet& immutablePaths, mutablebson::Document& doc) const; Status populateDocumentWithQueryFields(const CanonicalQuery& query, const FieldRefSet& immutablePaths, mutablebson::Document& doc) const; /** * Executes the update over 'doc'. If any modifier is positional, use 'matchedField' (index of * the array item matched). If 'doc' allows the modifiers to be applied in place and no index * updating is involved, then the modifiers may be applied "in place" over 'doc'. * * If the driver's '_logOp' mode is turned on, and if 'logOpRec' is not null, fills in the * latter with the oplog entry corresponding to the update. If the modifiers can't be applied, * returns an error status or uasserts with a corresponding description. * * 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 value of 'isInsert' controls whether $setOnInsert modifiers get applied. * * If 'modifiedPaths' is not null, this method will populate it with the set of paths that were * either modified at runtime or present statically in the update modifiers. For arrays, the * set will include only the path to the array if the length has changed. All paths encode array * indexes explicitly. * * The caller must either provide a null pointer, or a non-null pointer to an empty field ref * set. */ Status update(StringData matchedField, mutablebson::Document* doc, bool validateForStorage, const FieldRefSet& immutablePaths, bool isInsert, BSONObj* logOpRec = nullptr, 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) { if (_updateType == UpdateType::kOperator) { UpdateTreeExecutor* exec = static_cast(_updateExecutor.get()); invariant(exec); return exec->getUpdateTree()->acceptVisitor(visitor); } MONGO_UNREACHABLE; } UpdateExecutor* getUpdateExecutor() { return _updateExecutor.get(); } // // Accessors // UpdateType type() const { return _updateType; } static bool isDocReplacement(const write_ops::UpdateModification& updateMod); bool modsAffectIndices() const { return _affectIndices; } void refreshIndexKeys(const UpdateIndexData* indexedFields) { _indexedFields = indexedFields; } bool logOp() const { return _logOp; } void setLogOp(bool logOp) { _logOp = logOp; } bool fromOplogApplication() const { return _fromOplogApplication; } void setFromOplogApplication(bool fromOplogApplication) { _fromOplogApplication = fromOplogApplication; } mutablebson::Document& getDocument() { return _objDoc; } const mutablebson::Document& getDocument() const { return _objDoc; } bool needMatchDetails() const { return _positional; } /** * Serialize the update expression to Value. Output of this method is expected to, when parsed, * produce a logically equivalent update expression. */ Value serialize() const { return _updateExecutor->serialize(); } /** * Set the collator which will be used by all of the UpdateDriver's underlying modifiers. * * 'collator' must outlive the UpdateDriver. */ void setCollator(const CollatorInterface* collator); private: /** Create the modifier and add it to the back of the modifiers vector */ inline Status addAndParse(const modifiertable::ModifierType type, const BSONElement& elem); // // immutable properties after parsing // UpdateType _updateType = UpdateType::kOperator; std::unique_ptr _updateExecutor; // 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 = nullptr; // // mutable properties after parsing // // Should this driver generate an oplog record when it applies the update? bool _logOp = false; // True if this update comes from an oplog application. bool _fromOplogApplication = false; boost::intrusive_ptr _expCtx; // Are any of the fields mentioned in the mods participating in any index? Is set anew // at each call to update. bool _affectIndices = false; // Do any of the mods require positional match details when calling 'prepare'? bool _positional = false; // The document used to represent or store the object being updated. mutablebson::Document _objDoc; // The document used to build the oplog entry for the update. mutablebson::Document _logDoc; }; } // namespace mongo