diff options
Diffstat (limited to 'src/mongo/db/ops')
38 files changed, 833 insertions, 365 deletions
diff --git a/src/mongo/db/ops/SConscript b/src/mongo/db/ops/SConscript index 6f8a369f43f..a56e2193c87 100644 --- a/src/mongo/db/ops/SConscript +++ b/src/mongo/db/ops/SConscript @@ -6,6 +6,7 @@ env.StaticLibrary( target='update_common', source=[ 'field_checker.cpp', + 'log_builder.cpp', 'path_support.cpp', ], LIBDEPS=[ @@ -26,6 +27,17 @@ env.CppUnitTest( ) env.CppUnitTest( + target='log_builder_test', + source=[ + 'log_builder_test.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/mutable_bson_test_utils', + 'update_common', + ], +) + +env.CppUnitTest( target='path_support_test', source=[ 'path_support_test.cpp', diff --git a/src/mongo/db/ops/log_builder.cpp b/src/mongo/db/ops/log_builder.cpp new file mode 100644 index 00000000000..cde2668f1ca --- /dev/null +++ b/src/mongo/db/ops/log_builder.cpp @@ -0,0 +1,115 @@ +/** + * Copyright (C) 2013 10gen Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "mongo/db/ops/log_builder.h" + +namespace mongo { + + using mutablebson::Document; + using mutablebson::Element; + + namespace { + const char kSet[] = "$set"; + const char kUnset[] = "$unset"; + } // namespace + + inline Status LogBuilder::addToSection(Element newElt, + Element* section, + const char* sectionName) { + + // If we don't already have this section, try to create it now. + if (!section->ok()) { + + // If we already have object replacement data, we can't also have section entries. + if (hasObjectReplacement()) + return Status( + ErrorCodes::IllegalOperation, + "LogBuilder: Invalid attempt to add a $set/$unset entry" + "to a log with an existing object replacement"); + + Document& doc = _logRoot.getDocument(); + + // We should not already have an element with the section name under the root. + dassert(_logRoot[sectionName] == doc.end()); + + // Construct a new object element to represent this section in the log. + const Element newElement = doc.makeElementObject(sectionName); + if (!newElement.ok()) + return Status(ErrorCodes::InternalError, + "LogBuilder: failed to construct Object Element for $set/$unset"); + + // Enqueue the new section under the root, and record it as our out parameter. + Status result = _logRoot.pushBack(newElement); + if (!result.isOK()) + return result; + *section = newElement; + + // Invalidate attempts to add an object replacement, now that we have a named + // section under the root. + _objectReplacementAccumulator = doc.end(); + } + + // Whatever transpired, we should now have an ok accumulator for the section, and not + // have a replacement accumulator. + dassert(section->ok()); + dassert(!_objectReplacementAccumulator.ok()); + + // Enqueue the provided element to the section and propagate the result. + return section->pushBack(newElt); + } + + Status LogBuilder::addToSets(Element elt) { + return addToSection(elt, &_setAccumulator, kSet); + } + + Status LogBuilder::addToUnsets(Element elt) { + return addToSection(elt, &_unsetAccumulator, kUnset); + } + + Status LogBuilder::getReplacementObject(Element* outElt) { + + // If the replacement accumulator is not ok, we must have started a $set or $unset + // already, so an object replacement is not permitted. + if (!_objectReplacementAccumulator.ok()) { + dassert(_setAccumulator.ok() || _unsetAccumulator.ok()); + return Status( + ErrorCodes::IllegalOperation, + "LogBuilder: Invalid attempt to obtain the object replacement slot " + "for a log containing $set or $unset entries"); + } + + if (hasObjectReplacement()) + return Status( + ErrorCodes::IllegalOperation, + "LogBuilder: Invalid attempt to acquire the replacement object " + "in a log with existing object replacement data"); + + // OK to enqueue object replacement items. + *outElt = _objectReplacementAccumulator; + return Status::OK(); + } + + inline bool LogBuilder::hasObjectReplacement() const { + if (!_objectReplacementAccumulator.ok()) + return false; + + dassert(!_setAccumulator.ok()); + dassert(!_unsetAccumulator.ok()); + + return _objectReplacementAccumulator.hasChildren(); + } + +} // namespace mongo diff --git a/src/mongo/db/ops/log_builder.h b/src/mongo/db/ops/log_builder.h new file mode 100644 index 00000000000..f0893aaf747 --- /dev/null +++ b/src/mongo/db/ops/log_builder.h @@ -0,0 +1,86 @@ +/** + * Copyright (C) 2013 10gen Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <mongo/base/status.h> +#include <mongo/bson/mutable/document.h> + +namespace mongo { + + /** LogBuilder abstracts away some of the details of producing a properly constructed oplog + * update entry. It manages separate regions into which it accumulates $set and $unset + * operations, and distinguishes object replacement style oplog generation from + * $set/$unset style generation and prevents admixture. + */ + class LogBuilder { + public: + /** Construct a new LogBuilder. Log entries will be recorded as new children under the + * 'logRoot' Element, which must be of type mongo::Object and have no children. + */ + inline LogBuilder(mutablebson::Element logRoot) + : _logRoot(logRoot) + , _objectReplacementAccumulator(_logRoot) + , _setAccumulator(_logRoot.getDocument().end()) + , _unsetAccumulator(_setAccumulator) { + dassert(logRoot.isType(mongo::Object)); + dassert(!logRoot.hasChildren()); + } + + /** Return the Document to which the logging root belongs. */ + inline mutablebson::Document& getDocument() { + return _logRoot.getDocument(); + } + + /** Add the given Element as a new entry in the '$set' section of the log. If a $set + * section does not yet exist, it will be created. If this LogBuilder is currently + * configured to contain an object replacement, the request to add to the $set section + * will return an Error. + */ + Status addToSets(mutablebson::Element elt); + + /** Add the given Element as a new entry in the '$unset' section of the log. If an + * '$unset' section does not yet exist, it will be created. If this LogBuilder is + * currently configured to contain an object replacement, the request to add to the + * $unset section will return an Error. + */ + Status addToUnsets(mutablebson::Element elt); + + /** Obtain, via the out parameter 'outElt', a pointer to the mongo::Object type Element + * to which the components of an object replacement should be recorded. It is an error + * to call this if any Elements have been added by calling either addToSets or + * addToUnsets, and attempts to do so will return a non-OK Status. Similarly, if there + * is already object replacement data recorded for this log, the call will fail. + */ + Status getReplacementObject(mutablebson::Element* outElt); + + private: + // Returns true if the object replacement accumulator is valid and has children, false + // otherwise. + inline bool hasObjectReplacement() const; + + inline Status addToSection( + mutablebson::Element newElt, + mutablebson::Element* section, + const char* sectionName); + + mutablebson::Element _logRoot; + mutablebson::Element _objectReplacementAccumulator; + mutablebson::Element _setAccumulator; + mutablebson::Element _unsetAccumulator; + }; + +} // namespace mongo diff --git a/src/mongo/db/ops/log_builder_test.cpp b/src/mongo/db/ops/log_builder_test.cpp new file mode 100644 index 00000000000..99684c43ccd --- /dev/null +++ b/src/mongo/db/ops/log_builder_test.cpp @@ -0,0 +1,244 @@ +/** + * Copyright 2013 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mongo/db/ops/log_builder.h" + +#include "mongo/base/status.h" +#include "mongo/bson/mutable/mutable_bson_test_utils.h" +#include "mongo/db/json.h" +#include "mongo/unittest/unittest.h" + +namespace { + + namespace mmb = mongo::mutablebson; + using mongo::LogBuilder; + + TEST(LogBuilder, Initialization) { + mmb::Document doc; + LogBuilder lb(doc.root()); + ASSERT_EQUALS(&doc, &lb.getDocument()); + } + + TEST(LogBuilder, AddOneToSet) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + const mmb::Element elt_ab = doc.makeElementInt("a.b", 1); + ASSERT_TRUE(elt_ab.ok()); + ASSERT_OK(lb.addToSets(elt_ab)); + + ASSERT_EQUALS(mongo::fromjson("{ $set : { 'a.b' : 1 } }"), doc); + } + + TEST(LogBuilder, AddOneToUnset) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + const mmb::Element elt_xy = doc.makeElementInt("x.y", 1); + ASSERT_TRUE(elt_xy.ok()); + ASSERT_OK(lb.addToUnsets(elt_xy)); + + ASSERT_EQUALS(mongo::fromjson("{ $unset : { 'x.y' : 1 } }"), doc); + } + + TEST(LogBuilder, AddOneToEach) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + const mmb::Element elt_ab = doc.makeElementInt("a.b", 1); + ASSERT_TRUE(elt_ab.ok()); + ASSERT_OK(lb.addToSets(elt_ab)); + + const mmb::Element elt_xy = doc.makeElementInt("x.y", 1); + ASSERT_TRUE(elt_xy.ok()); + ASSERT_OK(lb.addToUnsets(elt_xy)); + + ASSERT_EQUALS( + mongo::fromjson( + "{ " + " $set : { 'a.b' : 1 }, " + " $unset : { 'x.y' : 1 } " + "}" + ), doc); + } + + TEST(LogBuilder, AddOneObjectReplacementEntry) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + mmb::Element replacement = doc.end(); + ASSERT_FALSE(replacement.ok()); + ASSERT_OK(lb.getReplacementObject(&replacement)); + ASSERT_TRUE(replacement.ok()); + ASSERT_TRUE(replacement.isType(mongo::Object)); + + const mmb::Element elt_a = doc.makeElementInt("a", 1); + ASSERT_TRUE(elt_a.ok()); + ASSERT_OK(replacement.pushBack(elt_a)); + + ASSERT_EQUALS(mongo::fromjson("{ a : 1 }"), doc); + } + + TEST(LogBuilder, AddTwoObjectReplacementEntry) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + mmb::Element replacement = doc.end(); + ASSERT_FALSE(replacement.ok()); + ASSERT_OK(lb.getReplacementObject(&replacement)); + ASSERT_TRUE(replacement.ok()); + ASSERT_TRUE(replacement.isType(mongo::Object)); + + const mmb::Element elt_a = doc.makeElementInt("a", 1); + ASSERT_TRUE(elt_a.ok()); + ASSERT_OK(replacement.pushBack(elt_a)); + + const mmb::Element elt_b = doc.makeElementInt("b", 2); + ASSERT_TRUE(elt_b.ok()); + ASSERT_OK(replacement.pushBack(elt_b)); + + ASSERT_EQUALS(mongo::fromjson("{ a : 1, b: 2 }"), doc); + } + + TEST(LogBuilder, VerifySetsAreGrouped) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + const mmb::Element elt_ab = doc.makeElementInt("a.b", 1); + ASSERT_TRUE(elt_ab.ok()); + ASSERT_OK(lb.addToSets(elt_ab)); + + const mmb::Element elt_xy = doc.makeElementInt("x.y", 1); + ASSERT_TRUE(elt_xy.ok()); + ASSERT_OK(lb.addToSets(elt_xy)); + + ASSERT_EQUALS( + mongo::fromjson( + "{ $set : {" + " 'a.b' : 1, " + " 'x.y' : 1 " + "} }" + ), doc); + } + + TEST(LogBuilder, VerifyUnsetsAreGrouped) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + const mmb::Element elt_ab = doc.makeElementInt("a.b", 1); + ASSERT_TRUE(elt_ab.ok()); + ASSERT_OK(lb.addToUnsets(elt_ab)); + + const mmb::Element elt_xy = doc.makeElementInt("x.y", 1); + ASSERT_TRUE(elt_xy.ok()); + ASSERT_OK(lb.addToUnsets(elt_xy)); + + ASSERT_EQUALS( + mongo::fromjson( + "{ $unset : {" + " 'a.b' : 1, " + " 'x.y' : 1 " + "} }" + ), doc); + } + + TEST(LogBuilder, PresenceOfSetPreventsObjectReplacement) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + mmb::Element replacement = doc.end(); + ASSERT_FALSE(replacement.ok()); + ASSERT_OK(lb.getReplacementObject(&replacement)); + ASSERT_TRUE(replacement.ok()); + + const mmb::Element elt_ab = doc.makeElementInt("a.b", 1); + ASSERT_TRUE(elt_ab.ok()); + ASSERT_OK(lb.addToSets(elt_ab)); + + replacement = doc.end(); + ASSERT_FALSE(replacement.ok()); + ASSERT_NOT_OK(lb.getReplacementObject(&replacement)); + ASSERT_FALSE(replacement.ok()); + } + + TEST(LogBuilder, PresenceOfUnsetPreventsObjectReplacement) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + mmb::Element replacement = doc.end(); + ASSERT_FALSE(replacement.ok()); + ASSERT_OK(lb.getReplacementObject(&replacement)); + ASSERT_TRUE(replacement.ok()); + + const mmb::Element elt_ab = doc.makeElementInt("a.b", 1); + ASSERT_TRUE(elt_ab.ok()); + ASSERT_OK(lb.addToSets(elt_ab)); + + replacement = doc.end(); + ASSERT_FALSE(replacement.ok()); + ASSERT_NOT_OK(lb.getReplacementObject(&replacement)); + ASSERT_FALSE(replacement.ok()); + } + + TEST(LogBuilder, CantAddSetWithObjectReplacementDataPresent) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + mmb::Element replacement = doc.end(); + ASSERT_FALSE(replacement.ok()); + ASSERT_OK(lb.getReplacementObject(&replacement)); + ASSERT_TRUE(replacement.ok()); + ASSERT_OK(replacement.appendInt("a", 1)); + + mmb::Element setCandidate = doc.makeElementInt("x", 0); + ASSERT_NOT_OK(lb.addToSets(setCandidate)); + } + + TEST(LogBuilder, CantAddUnsetWithObjectReplacementDataPresent) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + mmb::Element replacement = doc.end(); + ASSERT_FALSE(replacement.ok()); + ASSERT_OK(lb.getReplacementObject(&replacement)); + ASSERT_TRUE(replacement.ok()); + ASSERT_OK(replacement.appendInt("a", 1)); + + mmb::Element setCandidate = doc.makeElementInt("x", 0); + ASSERT_NOT_OK(lb.addToUnsets(setCandidate)); + } + + // Ensure that once you have obtained the object replacement slot and mutated it, that the + // object replacement slot becomes in accessible. This is a bit paranoid, since in practice + // the modifier conflict detection logic should prevent that outcome at a higher level, but + // preventing it here costs us nothing and add an extra safety check. + TEST(LogBuilder, CantReacquireObjectReplacementData) { + mmb::Document doc; + LogBuilder lb(doc.root()); + + mmb::Element replacement = doc.end(); + ASSERT_FALSE(replacement.ok()); + ASSERT_OK(lb.getReplacementObject(&replacement)); + ASSERT_TRUE(replacement.ok()); + ASSERT_OK(replacement.appendInt("a", 1)); + + mmb::Element again = doc.end(); + ASSERT_FALSE(again.ok()); + ASSERT_NOT_OK(lb.getReplacementObject(&again)); + ASSERT_FALSE(again.ok()); + } + +} // namespace diff --git a/src/mongo/db/ops/modifier_add_to_set.cpp b/src/mongo/db/ops/modifier_add_to_set.cpp index ea733922bd4..d04f9199c44 100644 --- a/src/mongo/db/ops/modifier_add_to_set.cpp +++ b/src/mongo/db/ops/modifier_add_to_set.cpp @@ -19,6 +19,7 @@ #include "mongo/base/error_codes.h" #include "mongo/bson/mutable/algorithm.h" #include "mongo/db/ops/field_checker.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/ops/path_support.h" namespace mongo { @@ -354,7 +355,7 @@ namespace mongo { return Status::OK(); } - Status ModifierAddToSet::log(mb::Element logRoot) const { + Status ModifierAddToSet::log(LogBuilder* logBuilder) const { // TODO: This is copied more or less identically from $push. As a result, it copies the // behavior in $push that relies on 'apply' having been called unless this is a no-op. @@ -364,11 +365,7 @@ namespace mongo { // We'd like to create an entry such as {$set: {<fieldname>: [<resulting aray>]}} under // 'logRoot'. We start by creating the {$set: ...} Element. - mb::Document& doc = logRoot.getDocument(); - mb::Element setElement = doc.makeElementObject("$set"); - if (!setElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot create log entry for $addToSet mod"); - } + mb::Document& doc = logBuilder->getDocument(); // Then we create the {<fieldname>:[]} Element, that is, an empty array. mb::Element logElement = doc.makeElementArray(_fieldRef.dottedField()); @@ -397,15 +394,7 @@ namespace mongo { curr = curr.rightSibling(); } - // Now, we attach the {<fieldname>: [<filled array>]} Element under the {$set: ...} - // one. - Status status = setElement.pushBack(logElement); - if (!status.isOK()) { - return status; - } - - // And attach the result under the 'logRoot' Element provided. - return logRoot.pushBack(setElement); + return logBuilder->addToSets(logElement); } } // namespace mongo diff --git a/src/mongo/db/ops/modifier_add_to_set.h b/src/mongo/db/ops/modifier_add_to_set.h index 7e6111f991b..7683577a032 100644 --- a/src/mongo/db/ops/modifier_add_to_set.h +++ b/src/mongo/db/ops/modifier_add_to_set.h @@ -25,6 +25,8 @@ namespace mongo { + class LogBuilder; + class ModifierAddToSet : public ModifierInterface { MONGO_DISALLOW_COPYING(ModifierAddToSet); @@ -51,7 +53,7 @@ namespace mongo { virtual Status apply() const; /** Converts the effects of this $addToSet into one or more equivalent $set operations. */ - virtual Status log(mutablebson::Element logRoot) const; + virtual Status log(LogBuilder* logBuilder) const; private: // Access to each component of fieldName that's the target of this mod. diff --git a/src/mongo/db/ops/modifier_add_to_set_test.cpp b/src/mongo/db/ops/modifier_add_to_set_test.cpp index 4ec5a1b2b08..dd3a2d30ea4 100644 --- a/src/mongo/db/ops/modifier_add_to_set_test.cpp +++ b/src/mongo/db/ops/modifier_add_to_set_test.cpp @@ -22,12 +22,14 @@ #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/platform/cstdint.h" #include "mongo/unittest/unittest.h" namespace { using mongo::BSONObj; + using mongo::LogBuilder; using mongo::ModifierAddToSet; using mongo::ModifierInterface; using mongo::Status; @@ -57,8 +59,8 @@ namespace { return _mod.apply(); } - Status log(Element logRoot) const { - return _mod.log(logRoot); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } ModifierAddToSet& mod() { @@ -137,7 +139,8 @@ namespace { ASSERT_TRUE(execInfo.noOp); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 1 ] } }"), logDoc); } @@ -171,7 +174,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [ 1 ] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 1 ] } }"), logDoc); } @@ -189,7 +193,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [ 1 ] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 1 ] } }"), logDoc); } @@ -207,7 +212,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [ 1, 2, 3 ] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); } @@ -225,7 +231,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [ 1, 2, 3 ] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); } @@ -243,7 +250,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [ 'x', 1 ] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 'x', 1 ] } }"), logDoc); } @@ -261,7 +269,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [ 'x', 1, 2, 3 ] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 'x', 1, 2, 3 ] } }"), logDoc); } @@ -275,7 +284,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); } @@ -289,7 +299,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); } @@ -303,7 +314,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); } @@ -321,7 +333,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [ 1, 1, 2, 1, 2, 2, 3] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 1, 2, 1, 2, 2, 3] } }"), logDoc); } @@ -339,7 +352,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [ 1, 1, 2, 1, 2, 2, 4, 3] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 1, 2, 1, 2, 2, 4, 3] } }"), logDoc); } diff --git a/src/mongo/db/ops/modifier_bit.cpp b/src/mongo/db/ops/modifier_bit.cpp index 26741c5cb25..2099b5f1a72 100644 --- a/src/mongo/db/ops/modifier_bit.cpp +++ b/src/mongo/db/ops/modifier_bit.cpp @@ -19,6 +19,7 @@ #include "mongo/base/error_codes.h" #include "mongo/bson/mutable/document.h" #include "mongo/db/ops/field_checker.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/ops/path_support.h" #include "mongo/util/mongoutils/str.h" @@ -242,31 +243,17 @@ namespace mongo { elemToSet); } - Status ModifierBit::log(mutablebson::Element logRoot) const { + Status ModifierBit::log(LogBuilder* logBuilder) const { - // We'd like to create an entry such as {$set: {<fieldname>: <value>}} under 'logRoot'. - // We start by creating the {$set: ...} Element. - mutablebson::Document& doc = logRoot.getDocument(); - mutablebson::Element setElement = doc.makeElementObject("$set"); - if (!setElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot append log entry for $set mod"); - } + mutablebson::Element logElement = logBuilder->getDocument().makeElementSafeNum( + _fieldRef.dottedField(), + _preparedState->newValue); - // Then we create the {<fieldname>: <value>} Element. - mutablebson::Element logElement = doc.makeElementSafeNum(_fieldRef.dottedField(), - _preparedState->newValue); - if (!logElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot append details for $set mod"); - } + if (!logElement.ok()) + return Status(ErrorCodes::InternalError, "cannot append details for $bit mod"); - // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} one. - Status status = setElement.pushBack(logElement); - if (!status.isOK()) { - return status; - } + return logBuilder->addToSets(logElement); - // And attach the result under the 'logRoot' Element provided. - return logRoot.pushBack(setElement); } } // namespace mongo diff --git a/src/mongo/db/ops/modifier_bit.h b/src/mongo/db/ops/modifier_bit.h index 8c90fd23594..4be9a4a0acb 100644 --- a/src/mongo/db/ops/modifier_bit.h +++ b/src/mongo/db/ops/modifier_bit.h @@ -27,6 +27,8 @@ namespace mongo { + class LogBuilder; + class ModifierBit : public ModifierInterface { MONGO_DISALLOW_COPYING(ModifierBit); @@ -54,7 +56,7 @@ namespace mongo { virtual Status apply() const; /** Converts the effects of this $bit into an equivalent $set */ - virtual Status log(mutablebson::Element logRoot) const; + virtual Status log(LogBuilder* logBuilder) const; private: // Access to each component of fieldName that's the target of this mod. diff --git a/src/mongo/db/ops/modifier_bit_test.cpp b/src/mongo/db/ops/modifier_bit_test.cpp index be2e028fc31..e5f2ee05940 100644 --- a/src/mongo/db/ops/modifier_bit_test.cpp +++ b/src/mongo/db/ops/modifier_bit_test.cpp @@ -22,12 +22,14 @@ #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/platform/cstdint.h" #include "mongo/unittest/unittest.h" namespace { using mongo::BSONObj; + using mongo::LogBuilder; using mongo::ModifierBit; using mongo::ModifierInterface; using mongo::Status; @@ -58,8 +60,8 @@ namespace { return _mod.apply(); } - Status log(Element logRoot) const { - return _mod.log(logRoot); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } ModifierBit& mod() { return _mod; } @@ -136,7 +138,8 @@ namespace { ASSERT_TRUE(execInfo.noOp); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc); } @@ -178,7 +181,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : 0 }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 0 } }"), logDoc); } @@ -195,7 +199,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : 1 }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc); } @@ -212,7 +217,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : 4 }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 4 } }"), logDoc); } @@ -229,7 +235,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : 7 }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 7 } }"), logDoc); } @@ -243,7 +250,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<int>(1))), logDoc); } @@ -257,7 +265,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<int>(1))), logDoc); } @@ -271,7 +280,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<long long>(1))), logDoc); } @@ -285,7 +295,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<long long>(1))), logDoc); } @@ -319,7 +330,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<int>(0xABCD1234U))), logDoc); } @@ -333,7 +345,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<int>(0xABCD1234U))), logDoc); } @@ -347,7 +360,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<long long>(0xABCD1234EF981234ULL))), logDoc); } @@ -362,7 +376,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<long long>(0xABCD1234EF981234ULL))), logDoc); } @@ -440,7 +455,8 @@ namespace { ASSERT_EQUALS(mongo::NumberInt, doc.root()["a"].getType()); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<int>(1))), logDoc); } @@ -458,7 +474,8 @@ namespace { ASSERT_EQUALS(mongo::NumberInt, doc.root()["a"].getType()); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(BSON("$set" << BSON("b" << static_cast<int>(1))), logDoc); } diff --git a/src/mongo/db/ops/modifier_inc.cpp b/src/mongo/db/ops/modifier_inc.cpp index 62af6efd6f5..c6e30c33263 100644 --- a/src/mongo/db/ops/modifier_inc.cpp +++ b/src/mongo/db/ops/modifier_inc.cpp @@ -19,6 +19,7 @@ #include "mongo/base/error_codes.h" #include "mongo/bson/mutable/document.h" #include "mongo/db/ops/field_checker.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/ops/path_support.h" #include "mongo/util/mongoutils/str.h" @@ -237,17 +238,13 @@ namespace mongo { elemToSet); } - Status ModifierInc::log(mutablebson::Element logRoot) const { + Status ModifierInc::log(LogBuilder* logBuilder) const { dassert(_preparedState->newValue.isValid()); // We'd like to create an entry such as {$set: {<fieldname>: <value>}} under 'logRoot'. // We start by creating the {$set: ...} Element. - mutablebson::Document& doc = logRoot.getDocument(); - mutablebson::Element setElement = doc.makeElementObject("$set"); - if (!setElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot append log entry for $set mod"); - } + mutablebson::Document& doc = logBuilder->getDocument(); // Then we create the {<fieldname>: <value>} Element. mutablebson::Element logElement = doc.makeElementSafeNum( @@ -255,17 +252,11 @@ namespace mongo { _preparedState->newValue); if (!logElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot append details for $set mod"); - } - - // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} one. - Status status = setElement.pushBack(logElement); - if (!status.isOK()) { - return status; + return Status(ErrorCodes::InternalError, "cannot append details for $inc mod"); } - // And attach the result under the 'logRoot' Element provided. - return logRoot.pushBack(setElement); + // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} segment. + return logBuilder->addToSets(logElement); } } // namespace mongo diff --git a/src/mongo/db/ops/modifier_inc.h b/src/mongo/db/ops/modifier_inc.h index 5dc9f8fae95..73415fe83d7 100644 --- a/src/mongo/db/ops/modifier_inc.h +++ b/src/mongo/db/ops/modifier_inc.h @@ -26,6 +26,8 @@ namespace mongo { + class LogBuilder; + class ModifierInc : public ModifierInterface { MONGO_DISALLOW_COPYING(ModifierInc); @@ -53,7 +55,7 @@ namespace mongo { virtual Status apply() const; /** Converts the result of the $inc into an equivalent $set under logRoot */ - virtual Status log(mutablebson::Element logRoot) const; + virtual Status log(LogBuilder* logBuilder) const; private: diff --git a/src/mongo/db/ops/modifier_inc_test.cpp b/src/mongo/db/ops/modifier_inc_test.cpp index 86e7a12a696..b5e76e8cfde 100644 --- a/src/mongo/db/ops/modifier_inc_test.cpp +++ b/src/mongo/db/ops/modifier_inc_test.cpp @@ -24,12 +24,14 @@ #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/platform/cstdint.h" #include "mongo/unittest/unittest.h" namespace { using mongo::BSONObj; + using mongo::LogBuilder; using mongo::ModifierInc; using mongo::ModifierInterface; using mongo::NumberInt; @@ -62,8 +64,8 @@ namespace { return _mod.apply(); } - Status log(Element logRoot) const { - return _mod.log(logRoot); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } ModifierInc& mod() { return _mod; } @@ -153,7 +155,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : 1 }"), doc); Document logDoc; - ASSERT_OK(incMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc); } @@ -167,7 +170,8 @@ namespace { ASSERT_FALSE(execInfo.noOp); Document logDoc; - ASSERT_OK(incMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc); } @@ -184,7 +188,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : 3 }"), doc); Document logDoc; - ASSERT_OK(incMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 3 } }"), logDoc); } @@ -201,7 +206,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : { b : 3 } }"), doc); Document logDoc; - ASSERT_OK(incMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { 'a.b' : 3 } }"), logDoc); } @@ -277,7 +283,8 @@ namespace { ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType()); Document logDoc; - ASSERT_OK(incMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc); ASSERT_EQUALS(mongo::NumberLong, logDoc.root()["$set"]["a"].getType()); } @@ -300,7 +307,8 @@ namespace { ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType()); Document logDoc; - ASSERT_OK(incMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 1.0 } }"), logDoc); ASSERT_EQUALS(mongo::NumberDouble, logDoc.root()["$set"]["a"].getType()); } @@ -323,7 +331,8 @@ namespace { ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType()); Document logDoc; - ASSERT_OK(incMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 1.0 } }"), logDoc); ASSERT_EQUALS(mongo::NumberDouble, logDoc.root()["$set"]["a"].getType()); } @@ -345,7 +354,8 @@ namespace { ASSERT_EQUALS(mongo::NumberDouble, doc.root()["a"].getType()); Document logDoc; - ASSERT_OK(incMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(incMod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : 2.0 } }"), logDoc); ASSERT_EQUALS(mongo::NumberDouble, logDoc.root()["$set"]["a"].getType()); } diff --git a/src/mongo/db/ops/modifier_interface.h b/src/mongo/db/ops/modifier_interface.h index 476681e5639..3341de8ba20 100644 --- a/src/mongo/db/ops/modifier_interface.h +++ b/src/mongo/db/ops/modifier_interface.h @@ -24,6 +24,8 @@ namespace mongo { + class LogBuilder; + /** * Abstract base class for update "modifiers" (a.k.a "$ operators"). To create a new * operator, implement a new derived class. @@ -104,10 +106,9 @@ namespace mongo { virtual Status apply() const = 0 ; /** - * Returns OK and registers the result of this mod in 'logRoot', the document that - * would eventually become a log entry. The mod must have kept enough state to - * be able to produce the log record (see idempotency note below). This call may be - * issued even if apply() was not. + * Returns OK and records the result of this mod in the provided LogBuilder. The mod + * must have kept enough state to be able to produce the log record (see idempotency + * note below). This call may be issued even if apply() was not. * * If the mod could not be logged, returns an error status with a reason description. * @@ -119,8 +120,7 @@ namespace mongo { * for logging purposes. An array based operator may check the contents of the * array before operating on it. */ - virtual Status log(mutablebson::Element logRoot) const = 0; - + virtual Status log(LogBuilder* logBuilder) const = 0; }; struct ModifierInterface::ExecInfo { diff --git a/src/mongo/db/ops/modifier_object_replace.cpp b/src/mongo/db/ops/modifier_object_replace.cpp index a8d6a128a85..70fdba5d718 100644 --- a/src/mongo/db/ops/modifier_object_replace.cpp +++ b/src/mongo/db/ops/modifier_object_replace.cpp @@ -18,6 +18,7 @@ #include "mongo/base/error_codes.h" #include "mongo/bson/mutable/document.h" +#include "mongo/db/ops/log_builder.h" namespace mongo { @@ -68,6 +69,8 @@ namespace mongo { Status ModifierObjectReplace::init(const BSONElement& modExpr) { + // TODO: Check for ok for storage here. + if (modExpr.type() != Object) { return Status(ErrorCodes::BadValue, "object replace expects full object"); } @@ -134,20 +137,20 @@ namespace mongo { return Status::OK(); } - Status ModifierObjectReplace::log(mutablebson::Element logRoot) const { + Status ModifierObjectReplace::log(LogBuilder* logBuilder) const { - // We'd like to create an entry such as {<object replacement>} under 'logRoot'. - mutablebson::Document& doc = logRoot.getDocument(); - BSONObjIterator it(_val); - while (it.more()) { - BSONElement elem = it.next(); - Status status = doc.root().appendElement(elem); - if (!status.isOK()) { - return status; - } + mutablebson::Document& doc = logBuilder->getDocument(); + + mutablebson::Element replacementObject = doc.end(); + Status status = logBuilder->getReplacementObject(&replacementObject); + + if (status.isOK()) { + BSONObjIterator it(_val); + while (status.isOK() && it.more()) + status = replacementObject.appendElement(it.next()); } - return Status::OK(); + return status; } } // namespace mongo diff --git a/src/mongo/db/ops/modifier_object_replace.h b/src/mongo/db/ops/modifier_object_replace.h index edead81eeb1..f81ec8de299 100644 --- a/src/mongo/db/ops/modifier_object_replace.h +++ b/src/mongo/db/ops/modifier_object_replace.h @@ -24,6 +24,8 @@ namespace mongo { + class LogBuilder; + class ModifierObjectReplace : public ModifierInterface { MONGO_DISALLOW_COPYING(ModifierObjectReplace); @@ -65,7 +67,7 @@ namespace mongo { * Adds a log entry to logRoot corresponding to full object replacement. Returns OK if * successful or a status describing the error. */ - virtual Status log(mutablebson::Element logRoot) const; + virtual Status log(LogBuilder* logBuilder) const; private: diff --git a/src/mongo/db/ops/modifier_pop.cpp b/src/mongo/db/ops/modifier_pop.cpp index 0aeb1406d72..6ce48efac8e 100644 --- a/src/mongo/db/ops/modifier_pop.cpp +++ b/src/mongo/db/ops/modifier_pop.cpp @@ -20,6 +20,7 @@ #include "mongo/bson/mutable/algorithm.h" #include "mongo/bson/mutable/document.h" #include "mongo/db/ops/field_checker.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/ops/path_support.h" namespace mongo { @@ -153,39 +154,27 @@ namespace mongo { return _preparedState->elementToRemove.remove(); } - Status ModifierPop::log(mutablebson::Element logRoot) const { + Status ModifierPop::log(LogBuilder* logBuilder) const { // log document - mutablebson::Document& doc = logRoot.getDocument(); + mutablebson::Document& doc = logBuilder->getDocument(); const bool pathExists = _preparedState->pathFoundElement.ok() && - (_preparedState->pathFoundIndex == - static_cast<int32_t>(_fieldRef.numParts() - 1)); - - // element to log, like $set/$unset - mutablebson::Element opElement = pathExists ? - doc.makeElementObject("$set") : - doc.makeElementObject("$unset"); - if (!opElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot create log entry"); - } + (_preparedState->pathFoundIndex == + static_cast<int32_t>(_fieldRef.numParts() - 1)); // value for the logElement ("field.path.name": <value>) mutablebson::Element logElement = pathExists ? - logRoot.getDocument().makeElementWithNewFieldName( - _fieldRef.dottedField(), - _preparedState->pathFoundElement - ): - doc.makeElementBool(_fieldRef.dottedField(), true); + doc.makeElementWithNewFieldName( + _fieldRef.dottedField(), + _preparedState->pathFoundElement) : + doc.makeElementBool(_fieldRef.dottedField(), true); + if (!logElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create details"); } // Now, we attach the {<fieldname>: <value>} Element under the {$op: ...} one. - Status status = opElement.pushBack(logElement); - if (!status.isOK()) { - return status; - } - - // And attach the result under the 'logRoot' Element provided. - return logRoot.pushBack(opElement); + return pathExists ? + logBuilder->addToSets(logElement) : + logBuilder->addToUnsets(logElement); } } // namespace mongo diff --git a/src/mongo/db/ops/modifier_pop.h b/src/mongo/db/ops/modifier_pop.h index 197e93af938..835712003c1 100644 --- a/src/mongo/db/ops/modifier_pop.h +++ b/src/mongo/db/ops/modifier_pop.h @@ -27,6 +27,8 @@ namespace mongo { + class LogBuilder; + class ModifierPop : public ModifierInterface { MONGO_DISALLOW_COPYING(ModifierPop); @@ -52,7 +54,7 @@ namespace mongo { virtual Status apply() const; - virtual Status log(mutablebson::Element logRoot) const; + virtual Status log(LogBuilder* logBuilder) const; private: diff --git a/src/mongo/db/ops/modifier_pop_test.cpp b/src/mongo/db/ops/modifier_pop_test.cpp index 3a73b9fa446..5e278e9ac31 100644 --- a/src/mongo/db/ops/modifier_pop_test.cpp +++ b/src/mongo/db/ops/modifier_pop_test.cpp @@ -23,6 +23,7 @@ #include "mongo/bson/mutable/document.h" #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/jsobj.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/json.h" #include "mongo/platform/cstdint.h" #include "mongo/unittest/unittest.h" @@ -31,6 +32,7 @@ namespace { using mongo::Array; using mongo::BSONObj; + using mongo::LogBuilder; using mongo::fromjson; using mongo::ModifierInterface; using mongo::ModifierPop; @@ -59,8 +61,8 @@ namespace { return _mod.apply(); } - Status log(Element logRoot) const { - return _mod.log(logRoot); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } ModifierPop& mod() { return _mod; } @@ -106,7 +108,8 @@ namespace { ASSERT_TRUE(execInfo.noOp); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{$unset: {'s': true}}"), logDoc); } @@ -194,7 +197,8 @@ namespace { ASSERT_EQUALS(fromjson(("{a:[1]}")), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); } @@ -225,7 +229,8 @@ namespace { ASSERT_EQUALS(fromjson(("{a:[]}")), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{$set: {a: []}}"), logDoc); } @@ -244,7 +249,8 @@ namespace { ASSERT_EQUALS(fromjson(("{a:[[1], 1]}")), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{$set: { 'a.0': [1]}}"), logDoc); } @@ -263,7 +269,8 @@ namespace { ASSERT_EQUALS(fromjson(("{a:[[], 1]}")), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{$set: { 'a.0': []}}"), logDoc); } diff --git a/src/mongo/db/ops/modifier_pull.cpp b/src/mongo/db/ops/modifier_pull.cpp index 40b0d327ad6..204572a452f 100644 --- a/src/mongo/db/ops/modifier_pull.cpp +++ b/src/mongo/db/ops/modifier_pull.cpp @@ -20,6 +20,7 @@ #include "mongo/bson/mutable/algorithm.h" #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/ops/field_checker.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/ops/path_support.h" namespace mongo { @@ -184,12 +185,9 @@ namespace mongo { return Status::OK(); } - Status ModifierPull::log(mb::Element logRoot) const { + Status ModifierPull::log(LogBuilder* logBuilder) const { - mb::Document& doc = logRoot.getDocument(); - - mb::Element opElement = doc.end(); - mb::Element logElement = doc.end(); + mb::Document& doc = logBuilder->getDocument(); if (!_preparedState->elemFound.ok() || _preparedState->idxFound < static_cast<int32_t>(_fieldRef.numParts() - 1)) { @@ -197,12 +195,12 @@ namespace mongo { // If we didn't find the element that we wanted to pull from, we log an unset for // that element. - opElement = doc.makeElementObject("$unset"); - if (!opElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot create log entry for $pull mod"); - } + mb::Element logElement = doc.makeElementInt(_fieldRef.dottedField(), 1); + if (!logElement.ok()) + return Status(ErrorCodes::InternalError, + "cannot create log entry for $pull mod"); - logElement = doc.makeElementInt(_fieldRef.dottedField(), 1); + return logBuilder->addToUnsets(logElement); } else { @@ -215,13 +213,8 @@ namespace mongo { // We'd like to create an entry such as {$set: {<fieldname>: [<resulting aray>]}} under // 'logRoot'. We start by creating the {$set: ...} Element. - opElement = doc.makeElementObject("$set"); - if (!opElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot create log entry for $pull mod"); - } - // Then we create the {<fieldname>:[]} Element, that is, an empty array. - logElement = doc.makeElementArray(_fieldRef.dottedField()); + mb::Element logElement = doc.makeElementArray(_fieldRef.dottedField()); if (!logElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create details for $pull mod"); } @@ -246,16 +239,8 @@ namespace mongo { curr = curr.rightSibling(); } + return logBuilder->addToSets(logElement); } - - // Now, we attach log element under the op element. - Status status = opElement.pushBack(logElement); - if (!status.isOK()) { - return status; - } - - // And attach the result under the 'logRoot' Element provided by the caller. - return logRoot.pushBack(opElement); } } // namespace mongo diff --git a/src/mongo/db/ops/modifier_pull.h b/src/mongo/db/ops/modifier_pull.h index 364f0e68549..afea9c3dbbb 100644 --- a/src/mongo/db/ops/modifier_pull.h +++ b/src/mongo/db/ops/modifier_pull.h @@ -46,7 +46,7 @@ namespace mongo { virtual Status apply() const; /** Converts the effects of this $pull into one or more equivalent $unset operations. */ - virtual Status log(mutablebson::Element logRoot) const; + virtual Status log(LogBuilder* logBuilder) const; private: // Access to each component of fieldName that's the target of this mod. diff --git a/src/mongo/db/ops/modifier_pull_all.cpp b/src/mongo/db/ops/modifier_pull_all.cpp index 88a23f87109..ea9d57faedb 100644 --- a/src/mongo/db/ops/modifier_pull_all.cpp +++ b/src/mongo/db/ops/modifier_pull_all.cpp @@ -20,6 +20,7 @@ #include "mongo/bson/mutable/algorithm.h" #include "mongo/bson/mutable/document.h" #include "mongo/db/ops/field_checker.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/ops/path_support.h" namespace mongo { @@ -191,41 +192,29 @@ namespace mongo { return Status::OK(); } - Status ModifierPullAll::log(mutablebson::Element logRoot) const { + Status ModifierPullAll::log(LogBuilder* logBuilder) const { // log document - mutablebson::Document& doc = logRoot.getDocument(); + mutablebson::Document& doc = logBuilder->getDocument(); const bool pathExists = _preparedState->pathFoundElement.ok() && - (_preparedState->pathFoundIndex == - static_cast<int32_t>(_fieldRef.numParts() - 1)); - - // element to log, like $set/$unset - mutablebson::Element opElement = pathExists ? - doc.makeElementObject("$set") : - doc.makeElementObject("$unset"); - if (!opElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot create log entry"); - } + (_preparedState->pathFoundIndex == + static_cast<int32_t>(_fieldRef.numParts() - 1)); // value for the logElement ("field.path.name": <value>) mutablebson::Element logElement = pathExists ? - logRoot.getDocument().makeElementWithNewFieldName( - _fieldRef.dottedField(), - _preparedState->pathFoundElement - ): - doc.makeElementBool(_fieldRef.dottedField(), true); + doc.makeElementWithNewFieldName( + _fieldRef.dottedField(), + _preparedState->pathFoundElement): + doc.makeElementBool(_fieldRef.dottedField(), true); + if (!logElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create details"); } // Now, we attach the {<fieldname>: <value>} Element under the {$op: ...} one. - Status status = opElement.pushBack(logElement); - if (!status.isOK()) { - return status; - } - - // And attach the result under the 'logRoot' Element provided. - return logRoot.pushBack(opElement); + return pathExists ? + logBuilder->addToSets(logElement) : + logBuilder->addToUnsets(logElement); } } // namespace mongo diff --git a/src/mongo/db/ops/modifier_pull_all.h b/src/mongo/db/ops/modifier_pull_all.h index d27637c2df5..8a157bf8271 100644 --- a/src/mongo/db/ops/modifier_pull_all.h +++ b/src/mongo/db/ops/modifier_pull_all.h @@ -27,6 +27,8 @@ namespace mongo { + class LogBuilder; + class ModifierPullAll : public ModifierInterface { MONGO_DISALLOW_COPYING(ModifierPullAll); @@ -50,7 +52,7 @@ namespace mongo { virtual Status apply() const; - virtual Status log(mutablebson::Element logRoot) const; + virtual Status log(LogBuilder* logBuilder) const; private: diff --git a/src/mongo/db/ops/modifier_pull_all_test.cpp b/src/mongo/db/ops/modifier_pull_all_test.cpp index 880c71f5bec..1053cf8d74d 100644 --- a/src/mongo/db/ops/modifier_pull_all_test.cpp +++ b/src/mongo/db/ops/modifier_pull_all_test.cpp @@ -24,12 +24,14 @@ #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/platform/cstdint.h" #include "mongo/unittest/unittest.h" namespace { using mongo::BSONObj; + using mongo::LogBuilder; using mongo::ModifierPullAll; using mongo::ModifierInterface; using mongo::NumberInt; @@ -61,8 +63,8 @@ namespace { return _mod.apply(); } - Status log(Element logRoot) const { - return _mod.log(logRoot); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } ModifierPullAll& mod() { return _mod; } @@ -142,7 +144,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [] } }"), logDoc); } @@ -159,7 +162,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [] } }"), logDoc); } @@ -176,7 +180,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [] } }"), logDoc); } @@ -190,7 +195,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [1, 'a', {r:1, b:2}] } }"), logDoc); } @@ -204,7 +210,8 @@ namespace { ASSERT_TRUE(execInfo.inPlace); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $unset : { b : true } }"), logDoc); } diff --git a/src/mongo/db/ops/modifier_pull_test.cpp b/src/mongo/db/ops/modifier_pull_test.cpp index 53368379a42..064d717006a 100644 --- a/src/mongo/db/ops/modifier_pull_test.cpp +++ b/src/mongo/db/ops/modifier_pull_test.cpp @@ -22,12 +22,14 @@ #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/platform/cstdint.h" #include "mongo/unittest/unittest.h" namespace { using mongo::BSONObj; + using mongo::LogBuilder; using mongo::ModifierPull; using mongo::ModifierInterface; using mongo::Status; @@ -57,8 +59,8 @@ namespace { return _mod.apply(); } - Status log(Element logRoot) const { - return _mod.log(logRoot); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } ModifierPull& mod() { @@ -81,7 +83,8 @@ namespace { ASSERT_TRUE(execInfo.noOp); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $unset : { a : 1 } }"), logDoc); } @@ -123,7 +126,8 @@ namespace { ASSERT_TRUE(execInfo.noOp); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $unset : { a : 1 } }"), logDoc); } @@ -138,7 +142,8 @@ namespace { ASSERT_TRUE(execInfo.noOp); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $unset : { 'a.b.c.d' : 1 } }"), logDoc); } @@ -153,7 +158,8 @@ namespace { ASSERT_TRUE(execInfo.noOp); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [] } }"), logDoc); } @@ -168,7 +174,8 @@ namespace { ASSERT_TRUE(execInfo.noOp); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [2, 3, 4, 5] } }"), logDoc); } @@ -186,7 +193,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [ 1, 2, 3, 4, 5 ] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [1, 2, 3, 4, 5] } }"), logDoc); } @@ -204,7 +212,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [ 1, 2, 3, 4, 5 ] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [1, 2, 3, 4, 5] } }"), logDoc); } @@ -222,7 +231,8 @@ namespace { ASSERT_EQUALS(fromjson("{ a : [] }"), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson("{ $set : { a : [] } }"), logDoc); } @@ -262,7 +272,8 @@ namespace { ASSERT_EQUALS(fromjson(strings[2]), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson(strings[3]), logDoc); } @@ -296,7 +307,8 @@ namespace { ASSERT_EQUALS(fromjson(strings[2]), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson(strings[3]), logDoc); } #endif @@ -330,7 +342,8 @@ namespace { ASSERT_EQUALS(fromjson(strings[2]), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson(strings[3]), logDoc); } @@ -363,7 +376,8 @@ namespace { ASSERT_EQUALS(fromjson(strings[2]), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson(strings[3]), logDoc); } @@ -396,7 +410,8 @@ namespace { ASSERT_EQUALS(fromjson(strings[2]), doc); Document logDoc; - ASSERT_OK(mod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); ASSERT_EQUALS(fromjson(strings[3]), logDoc); } diff --git a/src/mongo/db/ops/modifier_push.cpp b/src/mongo/db/ops/modifier_push.cpp index cad0fdd10b1..875ea260f87 100644 --- a/src/mongo/db/ops/modifier_push.cpp +++ b/src/mongo/db/ops/modifier_push.cpp @@ -21,6 +21,7 @@ #include "mongo/base/error_codes.h" #include "mongo/bson/mutable/algorithm.h" #include "mongo/db/ops/field_checker.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/ops/path_support.h" #include "mongo/util/assert_util.h" #include "mongo/util/mongoutils/str.h" @@ -515,36 +516,24 @@ namespace mongo { return status; } - Status ModifierPush::log(mutablebson::Element logRoot) const { + Status ModifierPush::log(LogBuilder* logBuilder) const { // TODO We can log just a positional set in several cases. For now, let's just log the // full resulting array. // We'd like to create an entry such as {$set: {<fieldname>: [<resulting aray>]}} under // 'logRoot'. We start by creating the {$set: ...} Element. - mutablebson::Document& doc = logRoot.getDocument(); - mutablebson::Element setElement = doc.makeElementObject("$set"); - if (!setElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot create log entry for $push mod"); - } + mutablebson::Document& doc = logBuilder->getDocument(); // value for the logElement ("field.path.name": <value>) - mutablebson::Element logElement = logRoot.getDocument().makeElementWithNewFieldName( - _fieldRef.dottedField(), - _preparedState->elemFound); + mutablebson::Element logElement = doc.makeElementWithNewFieldName( + _fieldRef.dottedField(), + _preparedState->elemFound); + if (!logElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create details for $push mod"); } - // Now, we attach the {<fieldname>: [<filled array>]} Element under the {$set: ...} - // one. - Status status = setElement.pushBack(logElement); - if (!status.isOK()) { - return status; - } - - // And attach the result under the 'logRoot' Element provided. - return logRoot.pushBack(setElement); - + return logBuilder->addToSets(logElement); } } // namespace mongo diff --git a/src/mongo/db/ops/modifier_push.h b/src/mongo/db/ops/modifier_push.h index 42c0ff12dcc..9f5e0f99a13 100644 --- a/src/mongo/db/ops/modifier_push.h +++ b/src/mongo/db/ops/modifier_push.h @@ -28,6 +28,8 @@ namespace mongo { + class LogBuilder; + class ModifierPush : public ModifierInterface { MONGO_DISALLOW_COPYING(ModifierPush); @@ -83,7 +85,7 @@ namespace mongo { * * TODO Log a positional $set in the array, whenever possible. */ - virtual Status log(mutablebson::Element logRoot) const; + virtual Status log(LogBuilder* logBuilder) const; private: diff --git a/src/mongo/db/ops/modifier_push_test.cpp b/src/mongo/db/ops/modifier_push_test.cpp index 0a8c46b670a..df12ff2fc2c 100644 --- a/src/mongo/db/ops/modifier_push_test.cpp +++ b/src/mongo/db/ops/modifier_push_test.cpp @@ -27,6 +27,7 @@ #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/platform/cstdint.h" #include "mongo/unittest/unittest.h" #include "mongo/util/mongoutils/str.h" @@ -37,6 +38,7 @@ namespace { using mongo::BSONObjBuilder; using mongo::BSONArrayBuilder; using mongo::fromjson; + using mongo::LogBuilder; using mongo::ModifierInterface; using mongo::ModifierPush; using mongo::NumberInt; @@ -437,8 +439,8 @@ namespace { return _mod.apply(); } - Status log(Element logRoot) const { - return _mod.log(logRoot); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } ModifierPush& mod() { return _mod; } @@ -471,7 +473,8 @@ namespace { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); } @@ -491,7 +494,8 @@ namespace { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); } @@ -511,7 +515,8 @@ namespace { ASSERT_EQUALS(fromjson("{a: [0,1]}"), doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: [0,1]}}"), logDoc); } @@ -543,7 +548,8 @@ namespace { ASSERT_EQUALS(fromjson("{a: [{b:1}]}"), doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: [{b:1}]}}"), logDoc); } @@ -563,7 +569,8 @@ namespace { ASSERT_EQUALS(fromjson("{a: [{b:1}]}"), doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: [{b:1}]}}"), logDoc); } @@ -583,7 +590,8 @@ namespace { ASSERT_EQUALS(fromjson("{a: [{b:0},{b:1}]}"), doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: [{b:0},{b:1}]}}"), logDoc); } @@ -614,7 +622,8 @@ namespace { doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'choices.first.votes':[1]}}"), logDoc); } @@ -639,7 +648,8 @@ namespace { ASSERT_EQUALS(doc, fromjson("{a: [1]}")); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_TRUE(checkDoc(logDoc, fromjson("{$set: {a: [1]}}"))); } @@ -659,7 +669,8 @@ namespace { ASSERT_EQUALS(doc, fromjson("{a: [1]}")); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_TRUE(checkDoc(logDoc, fromjson("{$set: {a: [1]}}"))); } @@ -679,7 +690,8 @@ namespace { ASSERT_TRUE(checkDoc(doc, fromjson("{a: [0,1,2]}"))); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_TRUE(checkDoc(logDoc, fromjson("{$set: {a: [0,1,2]}}"))); } @@ -711,7 +723,8 @@ namespace { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); } @@ -731,7 +744,8 @@ namespace { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), logDoc); } @@ -751,7 +765,8 @@ namespace { ASSERT_EQUALS(fromjson("{a: [1, 2]}"), doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: [1, 2]}}"), logDoc); } @@ -771,7 +786,8 @@ namespace { ASSERT_EQUALS(fromjson("{a: [0,1]}"), doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: [0,1]}}"), logDoc); } @@ -791,7 +807,8 @@ namespace { ASSERT_EQUALS(fromjson("{a: [0,1,2]}"), doc); Document logDoc; - ASSERT_OK(pushMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(pushMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: [0,1,2]}}"), logDoc); } @@ -997,7 +1014,8 @@ namespace { ASSERT_EQUALS(getObjectUsing(combinedVec), doc); Document logDoc; - ASSERT_OK(mod().log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod().log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(BSON("$set" << getObjectUsing(combinedVec)), logDoc); @@ -1050,7 +1068,8 @@ namespace { ASSERT_EQUALS(getObjectUsing(combinedVec), doc); Document logDoc; - ASSERT_OK(mod().log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod().log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(BSON("$set" << getObjectUsing(combinedVec)), logDoc); diff --git a/src/mongo/db/ops/modifier_rename.cpp b/src/mongo/db/ops/modifier_rename.cpp index 35e179e7747..ea7277b489e 100644 --- a/src/mongo/db/ops/modifier_rename.cpp +++ b/src/mongo/db/ops/modifier_rename.cpp @@ -19,6 +19,7 @@ #include "mongo/base/error_codes.h" #include "mongo/bson/mutable/document.h" #include "mongo/db/ops/field_checker.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/ops/path_support.h" #include "mongo/util/mongoutils/str.h" @@ -238,7 +239,7 @@ namespace mongo { elemToSet); } - Status ModifierRename::log(mutablebson::Element logRoot) const { + Status ModifierRename::log(LogBuilder* logBuilder) const { // If there was no element found then it was a noop, so return immediately if (!_preparedState->fromElemFound.ok()) @@ -254,67 +255,37 @@ namespace mongo { // We'd like to create an entry such as {$set: {<fieldname>: <value>}} under 'logRoot'. // We start by creating the {$set: ...} Element. - mutablebson::Document& doc = logRoot.getDocument(); + mutablebson::Document& doc = logBuilder->getDocument(); - // Create $set - mutablebson::Element setElement = doc.makeElementObject("$set"); - if (!setElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot create log entry for $set mod"); - } - - // Then we create the {<fieldname>: <value>} Element. Note that we log the mod with a + // Create the {<fieldname>: <value>} Element. Note that we log the mod with a // dotted field, if it was applied over a dotted field. The rationale is that the // secondary may be in a different state than the primary and thus make different // decisions about creating the intermediate path in _fieldRef or not. - mutablebson::Element logElement - = doc.makeElementWithNewFieldName( setPath, _preparedState->fromElemFound.getValue()); - if (!logElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot create details for $set mod"); - } + mutablebson::Element logElement = doc.makeElementWithNewFieldName( + setPath, _preparedState->fromElemFound.getValue()); - // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} one. - Status status = setElement.pushBack(logElement); - if (!status.isOK()) { - return status; + if (!logElement.ok()) { + return Status(ErrorCodes::InternalError, "cannot create details for $rename mod"); } - mutablebson::Element unsetElement = doc.end(); - if (doUnset) { - // Create $unset - unsetElement = doc.makeElementObject("$unset"); - if (!setElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot create log entry for $unset mod"); - } + // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} section. + Status status = logBuilder->addToSets(logElement); - // Then we create the {<fieldname>: <value>} Element. Note that we log the mod with a + if (status.isOK() && doUnset) { + // Create the {<fieldname>: <value>} Element. Note that we log the mod with a // dotted field, if it was applied over a dotted field. The rationale is that the // secondary may be in a different state than the primary and thus make different // decisions about creating the intermediate path in _fieldRef or not. mutablebson::Element unsetEntry = doc.makeElementBool(unsetPath, true); if (!unsetEntry.ok()) { - return Status(ErrorCodes::InternalError, "cannot create details for $unset mod"); + return Status(ErrorCodes::InternalError, "cannot create details for $rename mod"); } - // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} one. - status = unsetElement.pushBack(unsetEntry); - if (!status.isOK()) { - return status; - } - - } - - // And attach the result under the 'logRoot' Element provided. - status = logRoot.pushBack(setElement); - if (!status.isOK()) - return status; - - if (doUnset) { - status = logRoot.pushBack(unsetElement); - if (!status.isOK()) - return status; + // Now, we attach the Element under the {$unset: ...} section. + status = logBuilder->addToUnsets(unsetEntry); } - return Status::OK(); + return status; } } // namespace mongo diff --git a/src/mongo/db/ops/modifier_rename.h b/src/mongo/db/ops/modifier_rename.h index ff6dad0fa1a..9a80f0ae0f2 100644 --- a/src/mongo/db/ops/modifier_rename.h +++ b/src/mongo/db/ops/modifier_rename.h @@ -27,6 +27,8 @@ namespace mongo { + class LogBuilder; + /** * The $rename modifier moves the field from source to the destination to perform * the rename. @@ -68,7 +70,7 @@ namespace mongo { * For the oplog entry we will generate an $unset on the 'from' field, and $set for * the 'to' field. If no 'from' element is found then function will return immediately. */ - virtual Status log(mutablebson::Element logRoot) const; + virtual Status log(LogBuilder* logBuilder) const; private: diff --git a/src/mongo/db/ops/modifier_rename_test.cpp b/src/mongo/db/ops/modifier_rename_test.cpp index e53a0349f73..6c6065c2303 100644 --- a/src/mongo/db/ops/modifier_rename_test.cpp +++ b/src/mongo/db/ops/modifier_rename_test.cpp @@ -23,6 +23,7 @@ #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/platform/cstdint.h" #include "mongo/unittest/unittest.h" @@ -30,6 +31,7 @@ namespace { using mongo::BSONObj; using mongo::fromjson; + using mongo::LogBuilder; using mongo::ModifierInterface; using mongo::NumberInt; using mongo::ModifierRename; @@ -59,8 +61,8 @@ namespace { return _mod.apply(); } - Status log(Element logRoot) const { - return _mod.log(logRoot); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } ModifierRename& mod() { return _mod; } @@ -98,8 +100,9 @@ namespace { ASSERT_OK(setMod.prepare(doc.root(), "", &execInfo)); Document logDoc; + LogBuilder logBuilder(logDoc.root()); BSONObj logObj = fromjson("{}"); - ASSERT_OK(setMod.log(logDoc.root())); + ASSERT_OK(setMod.log(&logBuilder)); ASSERT_EQUALS(logDoc, logObj); } @@ -134,8 +137,9 @@ namespace { ASSERT_EQUALS(doc, fromjson("{b:2}")); Document logDoc; + LogBuilder logBuilder(logDoc.root()); BSONObj logObj = fromjson("{$set:{ 'b': 2}, $unset: {'a': true}}"); - ASSERT_OK(setMod.log(logDoc.root())); + ASSERT_OK(setMod.log(&logBuilder)); ASSERT_EQUALS(logDoc, logObj); } @@ -155,8 +159,9 @@ namespace { ASSERT_EQUALS(doc, fromjson("{b:2}")); Document logDoc; + LogBuilder logBuilder(logDoc.root()); BSONObj logObj = fromjson("{$set:{ 'b': 2}, $unset: {'a': true}}"); - ASSERT_OK(setMod.log(logDoc.root())); + ASSERT_OK(setMod.log(&logBuilder)); ASSERT_EQUALS(logDoc, logObj); } @@ -176,8 +181,9 @@ namespace { ASSERT_EQUALS(doc, fromjson("{a: {}, b:{ d: 6}}")); Document logDoc; + LogBuilder logBuilder(logDoc.root()); BSONObj logObj = fromjson("{$set:{ 'b': {d: 6}}, $unset: {'a.c': true}}"); - ASSERT_OK(setMod.log(logDoc.root())); + ASSERT_OK(setMod.log(&logBuilder)); ASSERT_EQUALS(logDoc, logObj); } @@ -197,8 +203,9 @@ namespace { ASSERT_EQUALS(doc, fromjson("{b:1, c: { r: { d: 2}}}")); Document logDoc; + LogBuilder logBuilder(logDoc.root()); BSONObj logObj = fromjson("{$set:{ 'c.r.d': 2}, $unset: {'a': true}}"); - ASSERT_OK(setMod.log(logDoc.root())); + ASSERT_OK(setMod.log(&logBuilder)); ASSERT_EQUALS(logDoc, logObj); } @@ -218,8 +225,9 @@ namespace { ASSERT_EQUALS(doc, fromjson("{b: {c: {d: 2}}}")); Document logDoc; + LogBuilder logBuilder(logDoc.root()); BSONObj logObj = fromjson("{$set:{ 'b.c.d': 2}, $unset: {'a': true}}"); - ASSERT_OK(setMod.log(logDoc.root())); + ASSERT_OK(setMod.log(&logBuilder)); ASSERT_EQUALS(logDoc, logObj); } @@ -239,8 +247,9 @@ namespace { ASSERT_EQUALS(doc, fromjson("{b: {c: {d: [ {a:2, b:1} ]}}}")); Document logDoc; + LogBuilder logBuilder(logDoc.root()); BSONObj logObj = fromjson("{$set:{ 'b.c.d': [ {a:2, b:1} ]}, $unset: {'a': true}}"); - ASSERT_OK(setMod.log(logDoc.root())); + ASSERT_OK(setMod.log(&logBuilder)); ASSERT_EQUALS(logDoc, logObj); } diff --git a/src/mongo/db/ops/modifier_set.cpp b/src/mongo/db/ops/modifier_set.cpp index d940baf4a3d..2de14dc020b 100644 --- a/src/mongo/db/ops/modifier_set.cpp +++ b/src/mongo/db/ops/modifier_set.cpp @@ -19,6 +19,7 @@ #include "mongo/base/error_codes.h" #include "mongo/bson/mutable/document.h" #include "mongo/db/ops/field_checker.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/ops/path_support.h" namespace mongo { @@ -227,34 +228,24 @@ namespace mongo { elemToSet); } - Status ModifierSet::log(mutablebson::Element logRoot) const { + Status ModifierSet::log(LogBuilder* logBuilder) const { // We'd like to create an entry such as {$set: {<fieldname>: <value>}} under 'logRoot'. // We start by creating the {$set: ...} Element. - mutablebson::Document& doc = logRoot.getDocument(); - mutablebson::Element setElement = doc.makeElementObject("$set"); - if (!setElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot create log entry for $set mod"); - } + mutablebson::Document& doc = logBuilder->getDocument(); - // Then we create the {<fieldname>: <value>} Element. Note that we log the mod with a + // Create the {<fieldname>: <value>} Element. Note that we log the mod with a // dotted field, if it was applied over a dotted field. The rationale is that the // secondary may be in a different state than the primary and thus make different // decisions about creating the intermediate path in _fieldRef or not. - mutablebson::Element logElement = doc.makeElementWithNewFieldName(_fieldRef.dottedField(), - _val); + mutablebson::Element logElement = doc.makeElementWithNewFieldName( + _fieldRef.dottedField(), _val); + if (!logElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create details for $set mod"); } - // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} one. - Status status = setElement.pushBack(logElement); - if (!status.isOK()) { - return status; - } - - // And attach the result under the 'logRoot' Element provided. - return logRoot.pushBack(setElement); + return logBuilder->addToSets(logElement); } } // namespace mongo diff --git a/src/mongo/db/ops/modifier_set.h b/src/mongo/db/ops/modifier_set.h index 6e5a2df1e0f..71d44abe9cd 100644 --- a/src/mongo/db/ops/modifier_set.h +++ b/src/mongo/db/ops/modifier_set.h @@ -27,6 +27,8 @@ namespace mongo { + class LogBuilder; + class ModifierSet : public ModifierInterface { MONGO_DISALLOW_COPYING(ModifierSet); @@ -70,7 +72,7 @@ namespace mongo { * Adds a log entry to logRoot corresponding to the operation applied here. Returns OK * if successful or a status describing the error. */ - virtual Status log(mutablebson::Element logRoot) const; + virtual Status log(LogBuilder* logBuilder) const; private: diff --git a/src/mongo/db/ops/modifier_set_test.cpp b/src/mongo/db/ops/modifier_set_test.cpp index 1e036fb5987..daa2f1ae852 100644 --- a/src/mongo/db/ops/modifier_set_test.cpp +++ b/src/mongo/db/ops/modifier_set_test.cpp @@ -24,6 +24,7 @@ #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/platform/cstdint.h" #include "mongo/unittest/unittest.h" @@ -31,6 +32,7 @@ namespace { using mongo::BSONObj; using mongo::fromjson; + using mongo::LogBuilder; using mongo::ModifierInterface; using mongo::NumberInt; using mongo::ModifierSet; @@ -64,8 +66,8 @@ namespace { return _mod.apply(); } - Status log(Element logRoot) const { - return _mod.log(logRoot); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } ModifierSet& mod() { return _mod; } @@ -204,7 +206,8 @@ namespace { ASSERT_OK(setMod.prepare(doc.root(), "", &dummy)); Document logDoc; - ASSERT_OK(setMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(setMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: 2}}"), logDoc); } @@ -514,7 +517,8 @@ namespace { ASSERT_OK(setMod.prepare(doc.root(), "", &execInfo)); Document logDoc; - ASSERT_OK(setMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(setMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), logDoc); } @@ -528,7 +532,8 @@ namespace { ASSERT_OK(setMod.prepare(doc.root(), "", &execInfo)); Document logDoc; - ASSERT_OK(setMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(setMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), logDoc); } @@ -542,7 +547,8 @@ namespace { ASSERT_OK(setMod.prepare(doc.root(), "", &execInfo)); Document logDoc; - ASSERT_OK(setMod.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(setMod.log(&logBuilder)); ASSERT_EQUALS(countChildren(logDoc.root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), logDoc); } diff --git a/src/mongo/db/ops/modifier_unset.cpp b/src/mongo/db/ops/modifier_unset.cpp index 6e029cdf1ea..1431f06bcc2 100644 --- a/src/mongo/db/ops/modifier_unset.cpp +++ b/src/mongo/db/ops/modifier_unset.cpp @@ -19,6 +19,7 @@ #include "mongo/base/error_codes.h" #include "mongo/bson/mutable/document.h" #include "mongo/db/ops/field_checker.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/ops/path_support.h" namespace mongo { @@ -163,17 +164,13 @@ namespace mongo { } } - Status ModifierUnset::log(mutablebson::Element logRoot) const { + Status ModifierUnset::log(LogBuilder* logBuilder) const { // We'd like to create an entry such as {$unset: {<fieldname>: 1}} under 'logRoot'. // We start by creating the {$unset: ...} Element. - mutablebson::Document& doc = logRoot.getDocument(); - mutablebson::Element unsetElement = doc.makeElementObject("$unset"); - if (!unsetElement.ok()) { - return Status(ErrorCodes::InternalError, "cannot create log entry for $unset mod"); - } + mutablebson::Document& doc = logBuilder->getDocument(); - // Then we create the {<fieldname>: <value>} Element. Note that <fieldname> must be a + // Create the {<fieldname>: <value>} Element. Note that <fieldname> must be a // dotted field, and not only the last part of that field. The rationale here is that // somoene picking up this log entry -- e.g., a secondary -- must be capable of doing // the same path find/creation that was done in the previous calls here. @@ -182,14 +179,7 @@ namespace mongo { return Status(ErrorCodes::InternalError, "cannot create log details for $unset mod"); } - // Now, we attach the {<fieldname>: `} Element under the {$unset: ...} one. - Status status = unsetElement.pushBack(logElement); - if (!status.isOK()) { - return status; - } - - // And attach the result under the 'logRoot' Element provided. - return logRoot.pushBack(unsetElement); + return logBuilder->addToUnsets(logElement); } } // namespace mongo diff --git a/src/mongo/db/ops/modifier_unset.h b/src/mongo/db/ops/modifier_unset.h index 0b591969cf9..a4a64135284 100644 --- a/src/mongo/db/ops/modifier_unset.h +++ b/src/mongo/db/ops/modifier_unset.h @@ -27,6 +27,8 @@ namespace mongo { + class LogBuilder; + class ModifierUnset : public ModifierInterface { MONGO_DISALLOW_COPYING(ModifierUnset); @@ -65,7 +67,7 @@ namespace mongo { /** * Adds the exact $unset mod to the log. */ - virtual Status log(mutablebson::Element logRoot) const; + virtual Status log(LogBuilder* logBuilder) const; private: diff --git a/src/mongo/db/ops/modifier_unset_test.cpp b/src/mongo/db/ops/modifier_unset_test.cpp index bc0058ae3fe..f2a1416a111 100644 --- a/src/mongo/db/ops/modifier_unset_test.cpp +++ b/src/mongo/db/ops/modifier_unset_test.cpp @@ -24,6 +24,7 @@ #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/platform/cstdint.h" #include "mongo/unittest/unittest.h" @@ -32,6 +33,7 @@ namespace { using mongo::Array; using mongo::BSONObj; using mongo::fromjson; + using mongo::LogBuilder; using mongo::ModifierInterface; using mongo::ModifierUnset; using mongo::Status; @@ -59,8 +61,8 @@ namespace { return _mod.apply(); } - Status log(Element logRoot) const { - return _mod.log(logRoot); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } ModifierUnset& mod() { return _mod; } @@ -161,7 +163,8 @@ namespace { ASSERT_FALSE(execInfo.noOp); Document logDoc; - ASSERT_OK(modUnset.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(modUnset.log(&logBuilder)); ASSERT_EQUALS(modUnset.modObj(), logDoc); } @@ -326,7 +329,8 @@ namespace { ASSERT_OK(modUnset.prepare(doc.root(), "", &execInfo)); Document logDoc; - ASSERT_OK(modUnset.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(modUnset.log(&logBuilder)); ASSERT_EQUALS(modUnset.modObj(), logDoc); } @@ -411,7 +415,8 @@ namespace { ASSERT_FALSE(execInfo.noOp); Document logDoc; - ASSERT_OK(modUnset.log(logDoc.root())); + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(modUnset.log(&logBuilder)); ASSERT_EQUALS(fromjson("{$unset: {'a.0.b': 1}}"), logDoc); } diff --git a/src/mongo/db/ops/update_driver.cpp b/src/mongo/db/ops/update_driver.cpp index c0987e28c1b..893bdb2f640 100644 --- a/src/mongo/db/ops/update_driver.cpp +++ b/src/mongo/db/ops/update_driver.cpp @@ -21,6 +21,7 @@ #include "mongo/bson/mutable/document.h" #include "mongo/db/field_ref.h" #include "mongo/db/field_ref_set.h" +#include "mongo/db/ops/log_builder.h" #include "mongo/db/ops/modifier_object_replace.h" #include "mongo/db/ops/modifier_table.h" #include "mongo/util/embedded_builder.h" @@ -167,6 +168,13 @@ namespace mongo { FieldRefSet targetFields; _affectIndices = false; + // TODO: Should logBuilder own the document? Should we hold it by pointer to avoid + // creating it if we are not interested in logging? Or maybe by boost optional? For + // now, it is logically harmless to construct this and not use it, but a Document + // object isn't cheap, so we should avoid building it if we can. + mutablebson::Document logDoc; + LogBuilder logBuilder(logDoc.root()); + // Ask each of the mods to type check whether they can operate over the current document // and, if so, to change that document accordingly. for (vector<ModifierInterface*>::iterator it = _mods.begin(); it != _mods.end(); ++it) { @@ -218,25 +226,25 @@ namespace mongo { } if (!execInfo.noOp && validContext) { - Status status = (*it)->apply(); + status = (*it)->apply(); if (!status.isOK()) { return status; } } - } - // If we require a replication oplog entry for this update, go ahead and generate one. - if (_logOp && logOpRec) { - mutablebson::Document logDoc; - for (vector<ModifierInterface*>::iterator it = _mods.begin(); it != _mods.end(); ++it) { - Status status = (*it)->log(logDoc.root()); + // If we require a replication oplog entry for this update, go ahead and generate one. + if (_logOp && logOpRec) { + status = (*it)->log(&logBuilder); if (!status.isOK()) { return status; } } - *logOpRec = logDoc.getObject(); + } + if (_logOp && logOpRec) + *logOpRec = logDoc.getObject(); + return Status::OK(); } |