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 #include "mongo/platform/basic.h"
32 
33 #include "mongo/db/update/set_node.h"
34 
35 #include "mongo/bson/mutable/algorithm.h"
36 #include "mongo/bson/mutable/mutable_bson_test_utils.h"
37 #include "mongo/db/json.h"
38 #include "mongo/db/pipeline/expression_context_for_test.h"
39 #include "mongo/db/update/update_node_test_fixture.h"
40 #include "mongo/unittest/death_test.h"
41 #include "mongo/unittest/unittest.h"
42 
43 namespace mongo {
44 namespace {
45 
46 using SetNodeTest = UpdateNodeTest;
47 using mongo::mutablebson::Element;
48 using mongo::mutablebson::countChildren;
49 
50 DEATH_TEST(SetNodeTest, InitFailsForEmptyElement, "Invariant failure modExpr.ok()") {
51     auto update = fromjson("{$set: {}}");
52     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
53     SetNode node;
54     node.init(update["$set"].embeddedObject().firstElement(), expCtx).transitional_ignore();
55 }
56 
TEST(SetNodeTest,InitSucceedsForNonemptyElement)57 TEST(SetNodeTest, InitSucceedsForNonemptyElement) {
58     auto update = fromjson("{$set: {a: 5}}");
59     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
60     SetNode node;
61     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
62 }
63 
TEST_F(SetNodeTest,ApplyNoOp)64 TEST_F(SetNodeTest, ApplyNoOp) {
65     auto update = fromjson("{$set: {a: 5}}");
66     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
67     SetNode node;
68     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
69 
70     mutablebson::Document doc(fromjson("{a: 5}"));
71     setPathTaken("a");
72     addIndexedPath("a");
73     auto result = node.apply(getApplyParams(doc.root()["a"]));
74     ASSERT_TRUE(result.noop);
75     ASSERT_FALSE(result.indexesAffected);
76     ASSERT_EQUALS(fromjson("{a: 5}"), doc);
77     ASSERT_TRUE(doc.isInPlaceModeEnabled());
78     ASSERT_EQUALS(fromjson("{}"), getLogDoc());
79 }
80 
TEST_F(SetNodeTest,ApplyEmptyPathToCreate)81 TEST_F(SetNodeTest, ApplyEmptyPathToCreate) {
82     auto update = fromjson("{$set: {a: 6}}");
83     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
84     SetNode node;
85     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
86 
87     mutablebson::Document doc(fromjson("{a: 5}"));
88     setPathTaken("a");
89     addIndexedPath("a");
90     auto result = node.apply(getApplyParams(doc.root()["a"]));
91     ASSERT_FALSE(result.noop);
92     ASSERT_TRUE(result.indexesAffected);
93     ASSERT_EQUALS(fromjson("{a: 6}"), doc);
94     ASSERT_TRUE(doc.isInPlaceModeEnabled());
95     ASSERT_EQUALS(fromjson("{$set: {a: 6}}"), getLogDoc());
96 }
97 
TEST_F(SetNodeTest,ApplyCreatePath)98 TEST_F(SetNodeTest, ApplyCreatePath) {
99     auto update = fromjson("{$set: {'a.b.c': 6}}");
100     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
101     SetNode node;
102     ASSERT_OK(node.init(update["$set"]["a.b.c"], expCtx));
103 
104     mutablebson::Document doc(fromjson("{a: {d: 5}}"));
105     setPathToCreate("b.c");
106     setPathTaken("a");
107     addIndexedPath("a");
108     auto result = node.apply(getApplyParams(doc.root()["a"]));
109     ASSERT_FALSE(result.noop);
110     ASSERT_TRUE(result.indexesAffected);
111     ASSERT_EQUALS(fromjson("{a: {d: 5, b: {c: 6}}}"), doc);
112     ASSERT_FALSE(doc.isInPlaceModeEnabled());
113     ASSERT_EQUALS(fromjson("{$set: {'a.b.c': 6}}"), getLogDoc());
114 }
115 
TEST_F(SetNodeTest,ApplyCreatePathFromRoot)116 TEST_F(SetNodeTest, ApplyCreatePathFromRoot) {
117     auto update = fromjson("{$set: {'a.b': 6}}");
118     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
119     SetNode node;
120     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
121 
122     mutablebson::Document doc(fromjson("{c: 5}"));
123     setPathToCreate("a.b");
124     addIndexedPath("a");
125     auto result = node.apply(getApplyParams(doc.root()));
126     ASSERT_FALSE(result.noop);
127     ASSERT_TRUE(result.indexesAffected);
128     ASSERT_EQUALS(fromjson("{c: 5, a: {b: 6}}"), doc);
129     ASSERT_FALSE(doc.isInPlaceModeEnabled());
130     ASSERT_EQUALS(fromjson("{$set: {'a.b': 6}}"), getLogDoc());
131 }
132 
TEST_F(SetNodeTest,ApplyPositional)133 TEST_F(SetNodeTest, ApplyPositional) {
134     auto update = fromjson("{$set: {'a.$': 6}}");
135     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
136     SetNode node;
137     ASSERT_OK(node.init(update["$set"]["a.$"], expCtx));
138 
139     mutablebson::Document doc(fromjson("{a: [0, 1, 2]}"));
140     setPathTaken("a.1");
141     setMatchedField("1");
142     addIndexedPath("a");
143     auto result = node.apply(getApplyParams(doc.root()["a"][1]));
144     ASSERT_FALSE(result.noop);
145     ASSERT_TRUE(result.indexesAffected);
146     ASSERT_EQUALS(fromjson("{a: [0, 6, 2]}"), doc);
147     ASSERT_TRUE(doc.isInPlaceModeEnabled());
148     ASSERT_EQUALS(fromjson("{$set: {'a.1': 6}}"), getLogDoc());
149 }
150 
TEST_F(SetNodeTest,ApplyNonViablePathToCreate)151 TEST_F(SetNodeTest, ApplyNonViablePathToCreate) {
152     auto update = fromjson("{$set: {'a.b': 5}}");
153     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
154     SetNode node;
155     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
156 
157     mutablebson::Document doc(fromjson("{a: 5}"));
158     setPathToCreate("b");
159     setPathTaken("a");
160     addIndexedPath("a");
161     ASSERT_THROWS_CODE_AND_WHAT(node.apply(getApplyParams(doc.root()["a"])),
162                                 AssertionException,
163                                 ErrorCodes::PathNotViable,
164                                 "Cannot create field 'b' in element {a: 5}");
165 }
166 
TEST_F(SetNodeTest,ApplyNonViablePathToCreateFromReplicationIsNoOp)167 TEST_F(SetNodeTest, ApplyNonViablePathToCreateFromReplicationIsNoOp) {
168     auto update = fromjson("{$set: {'a.b': 5}}");
169     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
170     SetNode node;
171     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
172 
173     mutablebson::Document doc(fromjson("{a: 5}"));
174     setPathToCreate("b");
175     setPathTaken("a");
176     addIndexedPath("a");
177     setFromOplogApplication(true);
178     auto result = node.apply(getApplyParams(doc.root()["a"]));
179     ASSERT_TRUE(result.noop);
180     ASSERT_FALSE(result.indexesAffected);
181     ASSERT_EQUALS(fromjson("{a: 5}"), doc);
182     ASSERT_TRUE(doc.isInPlaceModeEnabled());
183     ASSERT_EQUALS(fromjson("{}"), getLogDoc());
184 }
185 
TEST_F(SetNodeTest,ApplyNoIndexDataNoLogBuilder)186 TEST_F(SetNodeTest, ApplyNoIndexDataNoLogBuilder) {
187     auto update = fromjson("{$set: {a: 6}}");
188     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
189     SetNode node;
190     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
191 
192     mutablebson::Document doc(fromjson("{a: 5}"));
193     setPathTaken("a");
194     setLogBuilderToNull();
195     auto result = node.apply(getApplyParams(doc.root()["a"]));
196     ASSERT_FALSE(result.noop);
197     ASSERT_FALSE(result.indexesAffected);
198     ASSERT_EQUALS(fromjson("{a: 6}"), doc);
199     ASSERT_TRUE(doc.isInPlaceModeEnabled());
200 }
201 
TEST_F(SetNodeTest,ApplyDoesNotAffectIndexes)202 TEST_F(SetNodeTest, ApplyDoesNotAffectIndexes) {
203     auto update = fromjson("{$set: {a: 6}}");
204     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
205     SetNode node;
206     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
207 
208     mutablebson::Document doc(fromjson("{a: 5}"));
209     setPathTaken("a");
210     addIndexedPath("b");
211     auto result = node.apply(getApplyParams(doc.root()["a"]));
212     ASSERT_FALSE(result.noop);
213     ASSERT_FALSE(result.indexesAffected);
214     ASSERT_EQUALS(fromjson("{a: 6}"), doc);
215     ASSERT_TRUE(doc.isInPlaceModeEnabled());
216 }
217 
TEST_F(SetNodeTest,TypeChangeIsNotANoop)218 TEST_F(SetNodeTest, TypeChangeIsNotANoop) {
219     auto update = fromjson("{$set: {a: NumberLong(2)}}");
220     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
221     SetNode node;
222     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
223 
224     mutablebson::Document doc(fromjson("{a: NumberInt(2)}"));
225     setPathTaken("a");
226     addIndexedPath("a");
227     auto result = node.apply(getApplyParams(doc.root()["a"]));
228     ASSERT_FALSE(result.noop);
229     ASSERT_TRUE(result.indexesAffected);
230     ASSERT_EQUALS(fromjson("{a: NumberLong(2)}"), doc);
231     ASSERT_FALSE(doc.isInPlaceModeEnabled());
232 }
233 
TEST_F(SetNodeTest,IdentityOpOnDeserializedIsNotANoOp)234 TEST_F(SetNodeTest, IdentityOpOnDeserializedIsNotANoOp) {
235     // Apply an op that would be a no-op.
236     auto update = fromjson("{$set: {a: {b : NumberInt(2)}}}");
237     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
238     SetNode node;
239     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
240 
241     mutablebson::Document doc(fromjson("{a: { b: NumberInt(0)}}"));
242     // Apply a mutation to the document that will make it non-serialized.
243     doc.root()["a"]["b"].setValueInt(2).transitional_ignore();
244 
245     setPathTaken("a");
246     addIndexedPath("a");
247     auto result = node.apply(getApplyParams(doc.root()["a"]));
248     ASSERT_FALSE(result.noop);
249     ASSERT_TRUE(result.indexesAffected);
250     ASSERT_EQUALS(fromjson("{a: {b : NumberInt(2)}}"), doc);
251     ASSERT_TRUE(doc.isInPlaceModeEnabled());
252 }
253 
TEST_F(SetNodeTest,ApplyEmptyDocument)254 TEST_F(SetNodeTest, ApplyEmptyDocument) {
255     auto update = fromjson("{$set: {a: 2}}");
256     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
257     SetNode node;
258     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
259 
260     mutablebson::Document doc(fromjson("{}"));
261     setPathToCreate("a");
262     addIndexedPath("a");
263     auto result = node.apply(getApplyParams(doc.root()));
264     ASSERT_FALSE(result.noop);
265     ASSERT_TRUE(result.indexesAffected);
266     ASSERT_EQUALS(fromjson("{a: 2}"), doc);
267     ASSERT_FALSE(doc.isInPlaceModeEnabled());
268 }
269 
TEST_F(SetNodeTest,ApplyInPlace)270 TEST_F(SetNodeTest, ApplyInPlace) {
271     auto update = fromjson("{$set: {a: 2}}");
272     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
273     SetNode node;
274     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
275 
276     mutablebson::Document doc(fromjson("{a: 1}"));
277     setPathTaken("a");
278     addIndexedPath("a");
279     auto result = node.apply(getApplyParams(doc.root()["a"]));
280     ASSERT_FALSE(result.noop);
281     ASSERT_TRUE(result.indexesAffected);
282     ASSERT_EQUALS(fromjson("{a: 2}"), doc);
283     ASSERT_TRUE(doc.isInPlaceModeEnabled());
284 }
285 
TEST_F(SetNodeTest,ApplyOverridePath)286 TEST_F(SetNodeTest, ApplyOverridePath) {
287     auto update = fromjson("{$set: {a: 2}}");
288     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
289     SetNode node;
290     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
291 
292     mutablebson::Document doc(fromjson("{a: {b: 1}}"));
293     setPathTaken("a");
294     addIndexedPath("a");
295     auto result = node.apply(getApplyParams(doc.root()["a"]));
296     ASSERT_FALSE(result.noop);
297     ASSERT_TRUE(result.indexesAffected);
298     ASSERT_EQUALS(fromjson("{a: 2}"), doc);
299     ASSERT_FALSE(doc.isInPlaceModeEnabled());
300 }
301 
TEST_F(SetNodeTest,ApplyChangeType)302 TEST_F(SetNodeTest, ApplyChangeType) {
303     auto update = fromjson("{$set: {a: 2}}");
304     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
305     SetNode node;
306     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
307 
308     mutablebson::Document doc(fromjson("{a: 'str'}"));
309     setPathTaken("a");
310     addIndexedPath("a");
311     auto result = node.apply(getApplyParams(doc.root()["a"]));
312     ASSERT_FALSE(result.noop);
313     ASSERT_TRUE(result.indexesAffected);
314     ASSERT_EQUALS(fromjson("{a: 2}"), doc);
315     ASSERT_FALSE(doc.isInPlaceModeEnabled());
316 }
317 
TEST_F(SetNodeTest,ApplyNewPath)318 TEST_F(SetNodeTest, ApplyNewPath) {
319     auto update = fromjson("{$set: {a: 2}}");
320     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
321     SetNode node;
322     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
323 
324     mutablebson::Document doc(fromjson("{b: 1}"));
325     setPathToCreate("a");
326     addIndexedPath("a");
327     auto result = node.apply(getApplyParams(doc.root()));
328     ASSERT_FALSE(result.noop);
329     ASSERT_TRUE(result.indexesAffected);
330     ASSERT_EQUALS(fromjson("{b: 1, a: 2}"), doc);
331     ASSERT_FALSE(doc.isInPlaceModeEnabled());
332 }
333 
TEST_F(SetNodeTest,ApplyLog)334 TEST_F(SetNodeTest, ApplyLog) {
335     auto update = fromjson("{$set: {a: 2}}");
336     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
337     SetNode node;
338     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
339 
340     mutablebson::Document doc(fromjson("{a: 1}"));
341     setPathTaken("a");
342     node.apply(getApplyParams(doc.root()["a"]));
343     ASSERT_EQUALS(fromjson("{a: 2}"), doc);
344     ASSERT_TRUE(doc.isInPlaceModeEnabled());
345     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
346     ASSERT_EQUALS(fromjson("{$set: {a: 2}}"), getLogDoc());
347 }
348 
TEST_F(SetNodeTest,ApplyNoOpDottedPath)349 TEST_F(SetNodeTest, ApplyNoOpDottedPath) {
350     auto update = fromjson("{$set: {'a.b': 2}}");
351     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
352     SetNode node;
353     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
354 
355     mutablebson::Document doc(fromjson("{a: {b: 2}}"));
356     setPathTaken("a.b");
357     addIndexedPath("a.b");
358     auto result = node.apply(getApplyParams(doc.root()["a"]["b"]));
359     ASSERT_TRUE(result.noop);
360     ASSERT_FALSE(result.indexesAffected);
361     ASSERT_EQUALS(fromjson("{a: {b : 2}}"), doc);
362     ASSERT_TRUE(doc.isInPlaceModeEnabled());
363 }
364 
TEST_F(SetNodeTest,TypeChangeOnDottedPathIsNotANoOp)365 TEST_F(SetNodeTest, TypeChangeOnDottedPathIsNotANoOp) {
366     auto update = fromjson("{$set: {'a.b': NumberInt(2)}}");
367     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
368     SetNode node;
369     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
370 
371     mutablebson::Document doc(fromjson("{a: {b: NumberLong(2)}}"));
372     setPathTaken("a.b");
373     addIndexedPath("a.b");
374     auto result = node.apply(getApplyParams(doc.root()["a"]["b"]));
375     ASSERT_FALSE(result.noop);
376     ASSERT_TRUE(result.indexesAffected);
377     ASSERT_EQUALS(fromjson("{a: {b : NumberLong(2)}}"), doc);
378     ASSERT_FALSE(doc.isInPlaceModeEnabled());
379 }
380 
TEST_F(SetNodeTest,ApplyPathNotViable)381 TEST_F(SetNodeTest, ApplyPathNotViable) {
382     auto update = fromjson("{$set: {'a.b': 2}}");
383     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
384     SetNode node;
385     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
386 
387     mutablebson::Document doc(fromjson("{a:1}"));
388     setPathToCreate("b");
389     setPathTaken("a");
390     ASSERT_THROWS_CODE_AND_WHAT(node.apply(getApplyParams(doc.root()["a"])),
391                                 AssertionException,
392                                 ErrorCodes::PathNotViable,
393                                 "Cannot create field 'b' in element {a: 1}");
394 }
395 
TEST_F(SetNodeTest,ApplyPathNotViableArrray)396 TEST_F(SetNodeTest, ApplyPathNotViableArrray) {
397     auto update = fromjson("{$set: {'a.b': 2}}");
398     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
399     SetNode node;
400     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
401 
402     mutablebson::Document doc(fromjson("{a:[{b:1}]}"));
403     setPathToCreate("b");
404     setPathTaken("a");
405     ASSERT_THROWS_CODE_AND_WHAT(node.apply(getApplyParams(doc.root()["a"])),
406                                 AssertionException,
407                                 ErrorCodes::PathNotViable,
408                                 "Cannot create field 'b' in element {a: [ { b: 1 } ]}");
409 }
410 
TEST_F(SetNodeTest,ApplyInPlaceDottedPath)411 TEST_F(SetNodeTest, ApplyInPlaceDottedPath) {
412     auto update = fromjson("{$set: {'a.b': 2}}");
413     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
414     SetNode node;
415     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
416 
417     mutablebson::Document doc(fromjson("{a: {b: 1}}"));
418     setPathTaken("a.b");
419     addIndexedPath("a.b");
420     auto result = node.apply(getApplyParams(doc.root()["a"]["b"]));
421     ASSERT_FALSE(result.noop);
422     ASSERT_TRUE(result.indexesAffected);
423     ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc);
424     ASSERT_TRUE(doc.isInPlaceModeEnabled());
425 }
426 
TEST_F(SetNodeTest,ApplyChangeTypeDottedPath)427 TEST_F(SetNodeTest, ApplyChangeTypeDottedPath) {
428     auto update = fromjson("{$set: {'a.b': 2}}");
429     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
430     SetNode node;
431     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
432 
433     mutablebson::Document doc(fromjson("{a: {b: 'str'}}"));
434     setPathTaken("a.b");
435     addIndexedPath("a.b");
436     auto result = node.apply(getApplyParams(doc.root()["a"]["b"]));
437     ASSERT_FALSE(result.noop);
438     ASSERT_TRUE(result.indexesAffected);
439     ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc);
440     ASSERT_FALSE(doc.isInPlaceModeEnabled());
441 }
442 
TEST_F(SetNodeTest,ApplyChangePath)443 TEST_F(SetNodeTest, ApplyChangePath) {
444     auto update = fromjson("{$set: {'a.b': 2}}");
445     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
446     SetNode node;
447     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
448 
449     mutablebson::Document doc(fromjson("{a: {b: {c: 1}}}"));
450     setPathTaken("a.b");
451     addIndexedPath("a.b");
452     auto result = node.apply(getApplyParams(doc.root()["a"]["b"]));
453     ASSERT_FALSE(result.noop);
454     ASSERT_TRUE(result.indexesAffected);
455     ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc);
456     ASSERT_FALSE(doc.isInPlaceModeEnabled());
457 }
458 
TEST_F(SetNodeTest,ApplyExtendPath)459 TEST_F(SetNodeTest, ApplyExtendPath) {
460     auto update = fromjson("{$set: {'a.b': 2}}");
461     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
462     SetNode node;
463     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
464 
465     mutablebson::Document doc(fromjson("{a: {c: 1}}"));
466     setPathToCreate("b");
467     setPathTaken("a");
468     addIndexedPath("a.b");
469     auto result = node.apply(getApplyParams(doc.root()["a"]));
470     ASSERT_FALSE(result.noop);
471     ASSERT_TRUE(result.indexesAffected);
472     ASSERT_EQUALS(fromjson("{a: {c: 1, b: 2}}"), doc);
473     ASSERT_FALSE(doc.isInPlaceModeEnabled());
474 }
475 
TEST_F(SetNodeTest,ApplyNewDottedPath)476 TEST_F(SetNodeTest, ApplyNewDottedPath) {
477     auto update = fromjson("{$set: {'a.b': 2}}");
478     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
479     SetNode node;
480     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
481 
482     mutablebson::Document doc(fromjson("{c: 1}"));
483     setPathToCreate("a.b");
484     addIndexedPath("a.b");
485     auto result = node.apply(getApplyParams(doc.root()));
486     ASSERT_FALSE(result.noop);
487     ASSERT_TRUE(result.indexesAffected);
488     ASSERT_EQUALS(fromjson("{c: 1, a: {b: 2}}"), doc);
489     ASSERT_FALSE(doc.isInPlaceModeEnabled());
490 }
491 
TEST_F(SetNodeTest,ApplyEmptyDoc)492 TEST_F(SetNodeTest, ApplyEmptyDoc) {
493     auto update = fromjson("{$set: {'a.b': 2}}");
494     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
495     SetNode node;
496     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
497 
498     mutablebson::Document doc(fromjson("{}"));
499     setPathToCreate("a.b");
500     addIndexedPath("a.b");
501     auto result = node.apply(getApplyParams(doc.root()));
502     ASSERT_FALSE(result.noop);
503     ASSERT_TRUE(result.indexesAffected);
504     ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc);
505     ASSERT_FALSE(doc.isInPlaceModeEnabled());
506 }
507 
TEST_F(SetNodeTest,ApplyFieldWithDot)508 TEST_F(SetNodeTest, ApplyFieldWithDot) {
509     auto update = fromjson("{$set: {'a.b': 2}}");
510     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
511     SetNode node;
512     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
513 
514     mutablebson::Document doc(fromjson("{'a.b':4}"));
515     setPathToCreate("a.b");
516     addIndexedPath("a.b");
517     auto result = node.apply(getApplyParams(doc.root()));
518     ASSERT_FALSE(result.noop);
519     ASSERT_TRUE(result.indexesAffected);
520     ASSERT_EQUALS(fromjson("{'a.b':4, a: {b: 2}}"), doc);
521     ASSERT_FALSE(doc.isInPlaceModeEnabled());
522 }
523 
TEST_F(SetNodeTest,ApplyNoOpArrayIndex)524 TEST_F(SetNodeTest, ApplyNoOpArrayIndex) {
525     auto update = fromjson("{$set: {'a.2.b': 2}}");
526     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
527     SetNode node;
528     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
529 
530     mutablebson::Document doc(fromjson("{a: [{b: 0},{b: 1},{b: 2}]}"));
531     setPathTaken("a.2.b");
532     addIndexedPath("a.2.b");
533     auto result = node.apply(getApplyParams(doc.root()["a"][2]["b"]));
534     ASSERT_TRUE(result.noop);
535     ASSERT_FALSE(result.indexesAffected);
536     ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 2}]}"), doc);
537     ASSERT_TRUE(doc.isInPlaceModeEnabled());
538 }
539 
TEST_F(SetNodeTest,TypeChangeInArrayIsNotANoOp)540 TEST_F(SetNodeTest, TypeChangeInArrayIsNotANoOp) {
541     auto update = fromjson("{$set: {'a.2.b': NumberInt(2)}}");
542     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
543     SetNode node;
544     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
545 
546     mutablebson::Document doc(fromjson("{a: [{b: 0},{b: 1},{b: 2.0}]}"));
547     setPathTaken("a.2.b");
548     addIndexedPath("a.2.b");
549     auto result = node.apply(getApplyParams(doc.root()["a"][2]["b"]));
550     ASSERT_FALSE(result.noop);
551     ASSERT_TRUE(result.indexesAffected);
552     ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: NumberInt(2)}]}"), doc);
553     ASSERT_FALSE(doc.isInPlaceModeEnabled());
554 }
555 
TEST_F(SetNodeTest,ApplyNonViablePath)556 TEST_F(SetNodeTest, ApplyNonViablePath) {
557     auto update = fromjson("{$set: {'a.2.b': 2}}");
558     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
559     SetNode node;
560     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
561 
562     mutablebson::Document doc(fromjson("{a: 0}"));
563     setPathToCreate("2.b");
564     setPathTaken("a");
565     ASSERT_THROWS_CODE_AND_WHAT(node.apply(getApplyParams(doc.root()["a"])),
566                                 AssertionException,
567                                 ErrorCodes::PathNotViable,
568                                 "Cannot create field '2' in element {a: 0}");
569 }
570 
TEST_F(SetNodeTest,ApplyInPlaceArrayIndex)571 TEST_F(SetNodeTest, ApplyInPlaceArrayIndex) {
572     auto update = fromjson("{$set: {'a.2.b': 2}}");
573     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
574     SetNode node;
575     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
576 
577     mutablebson::Document doc(fromjson("{a: [{b: 0},{b: 1},{b: 1}]}"));
578     setPathTaken("a.2.b");
579     addIndexedPath("a.2.b");
580     auto result = node.apply(getApplyParams(doc.root()["a"][2]["b"]));
581     ASSERT_FALSE(result.noop);
582     ASSERT_TRUE(result.indexesAffected);
583     ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 2}]}"), doc);
584     ASSERT_TRUE(doc.isInPlaceModeEnabled());
585 }
586 
TEST_F(SetNodeTest,ApplyNormalArray)587 TEST_F(SetNodeTest, ApplyNormalArray) {
588     auto update = fromjson("{$set: {'a.2.b': 2}}");
589     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
590     SetNode node;
591     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
592 
593     mutablebson::Document doc(fromjson("{a: [{b: 0},{b: 1}]}"));
594     setPathToCreate("2.b");
595     setPathTaken("a");
596     addIndexedPath("a.2.b");
597     auto result = node.apply(getApplyParams(doc.root()["a"]));
598     ASSERT_FALSE(result.noop);
599     ASSERT_TRUE(result.indexesAffected);
600     ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 2}]}"), doc);
601     ASSERT_FALSE(doc.isInPlaceModeEnabled());
602 }
603 
TEST_F(SetNodeTest,ApplyPaddingArray)604 TEST_F(SetNodeTest, ApplyPaddingArray) {
605     auto update = fromjson("{$set: {'a.2.b': 2}}");
606     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
607     SetNode node;
608     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
609 
610     mutablebson::Document doc(fromjson("{a: [{b: 0}]}"));
611     setPathToCreate("2.b");
612     setPathTaken("a");
613     addIndexedPath("a.2.b");
614     auto result = node.apply(getApplyParams(doc.root()["a"]));
615     ASSERT_FALSE(result.noop);
616     ASSERT_TRUE(result.indexesAffected);
617     ASSERT_EQUALS(fromjson("{a: [{b: 0},null,{b: 2}]}"), doc);
618     ASSERT_FALSE(doc.isInPlaceModeEnabled());
619 }
620 
TEST_F(SetNodeTest,ApplyNumericObject)621 TEST_F(SetNodeTest, ApplyNumericObject) {
622     auto update = fromjson("{$set: {'a.2.b': 2}}");
623     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
624     SetNode node;
625     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
626 
627     mutablebson::Document doc(fromjson("{a: {b: 0}}"));
628     setPathToCreate("2.b");
629     setPathTaken("a");
630     addIndexedPath("a.2.b");
631     auto result = node.apply(getApplyParams(doc.root()["a"]));
632     ASSERT_FALSE(result.noop);
633     ASSERT_TRUE(result.indexesAffected);
634     ASSERT_EQUALS(fromjson("{a: {b: 0, '2': {b: 2}}}"), doc);
635     ASSERT_FALSE(doc.isInPlaceModeEnabled());
636 }
637 
TEST_F(SetNodeTest,ApplyNumericField)638 TEST_F(SetNodeTest, ApplyNumericField) {
639     auto update = fromjson("{$set: {'a.2.b': 2}}");
640     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
641     SetNode node;
642     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
643 
644     mutablebson::Document doc(fromjson("{a: {'2': {b: 1}}}"));
645     setPathTaken("a.2.b");
646     addIndexedPath("a.2.b");
647     auto result = node.apply(getApplyParams(doc.root()["a"]["2"]["b"]));
648     ASSERT_FALSE(result.noop);
649     ASSERT_TRUE(result.indexesAffected);
650     ASSERT_EQUALS(fromjson("{a: {'2': {b: 2}}}"), doc);
651     ASSERT_TRUE(doc.isInPlaceModeEnabled());
652 }
653 
TEST_F(SetNodeTest,ApplyExtendNumericField)654 TEST_F(SetNodeTest, ApplyExtendNumericField) {
655     auto update = fromjson("{$set: {'a.2.b': 2}}");
656     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
657     SetNode node;
658     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
659 
660     mutablebson::Document doc(fromjson("{a: {'2': {c: 1}}}"));
661     setPathToCreate("b");
662     setPathTaken("a.2");
663     addIndexedPath("a.2.b");
664     auto result = node.apply(getApplyParams(doc.root()["a"]["2"]));
665     ASSERT_FALSE(result.noop);
666     ASSERT_TRUE(result.indexesAffected);
667     ASSERT_EQUALS(fromjson("{a: {'2': {c: 1, b: 2}}}"), doc);
668     ASSERT_FALSE(doc.isInPlaceModeEnabled());
669 }
670 
TEST_F(SetNodeTest,ApplyEmptyObject)671 TEST_F(SetNodeTest, ApplyEmptyObject) {
672     auto update = fromjson("{$set: {'a.2.b': 2}}");
673     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
674     SetNode node;
675     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
676 
677     mutablebson::Document doc(fromjson("{a: {}}"));
678     setPathToCreate("2.b");
679     setPathTaken("a");
680     addIndexedPath("a.2.b");
681     auto result = node.apply(getApplyParams(doc.root()["a"]));
682     ASSERT_FALSE(result.noop);
683     ASSERT_TRUE(result.indexesAffected);
684     ASSERT_EQUALS(fromjson("{a: {'2': {b: 2}}}"), doc);
685     ASSERT_FALSE(doc.isInPlaceModeEnabled());
686 }
687 
TEST_F(SetNodeTest,ApplyEmptyArray)688 TEST_F(SetNodeTest, ApplyEmptyArray) {
689     auto update = fromjson("{$set: {'a.2.b': 2}}");
690     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
691     SetNode node;
692     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
693 
694     mutablebson::Document doc(fromjson("{a: []}"));
695     setPathToCreate("2.b");
696     setPathTaken("a");
697     addIndexedPath("a.2.b");
698     auto result = node.apply(getApplyParams(doc.root()["a"]));
699     ASSERT_FALSE(result.noop);
700     ASSERT_TRUE(result.indexesAffected);
701     ASSERT_EQUALS(fromjson("{a: [null, null, {b: 2}]}"), doc);
702     ASSERT_FALSE(doc.isInPlaceModeEnabled());
703 }
704 
TEST_F(SetNodeTest,ApplyLogDottedPath)705 TEST_F(SetNodeTest, ApplyLogDottedPath) {
706     auto update = fromjson("{$set: {'a.2.b': 2}}");
707     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
708     SetNode node;
709     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
710 
711     mutablebson::Document doc(fromjson("{a: [{b:0}, {b:1}]}"));
712     setPathToCreate("2.b");
713     setPathTaken("a");
714     node.apply(getApplyParams(doc.root()["a"]));
715     ASSERT_EQUALS(fromjson("{a: [{b:0}, {b:1}, {b:2}]}"), doc);
716     ASSERT_FALSE(doc.isInPlaceModeEnabled());
717     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
718     ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc());
719 }
720 
TEST_F(SetNodeTest,LogEmptyArray)721 TEST_F(SetNodeTest, LogEmptyArray) {
722     auto update = fromjson("{$set: {'a.2.b': 2}}");
723     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
724     SetNode node;
725     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
726 
727     mutablebson::Document doc(fromjson("{a: []}"));
728     setPathToCreate("2.b");
729     setPathTaken("a");
730     node.apply(getApplyParams(doc.root()["a"]));
731     ASSERT_EQUALS(fromjson("{a: [null, null, {b:2}]}"), doc);
732     ASSERT_FALSE(doc.isInPlaceModeEnabled());
733     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
734     ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc());
735 }
736 
TEST_F(SetNodeTest,LogEmptyObject)737 TEST_F(SetNodeTest, LogEmptyObject) {
738     auto update = fromjson("{$set: {'a.2.b': 2}}");
739     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
740     SetNode node;
741     ASSERT_OK(node.init(update["$set"]["a.2.b"], expCtx));
742 
743     mutablebson::Document doc(fromjson("{a: {}}"));
744     setPathToCreate("2.b");
745     setPathTaken("a");
746     node.apply(getApplyParams(doc.root()["a"]));
747     ASSERT_EQUALS(fromjson("{a: {'2': {b: 2}}}"), doc);
748     ASSERT_FALSE(doc.isInPlaceModeEnabled());
749     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
750     ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc());
751 }
752 
TEST_F(SetNodeTest,ApplyNoOpComplex)753 TEST_F(SetNodeTest, ApplyNoOpComplex) {
754     auto update = fromjson("{$set: {'a.1.b': {c: 1, d: 1}}}");
755     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
756     SetNode node;
757     ASSERT_OK(node.init(update["$set"]["a.1.b"], expCtx));
758 
759     mutablebson::Document doc(fromjson("{a: [{b: {c: 0, d: 0}}, {b: {c: 1, d: 1}}]}}"));
760     setPathTaken("a.1.b");
761     addIndexedPath("a.1.b");
762     auto result = node.apply(getApplyParams(doc.root()["a"][1]["b"]));
763     ASSERT_TRUE(result.noop);
764     ASSERT_FALSE(result.indexesAffected);
765     ASSERT_EQUALS(fromjson("{a: [{b: {c: 0, d: 0}}, {b: {c: 1, d: 1}}]}}"), doc);
766     ASSERT_TRUE(doc.isInPlaceModeEnabled());
767 }
768 
TEST_F(SetNodeTest,ApplySameStructure)769 TEST_F(SetNodeTest, ApplySameStructure) {
770     auto update = fromjson("{$set: {'a.1.b': {c: 1, d: 1}}}");
771     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
772     SetNode node;
773     ASSERT_OK(node.init(update["$set"]["a.1.b"], expCtx));
774 
775     mutablebson::Document doc(fromjson("{a: [{b: {c: 0, d: 0}}, {b: {c: 1, xxx: 1}}]}}"));
776     setPathTaken("a.1.b");
777     addIndexedPath("a.1.b");
778     auto result = node.apply(getApplyParams(doc.root()["a"][1]["b"]));
779     ASSERT_FALSE(result.noop);
780     ASSERT_TRUE(result.indexesAffected);
781     ASSERT_EQUALS(fromjson("{a: [{b: {c: 0, d: 0}}, {b: {c: 1, d: 1}}]}}"), doc);
782     ASSERT_FALSE(doc.isInPlaceModeEnabled());
783 }
784 
TEST_F(SetNodeTest,NonViablePathWithoutRepl)785 TEST_F(SetNodeTest, NonViablePathWithoutRepl) {
786     auto update = fromjson("{$set: {'a.1.b': 1}}");
787     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
788     SetNode node;
789     ASSERT_OK(node.init(update["$set"]["a.1.b"], expCtx));
790 
791     mutablebson::Document doc(fromjson("{a: 1}"));
792     setPathToCreate("1.b");
793     setPathTaken("a");
794     ASSERT_THROWS_CODE_AND_WHAT(node.apply(getApplyParams(doc.root()["a"])),
795                                 AssertionException,
796                                 ErrorCodes::PathNotViable,
797                                 "Cannot create field '1' in element {a: 1}");
798 }
799 
TEST_F(SetNodeTest,SingleFieldFromReplication)800 TEST_F(SetNodeTest, SingleFieldFromReplication) {
801     auto update = fromjson("{$set: {'a.1.b': 1}}");
802     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
803     SetNode node;
804     ASSERT_OK(node.init(update["$set"]["a.1.b"], expCtx));
805 
806     mutablebson::Document doc(fromjson("{_id:1, a: 1}"));
807     setPathToCreate("1.b");
808     setPathTaken("a");
809     addIndexedPath("a.1.b");
810     setFromOplogApplication(true);
811     auto result = node.apply(getApplyParams(doc.root()["a"]));
812     ASSERT_TRUE(result.noop);
813     ASSERT_FALSE(result.indexesAffected);
814     ASSERT_EQUALS(fromjson("{_id:1, a: 1}"), doc);
815     ASSERT_TRUE(doc.isInPlaceModeEnabled());
816 }
817 
TEST_F(SetNodeTest,SingleFieldNoIdFromReplication)818 TEST_F(SetNodeTest, SingleFieldNoIdFromReplication) {
819     auto update = fromjson("{$set: {'a.1.b': 1}}");
820     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
821     SetNode node;
822     ASSERT_OK(node.init(update["$set"]["a.1.b"], expCtx));
823 
824     mutablebson::Document doc(fromjson("{a: 1}"));
825     setPathToCreate("1.b");
826     setPathTaken("a");
827     addIndexedPath("a.1.b");
828     setFromOplogApplication(true);
829     auto result = node.apply(getApplyParams(doc.root()["a"]));
830     ASSERT_TRUE(result.noop);
831     ASSERT_FALSE(result.indexesAffected);
832     ASSERT_EQUALS(fromjson("{a: 1}"), doc);
833     ASSERT_TRUE(doc.isInPlaceModeEnabled());
834 }
835 
TEST_F(SetNodeTest,NestedFieldFromReplication)836 TEST_F(SetNodeTest, NestedFieldFromReplication) {
837     auto update = fromjson("{$set: {'a.a.1.b': 1}}");
838     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
839     SetNode node;
840     ASSERT_OK(node.init(update["$set"]["a.a.1.b"], expCtx));
841 
842     mutablebson::Document doc(fromjson("{_id:1, a: {a: 1}}"));
843     setPathToCreate("1.b");
844     setPathTaken("a.a");
845     addIndexedPath("a.a.1.b");
846     setFromOplogApplication(true);
847     auto result = node.apply(getApplyParams(doc.root()["a"]["a"]));
848     ASSERT_TRUE(result.noop);
849     ASSERT_FALSE(result.indexesAffected);
850     ASSERT_EQUALS(fromjson("{_id:1, a: {a: 1}}"), doc);
851     ASSERT_TRUE(doc.isInPlaceModeEnabled());
852 }
853 
TEST_F(SetNodeTest,DoubleNestedFieldFromReplication)854 TEST_F(SetNodeTest, DoubleNestedFieldFromReplication) {
855     auto update = fromjson("{$set: {'a.b.c.d': 2}}");
856     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
857     SetNode node;
858     ASSERT_OK(node.init(update["$set"]["a.b.c.d"], expCtx));
859 
860     mutablebson::Document doc(fromjson("{_id:1, a: {b: {c: 1}}}"));
861     setPathToCreate("d");
862     setPathTaken("a.b.c");
863     addIndexedPath("a.b.c.d");
864     setFromOplogApplication(true);
865     auto result = node.apply(getApplyParams(doc.root()["a"]["b"]["c"]));
866     ASSERT_TRUE(result.noop);
867     ASSERT_FALSE(result.indexesAffected);
868     ASSERT_EQUALS(fromjson("{_id:1, a: {b: {c: 1}}}"), doc);
869     ASSERT_TRUE(doc.isInPlaceModeEnabled());
870 }
871 
TEST_F(SetNodeTest,NestedFieldNoIdFromReplication)872 TEST_F(SetNodeTest, NestedFieldNoIdFromReplication) {
873     auto update = fromjson("{$set: {'a.a.1.b': 1}}");
874     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
875     SetNode node;
876     ASSERT_OK(node.init(update["$set"]["a.a.1.b"], expCtx));
877 
878     mutablebson::Document doc(fromjson("{a: {a: 1}}"));
879     setPathToCreate("1.b");
880     setPathTaken("a.a");
881     addIndexedPath("a.a.1.b");
882     setFromOplogApplication(true);
883     auto result = node.apply(getApplyParams(doc.root()["a"]["a"]));
884     ASSERT_TRUE(result.noop);
885     ASSERT_FALSE(result.indexesAffected);
886     ASSERT_EQUALS(fromjson("{a: {a: 1}}"), doc);
887     ASSERT_TRUE(doc.isInPlaceModeEnabled());
888 }
889 
TEST_F(SetNodeTest,ReplayArrayFieldNotAppendedIntermediateFromReplication)890 TEST_F(SetNodeTest, ReplayArrayFieldNotAppendedIntermediateFromReplication) {
891     auto update = fromjson("{$set: {'a.0.b': [0,2]}}}");
892     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
893     SetNode node;
894     ASSERT_OK(node.init(update["$set"]["a.0.b"], expCtx));
895 
896     mutablebson::Document doc(fromjson("{_id: 0, a: [1, {b: [1]}]}"));
897     setPathToCreate("b");
898     setPathTaken("a.0");
899     addIndexedPath("a.1.b");
900     setFromOplogApplication(true);
901     auto result = node.apply(getApplyParams(doc.root()["a"][0]));
902     ASSERT_TRUE(result.noop);
903     ASSERT_FALSE(result.indexesAffected);
904     ASSERT_EQUALS(fromjson("{_id: 0, a: [1, {b: [1]}]}"), doc);
905     ASSERT_TRUE(doc.isInPlaceModeEnabled());
906 }
907 
TEST_F(SetNodeTest,Set6)908 TEST_F(SetNodeTest, Set6) {
909     auto update = fromjson("{$set: {'r.a': 2}}");
910     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
911     SetNode node;
912     ASSERT_OK(node.init(update["$set"]["r.a"], expCtx));
913 
914     mutablebson::Document doc(fromjson("{_id: 1, r: {a:1, b:2}}"));
915     setPathTaken("r.a");
916     addIndexedPath("r.a");
917     auto result = node.apply(getApplyParams(doc.root()["r"]["a"]));
918     ASSERT_FALSE(result.noop);
919     ASSERT_TRUE(result.indexesAffected);
920     ASSERT_EQUALS(fromjson("{_id: 1, r: {a:2, b:2}}"), doc);
921     ASSERT_TRUE(doc.isInPlaceModeEnabled());
922     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
923     ASSERT_EQUALS(fromjson("{$set: {'r.a': 2}}"), getLogDoc());
924 }
925 
TEST_F(SetNodeTest,Set6FromRepl)926 TEST_F(SetNodeTest, Set6FromRepl) {
927     auto update = fromjson("{$set: { 'r.a': 2}}");
928     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
929     SetNode node;
930     ASSERT_OK(node.init(update["$set"]["r.a"], expCtx));
931 
932     mutablebson::Document doc(fromjson("{_id: 1, r: {a:1, b:2}}"));
933     setPathTaken("r.a");
934     addIndexedPath("r.a");
935     setFromOplogApplication(true);
936     auto result = node.apply(getApplyParams(doc.root()["r"]["a"]));
937     ASSERT_FALSE(result.noop);
938     ASSERT_TRUE(result.indexesAffected);
939     ASSERT_EQUALS(fromjson("{_id: 1, r: {a:2, b:2} }"), doc);
940     ASSERT_TRUE(doc.isInPlaceModeEnabled());
941     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
942     ASSERT_EQUALS(fromjson("{$set: {'r.a': 2}}"), getLogDoc());
943 }
944 
TEST_F(SetNodeTest,ApplySetModToEphemeralDocument)945 TEST_F(SetNodeTest, ApplySetModToEphemeralDocument) {
946     // The following mod when applied to a document constructed node by node exposed a
947     // latent debug only defect in mutable BSON, so this is more a test of mutable than
948     // $set.
949     auto update = fromjson("{ $set: { x: { a: 100, b: 2 }}}");
950     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
951     SetNode node;
952     ASSERT_OK(node.init(update["$set"]["x"], expCtx));
953 
954     mutablebson::Document doc;
955     Element x = doc.makeElementObject("x");
956     doc.root().pushBack(x).transitional_ignore();
957     Element a = doc.makeElementInt("a", 100);
958     x.pushBack(a).transitional_ignore();
959 
960     setPathTaken("x");
961     addIndexedPath("x");
962     auto result = node.apply(getApplyParams(doc.root()["x"]));
963     ASSERT_FALSE(result.noop);
964     ASSERT_TRUE(result.indexesAffected);
965     ASSERT_EQUALS(fromjson("{ x : { a : 100, b : 2 } }"), doc);
966     ASSERT_FALSE(doc.isInPlaceModeEnabled());
967 }
968 
TEST_F(SetNodeTest,ApplyCannotCreateDollarPrefixedFieldInsideSetElement)969 TEST_F(SetNodeTest, ApplyCannotCreateDollarPrefixedFieldInsideSetElement) {
970     auto update = fromjson("{$set: {a: {$bad: 1}}}");
971     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
972     SetNode node;
973     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
974 
975     mutablebson::Document doc(fromjson("{a: 5}"));
976     setPathTaken("a");
977     ASSERT_THROWS_CODE_AND_WHAT(
978         node.apply(getApplyParams(doc.root()["a"])),
979         AssertionException,
980         ErrorCodes::DollarPrefixedFieldName,
981         "The dollar ($) prefixed field '$bad' in 'a.$bad' is not valid for storage.");
982 }
983 
TEST_F(SetNodeTest,ApplyCannotCreateDollarPrefixedFieldAtStartOfPath)984 TEST_F(SetNodeTest, ApplyCannotCreateDollarPrefixedFieldAtStartOfPath) {
985     auto update = fromjson("{$set: {'$bad.a': 1}}");
986     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
987     SetNode node;
988     ASSERT_OK(node.init(update["$set"]["$bad.a"], expCtx));
989 
990     mutablebson::Document doc(fromjson("{}"));
991     setPathToCreate("$bad.a");
992     ASSERT_THROWS_CODE_AND_WHAT(
993         node.apply(getApplyParams(doc.root())),
994         AssertionException,
995         ErrorCodes::DollarPrefixedFieldName,
996         "The dollar ($) prefixed field '$bad' in '$bad' is not valid for storage.");
997 }
998 
TEST_F(SetNodeTest,ApplyCannotCreateDollarPrefixedFieldInMiddleOfPath)999 TEST_F(SetNodeTest, ApplyCannotCreateDollarPrefixedFieldInMiddleOfPath) {
1000     auto update = fromjson("{$set: {'a.$bad.b': 1}}");
1001     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1002     SetNode node;
1003     ASSERT_OK(node.init(update["$set"]["a.$bad.b"], expCtx));
1004 
1005     mutablebson::Document doc(fromjson("{}"));
1006     setPathToCreate("a.$bad.b");
1007     ASSERT_THROWS_CODE_AND_WHAT(
1008         node.apply(getApplyParams(doc.root())),
1009         AssertionException,
1010         ErrorCodes::DollarPrefixedFieldName,
1011         "The dollar ($) prefixed field '$bad' in 'a.$bad' is not valid for storage.");
1012 }
1013 
TEST_F(SetNodeTest,ApplyCannotCreateDollarPrefixedFieldAtEndOfPath)1014 TEST_F(SetNodeTest, ApplyCannotCreateDollarPrefixedFieldAtEndOfPath) {
1015     auto update = fromjson("{$set: {'a.$bad': 1}}");
1016     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1017     SetNode node;
1018     ASSERT_OK(node.init(update["$set"]["a.$bad"], expCtx));
1019 
1020     mutablebson::Document doc(fromjson("{}"));
1021     setPathToCreate("a.$bad");
1022     ASSERT_THROWS_CODE_AND_WHAT(
1023         node.apply(getApplyParams(doc.root())),
1024         AssertionException,
1025         ErrorCodes::DollarPrefixedFieldName,
1026         "The dollar ($) prefixed field '$bad' in 'a.$bad' is not valid for storage.");
1027 }
1028 
TEST_F(SetNodeTest,ApplyCanCreateDollarPrefixedFieldNameWhenValidateForStorageIsFalse)1029 TEST_F(SetNodeTest, ApplyCanCreateDollarPrefixedFieldNameWhenValidateForStorageIsFalse) {
1030     auto update = fromjson("{$set: {$bad: 1}}");
1031     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1032     SetNode node;
1033     ASSERT_OK(node.init(update["$set"]["$bad"], expCtx));
1034 
1035     mutablebson::Document doc(fromjson("{}"));
1036     setPathToCreate("$bad");
1037     addIndexedPath("$bad");
1038     setValidateForStorage(false);
1039     auto result = node.apply(getApplyParams(doc.root()));
1040     ASSERT_FALSE(result.noop);
1041     ASSERT_TRUE(result.indexesAffected);
1042     ASSERT_EQUALS(fromjson("{$bad: 1}"), doc);
1043     ASSERT_FALSE(doc.isInPlaceModeEnabled());
1044     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
1045     ASSERT_EQUALS(fromjson("{$set: {$bad: 1}}"), getLogDoc());
1046 }
1047 
TEST_F(SetNodeTest,ApplyCannotOverwriteImmutablePath)1048 TEST_F(SetNodeTest, ApplyCannotOverwriteImmutablePath) {
1049     auto update = fromjson("{$set: {'a.b': 1}}");
1050     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1051     SetNode node;
1052     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
1053 
1054     mutablebson::Document doc(fromjson("{a: {b: 2}}"));
1055     setPathTaken("a.b");
1056     addImmutablePath("a.b");
1057     ASSERT_THROWS_CODE_AND_WHAT(
1058         node.apply(getApplyParams(doc.root()["a"]["b"])),
1059         AssertionException,
1060         ErrorCodes::ImmutableField,
1061         "Performing an update on the path 'a.b' would modify the immutable field 'a.b'");
1062 }
1063 
TEST_F(SetNodeTest,ApplyCanPerformNoopOnImmutablePath)1064 TEST_F(SetNodeTest, ApplyCanPerformNoopOnImmutablePath) {
1065     auto update = fromjson("{$set: {'a.b': 2}}");
1066     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1067     SetNode node;
1068     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
1069 
1070     mutablebson::Document doc(fromjson("{a: {b: 2}}"));
1071     setPathTaken("a.b");
1072     addImmutablePath("a.b");
1073     addIndexedPath("a");
1074     auto result = node.apply(getApplyParams(doc.root()["a"]["b"]));
1075     ASSERT_TRUE(result.noop);
1076     ASSERT_FALSE(result.indexesAffected);
1077     ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc);
1078     ASSERT_TRUE(doc.isInPlaceModeEnabled());
1079     ASSERT_EQUALS(countChildren(getLogDoc().root()), 0u);
1080     ASSERT_EQUALS(fromjson("{}"), getLogDoc());
1081 }
1082 
TEST_F(SetNodeTest,ApplyCannotOverwritePrefixToRemoveImmutablePath)1083 TEST_F(SetNodeTest, ApplyCannotOverwritePrefixToRemoveImmutablePath) {
1084     auto update = fromjson("{$set: {a: 1}}");
1085     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1086     SetNode node;
1087     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
1088 
1089     mutablebson::Document doc(fromjson("{a: {b: 2}}"));
1090     setPathTaken("a");
1091     addImmutablePath("a.b");
1092     ASSERT_THROWS_CODE_AND_WHAT(
1093         node.apply(getApplyParams(doc.root()["a"])),
1094         AssertionException,
1095         ErrorCodes::ImmutableField,
1096         "After applying the update, the immutable field 'a.b' was found to have been removed.");
1097 }
1098 
TEST_F(SetNodeTest,ApplyCannotOverwritePrefixToModifyImmutablePath)1099 TEST_F(SetNodeTest, ApplyCannotOverwritePrefixToModifyImmutablePath) {
1100     auto update = fromjson("{$set: {a: {b: 1}}}");
1101     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1102     SetNode node;
1103     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
1104 
1105     mutablebson::Document doc(fromjson("{a: {b: 2}}"));
1106     setPathTaken("a");
1107     addImmutablePath("a.b");
1108     ASSERT_THROWS_CODE_AND_WHAT(node.apply(getApplyParams(doc.root()["a"])),
1109                                 AssertionException,
1110                                 ErrorCodes::ImmutableField,
1111                                 "After applying the update, the immutable field 'a.b' was found to "
1112                                 "have been altered to b: 1");
1113 }
1114 
TEST_F(SetNodeTest,ApplyCanPerformNoopOnPrefixOfImmutablePath)1115 TEST_F(SetNodeTest, ApplyCanPerformNoopOnPrefixOfImmutablePath) {
1116     auto update = fromjson("{$set: {a: {b: 2}}}");
1117     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1118     SetNode node;
1119     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
1120 
1121     mutablebson::Document doc(fromjson("{a: {b: 2}}"));
1122     setPathTaken("a");
1123     addImmutablePath("a.b");
1124     addIndexedPath("a");
1125     auto result = node.apply(getApplyParams(doc.root()["a"]));
1126     ASSERT_TRUE(result.noop);
1127     ASSERT_FALSE(result.indexesAffected);
1128     ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc);
1129     ASSERT_TRUE(doc.isInPlaceModeEnabled());
1130     ASSERT_EQUALS(countChildren(getLogDoc().root()), 0u);
1131     ASSERT_EQUALS(fromjson("{}"), getLogDoc());
1132 }
1133 
TEST_F(SetNodeTest,ApplyCanOverwritePrefixToCreateImmutablePath)1134 TEST_F(SetNodeTest, ApplyCanOverwritePrefixToCreateImmutablePath) {
1135     auto update = fromjson("{$set: {a: {b: 2}}}");
1136     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1137     SetNode node;
1138     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
1139 
1140     mutablebson::Document doc(fromjson("{a: 1}"));
1141     setPathTaken("a");
1142     addImmutablePath("a.b");
1143     addIndexedPath("a");
1144     auto result = node.apply(getApplyParams(doc.root()["a"]));
1145     ASSERT_FALSE(result.noop);
1146     ASSERT_TRUE(result.indexesAffected);
1147     ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc);
1148     ASSERT_FALSE(doc.isInPlaceModeEnabled());
1149     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
1150     ASSERT_EQUALS(fromjson("{$set: {a: {b: 2}}}"), getLogDoc());
1151 }
1152 
TEST_F(SetNodeTest,ApplyCanOverwritePrefixOfImmutablePathIfNoopOnImmutablePath)1153 TEST_F(SetNodeTest, ApplyCanOverwritePrefixOfImmutablePathIfNoopOnImmutablePath) {
1154     auto update = fromjson("{$set: {a: {b: 2, c: 3}}}");
1155     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1156     SetNode node;
1157     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
1158 
1159     mutablebson::Document doc(fromjson("{a: {b: 2}}"));
1160     setPathTaken("a");
1161     addImmutablePath("a.b");
1162     addIndexedPath("a");
1163     auto result = node.apply(getApplyParams(doc.root()["a"]));
1164     ASSERT_FALSE(result.noop);
1165     ASSERT_TRUE(result.indexesAffected);
1166     ASSERT_EQUALS(fromjson("{a: {b: 2, c: 3}}"), doc);
1167     ASSERT_FALSE(doc.isInPlaceModeEnabled());
1168     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
1169     ASSERT_EQUALS(fromjson("{$set: {a: {b: 2, c: 3}}}"), getLogDoc());
1170 }
1171 
TEST_F(SetNodeTest,ApplyCannotOverwriteSuffixOfImmutablePath)1172 TEST_F(SetNodeTest, ApplyCannotOverwriteSuffixOfImmutablePath) {
1173     auto update = fromjson("{$set: {'a.b.c': 1}}");
1174     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1175     SetNode node;
1176     ASSERT_OK(node.init(update["$set"]["a.b.c"], expCtx));
1177 
1178     mutablebson::Document doc(fromjson("{a: {b: {c: 2}}}"));
1179     setPathTaken("a.b.c");
1180     addImmutablePath("a.b");
1181     ASSERT_THROWS_CODE_AND_WHAT(
1182         node.apply(getApplyParams(doc.root()["a"]["b"]["c"])),
1183         AssertionException,
1184         ErrorCodes::ImmutableField,
1185         "Performing an update on the path 'a.b.c' would modify the immutable field 'a.b'");
1186 }
1187 
TEST_F(SetNodeTest,ApplyCanPerformNoopOnSuffixOfImmutablePath)1188 TEST_F(SetNodeTest, ApplyCanPerformNoopOnSuffixOfImmutablePath) {
1189     auto update = fromjson("{$set: {'a.b.c': 2}}");
1190     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1191     SetNode node;
1192     ASSERT_OK(node.init(update["$set"]["a.b.c"], expCtx));
1193 
1194     mutablebson::Document doc(fromjson("{a: {b: {c: 2}}}"));
1195     setPathTaken("a.b.c");
1196     addImmutablePath("a.b");
1197     addIndexedPath("a");
1198     auto result = node.apply(getApplyParams(doc.root()["a"]["b"]["c"]));
1199     ASSERT_TRUE(result.noop);
1200     ASSERT_FALSE(result.indexesAffected);
1201     ASSERT_EQUALS(fromjson("{a: {b: {c: 2}}}"), doc);
1202     ASSERT_TRUE(doc.isInPlaceModeEnabled());
1203     ASSERT_EQUALS(countChildren(getLogDoc().root()), 0u);
1204     ASSERT_EQUALS(fromjson("{}"), getLogDoc());
1205 }
1206 
TEST_F(SetNodeTest,ApplyCannotCreateFieldAtEndOfImmutablePath)1207 TEST_F(SetNodeTest, ApplyCannotCreateFieldAtEndOfImmutablePath) {
1208     auto update = fromjson("{$set: {'a.b.c': 1}}");
1209     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1210     SetNode node;
1211     ASSERT_OK(node.init(update["$set"]["a.b.c"], expCtx));
1212 
1213     mutablebson::Document doc(fromjson("{a: {b: {}}}"));
1214     setPathToCreate("c");
1215     setPathTaken("a.b");
1216     addImmutablePath("a.b");
1217     ASSERT_THROWS_CODE_AND_WHAT(
1218         node.apply(getApplyParams(doc.root()["a"]["b"])),
1219         AssertionException,
1220         ErrorCodes::ImmutableField,
1221         "Updating the path 'a.b' to b: { c: 1 } would modify the immutable field 'a.b'");
1222 }
1223 
TEST_F(SetNodeTest,ApplyCannotCreateFieldBeyondEndOfImmutablePath)1224 TEST_F(SetNodeTest, ApplyCannotCreateFieldBeyondEndOfImmutablePath) {
1225     auto update = fromjson("{$set: {'a.b.c': 1}}");
1226     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1227     SetNode node;
1228     ASSERT_OK(node.init(update["$set"]["a.b.c"], expCtx));
1229 
1230     mutablebson::Document doc(fromjson("{a: {b: {}}}"));
1231     setPathToCreate("c");
1232     setPathTaken("a.b");
1233     addImmutablePath("a");
1234     ASSERT_THROWS_CODE_AND_WHAT(
1235         node.apply(getApplyParams(doc.root()["a"]["b"])),
1236         AssertionException,
1237         ErrorCodes::ImmutableField,
1238         "Updating the path 'a.b' to b: { c: 1 } would modify the immutable field 'a'");
1239 }
1240 
TEST_F(SetNodeTest,ApplyCanCreateImmutablePath)1241 TEST_F(SetNodeTest, ApplyCanCreateImmutablePath) {
1242     auto update = fromjson("{$set: {'a.b': 2}}");
1243     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1244     SetNode node;
1245     ASSERT_OK(node.init(update["$set"]["a.b"], expCtx));
1246 
1247     mutablebson::Document doc(fromjson("{a: {}}"));
1248     setPathToCreate("b");
1249     setPathTaken("a");
1250     addImmutablePath("a.b");
1251     addIndexedPath("a");
1252     auto result = node.apply(getApplyParams(doc.root()["a"]));
1253     ASSERT_FALSE(result.noop);
1254     ASSERT_TRUE(result.indexesAffected);
1255     ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc);
1256     ASSERT_FALSE(doc.isInPlaceModeEnabled());
1257     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
1258     ASSERT_EQUALS(fromjson("{$set: {'a.b': 2}}"), getLogDoc());
1259 }
1260 
TEST_F(SetNodeTest,ApplyCanCreatePrefixOfImmutablePath)1261 TEST_F(SetNodeTest, ApplyCanCreatePrefixOfImmutablePath) {
1262     auto update = fromjson("{$set: {a: 2}}");
1263     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1264     SetNode node;
1265     ASSERT_OK(node.init(update["$set"]["a"], expCtx));
1266 
1267     mutablebson::Document doc(fromjson("{}"));
1268     setPathToCreate("a");
1269     addImmutablePath("a.b");
1270     addIndexedPath("a");
1271     auto result = node.apply(getApplyParams(doc.root()));
1272     ASSERT_FALSE(result.noop);
1273     ASSERT_TRUE(result.indexesAffected);
1274     ASSERT_EQUALS(fromjson("{a: 2}"), doc);
1275     ASSERT_FALSE(doc.isInPlaceModeEnabled());
1276     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
1277     ASSERT_EQUALS(fromjson("{$set: {a: 2}}"), getLogDoc());
1278 }
1279 
TEST_F(SetNodeTest,ApplySetFieldInNonExistentArrayElementAffectsIndexOnSiblingField)1280 TEST_F(SetNodeTest, ApplySetFieldInNonExistentArrayElementAffectsIndexOnSiblingField) {
1281     auto update = fromjson("{$set: {'a.1.c': 2}}");
1282     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1283     SetNode node;
1284     ASSERT_OK(node.init(update["$set"]["a.1.c"], expCtx));
1285 
1286     mutablebson::Document doc(fromjson("{a: [{b: 0}]}"));
1287     setPathToCreate("1.c");
1288     setPathTaken("a");
1289     addIndexedPath("a.b");
1290     auto result = node.apply(getApplyParams(doc.root()["a"]));
1291     ASSERT_FALSE(result.noop);
1292     ASSERT_TRUE(result.indexesAffected);
1293     ASSERT_EQUALS(fromjson("{a: [{b: 0}, {c: 2}]}"), doc);
1294     ASSERT_FALSE(doc.isInPlaceModeEnabled());
1295     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
1296     ASSERT_EQUALS(fromjson("{$set: {'a.1.c': 2}}"), getLogDoc());
1297 }
1298 
TEST_F(SetNodeTest,ApplySetFieldInExistingArrayElementDoesNotAffectIndexOnSiblingField)1299 TEST_F(SetNodeTest, ApplySetFieldInExistingArrayElementDoesNotAffectIndexOnSiblingField) {
1300     auto update = fromjson("{$set: {'a.0.c': 2}}");
1301     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1302     SetNode node;
1303     ASSERT_OK(node.init(update["$set"]["a.0.c"], expCtx));
1304 
1305     mutablebson::Document doc(fromjson("{a: [{b: 0}]}"));
1306     setPathToCreate("c");
1307     setPathTaken("a.0");
1308     addIndexedPath("a.b");
1309     auto result = node.apply(getApplyParams(doc.root()["a"][0]));
1310     ASSERT_FALSE(result.noop);
1311     ASSERT_FALSE(result.indexesAffected);
1312     ASSERT_EQUALS(fromjson("{a: [{b: 0, c: 2}]}"), doc);
1313     ASSERT_FALSE(doc.isInPlaceModeEnabled());
1314     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
1315     ASSERT_EQUALS(fromjson("{$set: {'a.0.c': 2}}"), getLogDoc());
1316 }
1317 
TEST_F(SetNodeTest,ApplySetFieldInNonExistentNumericFieldDoesNotAffectIndexOnSiblingField)1318 TEST_F(SetNodeTest, ApplySetFieldInNonExistentNumericFieldDoesNotAffectIndexOnSiblingField) {
1319     auto update = fromjson("{$set: {'a.1.c': 2}}");
1320     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1321     SetNode node;
1322     ASSERT_OK(node.init(update["$set"]["a.1.c"], expCtx));
1323 
1324     mutablebson::Document doc(fromjson("{a: {'0': {b: 0}}}"));
1325     setPathToCreate("1.c");
1326     setPathTaken("a");
1327     addIndexedPath("a.b");
1328     addIndexedPath("a.1.b");
1329     auto result = node.apply(getApplyParams(doc.root()["a"]));
1330     ASSERT_FALSE(result.noop);
1331     ASSERT_FALSE(result.indexesAffected);
1332     ASSERT_EQUALS(fromjson("{a: {'0': {b: 0}, '1': {c: 2}}}"), doc);
1333     ASSERT_FALSE(doc.isInPlaceModeEnabled());
1334     ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u);
1335     ASSERT_EQUALS(fromjson("{$set: {'a.1.c': 2}}"), getLogDoc());
1336 }
1337 
TEST_F(SetNodeTest,ApplySetOnInsertIsNoopWhenInsertIsFalse)1338 TEST_F(SetNodeTest, ApplySetOnInsertIsNoopWhenInsertIsFalse) {
1339     auto update = fromjson("{$setOnInsert: {a: 2}}");
1340     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1341     SetNode node(UpdateNode::Context::kInsertOnly);
1342     ASSERT_OK(node.init(update["$setOnInsert"]["a"], expCtx));
1343 
1344     mutablebson::Document doc(fromjson("{}"));
1345     setPathToCreate("a");
1346     addIndexedPath("a");
1347     auto result = node.apply(getApplyParams(doc.root()));
1348     ASSERT_TRUE(result.noop);
1349     ASSERT_FALSE(result.indexesAffected);
1350     ASSERT_EQUALS(fromjson("{}"), doc);
1351     ASSERT_TRUE(doc.isInPlaceModeEnabled());
1352     ASSERT_EQUALS(fromjson("{}"), getLogDoc());
1353 }
1354 
TEST_F(SetNodeTest,ApplySetOnInsertIsAppliedWhenInsertIsTrue)1355 TEST_F(SetNodeTest, ApplySetOnInsertIsAppliedWhenInsertIsTrue) {
1356     auto update = fromjson("{$setOnInsert: {a: 2}}");
1357     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1358     SetNode node(UpdateNode::Context::kInsertOnly);
1359     ASSERT_OK(node.init(update["$setOnInsert"]["a"], expCtx));
1360 
1361     mutablebson::Document doc(fromjson("{}"));
1362     setPathToCreate("a");
1363     setInsert(true);
1364     addIndexedPath("a");
1365     setLogBuilderToNull();  // The log builder is null for inserts.
1366     auto result = node.apply(getApplyParams(doc.root()));
1367     ASSERT_FALSE(result.noop);
1368     ASSERT_TRUE(result.indexesAffected);
1369     ASSERT_EQUALS(fromjson("{a: 2}"), doc);
1370     ASSERT_FALSE(doc.isInPlaceModeEnabled());
1371 }
1372 
TEST_F(SetNodeTest,ApplySetOnInsertExistingPath)1373 TEST_F(SetNodeTest, ApplySetOnInsertExistingPath) {
1374     auto update = fromjson("{$setOnInsert: {a: 2}}");
1375     boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
1376     SetNode node(UpdateNode::Context::kInsertOnly);
1377     ASSERT_OK(node.init(update["$setOnInsert"]["a"], expCtx));
1378 
1379     mutablebson::Document doc(fromjson("{a: 1}"));
1380     setPathTaken("a");
1381     setInsert(true);
1382     addIndexedPath("a");
1383     setLogBuilderToNull();  // The log builder is null for inserts.
1384     auto result = node.apply(getApplyParams(doc.root()["a"]));
1385     ASSERT_FALSE(result.noop);
1386     ASSERT_TRUE(result.indexesAffected);
1387     ASSERT_EQUALS(fromjson("{a: 2}"), doc);
1388     ASSERT_TRUE(doc.isInPlaceModeEnabled());
1389 }
1390 
1391 }  // namespace
1392 }  // namespace mongo
1393