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