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