1 2 /** 3 * Copyright (C) 2018-present MongoDB, Inc. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the Server Side Public License, version 1, 7 * as published by MongoDB, Inc. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * Server Side Public License for more details. 13 * 14 * You should have received a copy of the Server Side Public License 15 * along with this program. If not, see 16 * <http://www.mongodb.com/licensing/server-side-public-license>. 17 * 18 * As a special exception, the copyright holders give permission to link the 19 * code of portions of this program with the OpenSSL library under certain 20 * conditions as described in each individual source file and distribute 21 * linked combinations including the program with the OpenSSL library. You 22 * must comply with the Server Side Public License in all respects for 23 * all of the code used other than as permitted herein. If you modify file(s) 24 * with this exception, you may extend this exception to your version of the 25 * file(s), but you are not obligated to do so. If you do not wish to do so, 26 * delete this exception statement from your version. If you delete this 27 * exception statement from all source files in the program, then also delete 28 * it in the license file. 29 */ 30 31 #pragma once 32 33 #include "mongo/db/update/update_leaf_node.h" 34 #include "mongo/stdx/memory.h" 35 36 namespace mongo { 37 38 /** 39 * The apply() function for an update modifier must 40 * 1) update the element at the update path if it exists; 41 * 2) raise a user error, indicate a no-op, or create and initialize a new element if the update 42 * path does not exist, depending on the specific modifier semantics; 43 * 3) check for any changes that modify an immutable path; 44 * 4) determine whether indexes may be affected; 45 * 5) validate that the updated document is valid for storage; and 46 * 6) create an oplog entry for the update. 47 * ModifierNode provides a generic implementation of the apply() function that does all this heavy 48 * lifting and calls out to modifier-specific overrides that customize apply() behavior where 49 * necessary. 50 */ 51 class ModifierNode : public UpdateLeafNode { 52 public: UpdateLeafNode(context)53 explicit ModifierNode(Context context = Context::kAll) : UpdateLeafNode(context) {} 54 55 ApplyResult apply(ApplyParams applyParams) const final; 56 57 protected: 58 enum class ModifyResult { 59 // No log entry is necessary for no-op updates. 60 kNoOp, 61 62 // The update can be logged as normal (usually with a $set on the entire element). 63 kNormalUpdate, 64 65 // The element is an array, and the update only appends new array items to the end. The 66 // update can be logged as a $set on each appended element, rather than including the entire 67 // array in the log entry. 68 kArrayAppendUpdate, 69 70 // The element did not exist, so it was created then populated with setValueForNewElement(). 71 // The updateExistingElement() method should never return this value. 72 kCreated 73 }; 74 75 /** 76 * ModifierNode::apply() calls this method when applying an update to an existing path. The 77 * child's implementation of this method is responsible for updating 'element' and indicating 78 * what kind of update was performed in its return value. 79 */ 80 virtual ModifyResult updateExistingElement(mutablebson::Element* element, 81 std::shared_ptr<FieldRef> elementPath) const = 0; 82 83 /** 84 * ModifierNode::apply() calls this method when applying an update to a path that does not yet 85 * exist and should be created. The child's implementation of this method is responsible for 86 * assigning a value to the new element, which will initially be null. 87 * 88 * This method only gets called when the child class implementation of allowCreation() returns 89 * true. ModiferNode child classes should override setValueForNewElement() iff allowCreation() 90 * returns true. 91 */ setValueForNewElement(mutablebson::Element * element)92 virtual void setValueForNewElement(mutablebson::Element* element) const { 93 // Only implementations that return true for allowCreation() will override this method. 94 MONGO_UNREACHABLE; 95 }; 96 97 /** 98 * ModifierNode::apply() calls this method after it finishes applying its update to validate 99 * that no changes resulted in an invalid document. See the implementation of 100 * storage_validation::storageValid() for more detail about document validation requirements. 101 * Most ModifierNode child classes can use the default implementation of this method. 102 * 103 * - 'updatedElement' is the element that was set by either updateExistingElement() or 104 * setValueForNewElement(). 105 * - 'leftSibling' and 'rightSibling' are the left and right siblings of 'updatedElement' or 106 * are invalid if 'updatedElement' is the first or last element in its object, respectively. 107 * If a ModifierNode deletes an element as part of its update (as $unset does), 108 * 'updatedElement' will be invalid, but validateUpdate() can use the siblings to perform 109 * validation instead. 110 * - 'recursionLevel' is the document nesting depth of the 'updatedElement' field. 111 * - 'modifyResult' is either the value returned by updateExistingElement() or the value 112 * ModifyResult::kCreated. 113 */ 114 virtual void validateUpdate(mutablebson::ConstElement updatedElement, 115 mutablebson::ConstElement leftSibling, 116 mutablebson::ConstElement rightSibling, 117 std::uint32_t recursionLevel, 118 ModifyResult modifyResult) const; 119 120 /** 121 * ModifierNode::apply() calls this method after validation to create an oplog entry for the 122 * applied update. Most ModifierNode child classes can use the default implementation of this 123 * method, which creates a $set entry using 'pathTaken' for the path and 'element' for the 124 * value. 125 * 126 * - 'logBuilder' provides the interface for appending log entries. 127 * - 'pathTaken' is the path of the applied update. 128 * - 'element' is the element that was set by either updateExistingElement() or 129 * setValueForNewElement(). 130 * - 'modifyResult' is either the value returned by updateExistingElement() or the value 131 * ModifyResult::kCreated. 132 */ 133 virtual void logUpdate(LogBuilder* logBuilder, 134 StringData pathTaken, 135 mutablebson::Element element, 136 ModifyResult modifyResult) const; 137 138 /** 139 * ModifierNode::apply() calls this method to determine what to do when applying an update to a 140 * path that does not exist. If the child class overrides this method to return true, 141 * ModifierNode::apply() will create a new element at the path and call setValueForNewElement() 142 * on it. Child classes must implement setValueForNewElement() iff this function returns true. 143 */ allowCreation()144 virtual bool allowCreation() const { 145 return false; 146 } 147 148 149 /** 150 * When allowCreation() returns false, ModiferNode::apply() calls this method when determining 151 * if an update to a non-existent path is a no-op or an error. When allowNonViablePath() is 152 * false, an update to a path that could be created (i.e. a "viable" path) is a no-op, and an 153 * update to a path that could not be created results in a PathNotViable user error. When 154 * allowNonViablePath() is true, there is no viabilty check, and any update to a nonexistent 155 * path is a no-op. 156 */ allowNonViablePath()157 virtual bool allowNonViablePath() const { 158 return false; 159 } 160 161 /** 162 * When it is possible for updateExistingElement to replace the element's contents with an 163 * object value (e.g., {$set: {a: {c: 1}}} applied to the document {a: {b:1}}), 164 * ModifierNode::apply() has to do extra work to determine if any immutable paths have been 165 * modified. Any ModifierNode child class that can perform such an update should override this 166 * method to return true, so that ModifierNode::apply() knows to do the extra checks. 167 */ canSetObjectValue()168 virtual bool canSetObjectValue() const { 169 return false; 170 } 171 172 private: 173 ApplyResult applyToNonexistentElement(ApplyParams applyParams) const; 174 ApplyResult applyToExistingElement(ApplyParams applyParams) const; 175 }; 176 177 } // namespace mongo 178