summaryrefslogtreecommitdiff
path: root/src/mongo/db/ops/modifier_rename.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/ops/modifier_rename.cpp')
-rw-r--r--src/mongo/db/ops/modifier_rename.cpp429
1 files changed, 204 insertions, 225 deletions
diff --git a/src/mongo/db/ops/modifier_rename.cpp b/src/mongo/db/ops/modifier_rename.cpp
index 13ec0c70beb..123b253f227 100644
--- a/src/mongo/db/ops/modifier_rename.cpp
+++ b/src/mongo/db/ops/modifier_rename.cpp
@@ -38,279 +38,258 @@
namespace mongo {
- namespace str = mongoutils::str;
+namespace str = mongoutils::str;
- struct ModifierRename::PreparedState {
+struct ModifierRename::PreparedState {
+ PreparedState(mutablebson::Element root)
+ : doc(root.getDocument()),
+ fromElemFound(doc.end()),
+ toIdxFound(0),
+ toElemFound(doc.end()),
+ applyCalled(false) {}
- PreparedState(mutablebson::Element root)
- : doc(root.getDocument())
- , fromElemFound(doc.end())
- , toIdxFound(0)
- , toElemFound(doc.end())
- , applyCalled(false){
- }
-
- // Document that is going to be changed.
- mutablebson::Document& doc;
+ // Document that is going to be changed.
+ mutablebson::Document& doc;
- // The element to rename
- mutablebson::Element fromElemFound;
+ // The element to rename
+ mutablebson::Element fromElemFound;
- // Index in _fieldRef for which an Element exist in the document.
- size_t toIdxFound;
+ // Index in _fieldRef for which an Element exist in the document.
+ size_t toIdxFound;
- // Element to remove (in the destination position)
- mutablebson::Element toElemFound;
+ // Element to remove (in the destination position)
+ mutablebson::Element toElemFound;
- // Was apply called?
- bool applyCalled;
+ // Was apply called?
+ bool applyCalled;
+};
- };
+ModifierRename::ModifierRename() : _fromFieldRef(), _toFieldRef() {}
- ModifierRename::ModifierRename()
- : _fromFieldRef()
- , _toFieldRef() {
- }
+ModifierRename::~ModifierRename() {}
- ModifierRename::~ModifierRename() {
+Status ModifierRename::init(const BSONElement& modExpr, const Options& opts, bool* positional) {
+ if (modExpr.type() != String) {
+ return Status(ErrorCodes::BadValue,
+ str::stream() << "The 'to' field for $rename must be a string: " << modExpr);
}
- Status ModifierRename::init(const BSONElement& modExpr, const Options& opts,
- bool* positional) {
-
- if (modExpr.type() != String) {
- return Status(ErrorCodes::BadValue,
- str::stream() << "The 'to' field for $rename must be a string: "
- << modExpr);
- }
+ // Extract the field names from the mod expression
- // Extract the field names from the mod expression
+ _fromFieldRef.parse(modExpr.fieldName());
+ Status status = fieldchecker::isUpdatable(_fromFieldRef);
+ if (!status.isOK())
+ return status;
- _fromFieldRef.parse(modExpr.fieldName());
- Status status = fieldchecker::isUpdatable(_fromFieldRef);
- if (!status.isOK())
- return status;
+ _toFieldRef.parse(modExpr.String());
+ status = fieldchecker::isUpdatable(_toFieldRef);
+ if (!status.isOK())
+ return status;
- _toFieldRef.parse(modExpr.String());
- status = fieldchecker::isUpdatable(_toFieldRef);
- if (!status.isOK())
+ // TODO: Remove this restriction and make a noOp to lift restriction
+ // Old restriction is that if the fields are the same then it is not allowed.
+ if (_fromFieldRef == _toFieldRef)
+ return Status(ErrorCodes::BadValue,
+ str::stream()
+ << "The source and target field for $rename must differ: " << modExpr);
+
+ // TODO: Remove this restriction by allowing moving deeping from the 'from' path
+ // Old restriction is that if the to/from is on the same path it fails
+ if (_fromFieldRef.isPrefixOf(_toFieldRef) || _toFieldRef.isPrefixOf(_fromFieldRef)) {
+ return Status(ErrorCodes::BadValue,
+ str::stream() << "The source and target field for $rename must "
+ "not be on the same path: " << modExpr);
+ }
+ // TODO: We can remove this restriction as long as there is only one,
+ // or it is the same array -- should think on this a bit.
+ //
+ // If a $-positional operator was used it is an error
+ size_t dummyPos;
+ if (fieldchecker::isPositional(_fromFieldRef, &dummyPos))
+ return Status(ErrorCodes::BadValue,
+ str::stream() << "The source field for $rename may not be dynamic: "
+ << _fromFieldRef.dottedField());
+ else if (fieldchecker::isPositional(_toFieldRef, &dummyPos))
+ return Status(ErrorCodes::BadValue,
+ str::stream() << "The destination field for $rename may not be dynamic: "
+ << _toFieldRef.dottedField());
+
+ if (positional)
+ *positional = false;
+
+ return Status::OK();
+}
+
+Status ModifierRename::prepare(mutablebson::Element root,
+ StringData matchedField,
+ ExecInfo* execInfo) {
+ // Rename doesn't work with positional fields ($)
+ dassert(matchedField.empty());
+
+ _preparedState.reset(new PreparedState(root));
+
+ // Locate the to field name in 'root', which must exist.
+ size_t fromIdxFound;
+ Status status = pathsupport::findLongestPrefix(
+ _fromFieldRef, root, &fromIdxFound, &_preparedState->fromElemFound);
+
+ const bool sourceExists =
+ (_preparedState->fromElemFound.ok() && fromIdxFound == (_fromFieldRef.numParts() - 1));
+
+ // If we can't find the full element in the from field then we can't do anything.
+ if (!status.isOK() || !sourceExists) {
+ execInfo->noOp = true;
+ _preparedState->fromElemFound = root.getDocument().end();
+
+ // TODO: remove this special case from existing behavior
+ if (status.code() == ErrorCodes::PathNotViable) {
return status;
-
- // TODO: Remove this restriction and make a noOp to lift restriction
- // Old restriction is that if the fields are the same then it is not allowed.
- if (_fromFieldRef == _toFieldRef)
- return Status(ErrorCodes::BadValue,
- str::stream() << "The source and target field for $rename must differ: "
- << modExpr);
-
- // TODO: Remove this restriction by allowing moving deeping from the 'from' path
- // Old restriction is that if the to/from is on the same path it fails
- if (_fromFieldRef.isPrefixOf(_toFieldRef) || _toFieldRef.isPrefixOf(_fromFieldRef)){
- return Status(ErrorCodes::BadValue,
- str::stream() << "The source and target field for $rename must "
- "not be on the same path: "
- << modExpr);
}
- // TODO: We can remove this restriction as long as there is only one,
- // or it is the same array -- should think on this a bit.
- //
- // If a $-positional operator was used it is an error
- size_t dummyPos;
- if (fieldchecker::isPositional(_fromFieldRef, &dummyPos))
- return Status(ErrorCodes::BadValue,
- str::stream() << "The source field for $rename may not be dynamic: "
- << _fromFieldRef.dottedField());
- else if (fieldchecker::isPositional(_toFieldRef, &dummyPos))
- return Status(ErrorCodes::BadValue,
- str::stream() << "The destination field for $rename may not be dynamic: "
- << _toFieldRef.dottedField());
-
- if (positional)
- *positional = false;
return Status::OK();
}
- Status ModifierRename::prepare(mutablebson::Element root,
- StringData matchedField,
- ExecInfo* execInfo) {
- // Rename doesn't work with positional fields ($)
- dassert(matchedField.empty());
-
- _preparedState.reset(new PreparedState(root));
-
- // Locate the to field name in 'root', which must exist.
- size_t fromIdxFound;
- Status status = pathsupport::findLongestPrefix(_fromFieldRef,
- root,
- &fromIdxFound,
- &_preparedState->fromElemFound);
+ // Ensure no array in ancestry if what we found is not at the root
+ mutablebson::Element curr = _preparedState->fromElemFound.parent();
+ if (curr != curr.getDocument().root())
+ while (curr.ok() && (curr != curr.getDocument().root())) {
+ if (curr.getType() == Array)
+ return Status(ErrorCodes::BadValue,
+ str::stream() << "The source field cannot be an array element, '"
+ << _fromFieldRef.dottedField() << "' in doc with "
+ << findElementNamed(root.leftChild(), "_id").toString()
+ << " has an array field called '" << curr.getFieldName()
+ << "'");
+ curr = curr.parent();
+ }
- const bool sourceExists = (_preparedState->fromElemFound.ok() &&
- fromIdxFound == (_fromFieldRef.numParts() - 1));
+ // "To" side validation below
- // If we can't find the full element in the from field then we can't do anything.
- if (!status.isOK() || !sourceExists) {
- execInfo->noOp = true;
- _preparedState->fromElemFound = root.getDocument().end();
+ status = pathsupport::findLongestPrefix(
+ _toFieldRef, root, &_preparedState->toIdxFound, &_preparedState->toElemFound);
- // TODO: remove this special case from existing behavior
- if (status.code() == ErrorCodes::PathNotViable) {
- return status;
- }
+ // FindLongestPrefix may return not viable or any other error and then we cannot proceed.
+ if (status.code() == ErrorCodes::NonExistentPath) {
+ // Not an error condition as we will create the "to" path as needed.
+ } else if (!status.isOK()) {
+ return status;
+ }
- return Status::OK();
+ const bool destExists = _preparedState->toElemFound.ok() &&
+ (_preparedState->toIdxFound == (_toFieldRef.numParts() - 1));
+
+ // Ensure no array in ancestry of "to" Element
+ // Set to either parent, or node depending on if the full path element was found
+ curr = (destExists ? _preparedState->toElemFound.parent() : _preparedState->toElemFound);
+ if (curr != curr.getDocument().root()) {
+ while (curr.ok()) {
+ if (curr.getType() == Array)
+ return Status(ErrorCodes::BadValue,
+ str::stream() << "The destination field cannot be an array element, '"
+ << _fromFieldRef.dottedField() << "' in doc with "
+ << findElementNamed(root.leftChild(), "_id").toString()
+ << " has an array field called '" << curr.getFieldName()
+ << "'");
+ curr = curr.parent();
}
+ }
- // Ensure no array in ancestry if what we found is not at the root
- mutablebson::Element curr = _preparedState->fromElemFound.parent();
- if (curr != curr.getDocument().root())
- while (curr.ok() && (curr != curr.getDocument().root())) {
- if (curr.getType() == Array)
- return Status(ErrorCodes::BadValue,
- str::stream() << "The source field cannot be an array element, '"
- << _fromFieldRef.dottedField() << "' in doc with "
- << findElementNamed(root.leftChild(), "_id").toString()
- << " has an array field called '" << curr.getFieldName() << "'");
- curr = curr.parent();
- }
-
- // "To" side validation below
-
- status = pathsupport::findLongestPrefix(_toFieldRef,
- root,
- &_preparedState->toIdxFound,
- &_preparedState->toElemFound);
-
- // FindLongestPrefix may return not viable or any other error and then we cannot proceed.
- if (status.code() == ErrorCodes::NonExistentPath) {
- // Not an error condition as we will create the "to" path as needed.
- } else if (!status.isOK()) {
- return status;
- }
+ // We register interest in the field name. The driver needs this info to sort out if
+ // there is any conflict among mods.
+ execInfo->fieldRef[0] = &_fromFieldRef;
+ execInfo->fieldRef[1] = &_toFieldRef;
- const bool destExists = _preparedState->toElemFound.ok() &&
- (_preparedState->toIdxFound == (_toFieldRef.numParts()-1));
-
- // Ensure no array in ancestry of "to" Element
- // Set to either parent, or node depending on if the full path element was found
- curr = (destExists ? _preparedState->toElemFound.parent() : _preparedState->toElemFound);
- if (curr != curr.getDocument().root()) {
- while (curr.ok()) {
- if (curr.getType() == Array)
- return Status(ErrorCodes::BadValue,
- str::stream()
- << "The destination field cannot be an array element, '"
- << _fromFieldRef.dottedField() << "' in doc with "
- << findElementNamed(root.leftChild(), "_id").toString()
- << " has an array field called '" << curr.getFieldName() << "'");
- curr = curr.parent();
- }
- }
+ execInfo->noOp = false;
- // We register interest in the field name. The driver needs this info to sort out if
- // there is any conflict among mods.
- execInfo->fieldRef[0] = &_fromFieldRef;
- execInfo->fieldRef[1] = &_toFieldRef;
+ return Status::OK();
+}
- execInfo->noOp = false;
+Status ModifierRename::apply() const {
+ dassert(_preparedState->fromElemFound.ok());
- return Status::OK();
- }
+ _preparedState->applyCalled = true;
- Status ModifierRename::apply() const {
- dassert(_preparedState->fromElemFound.ok());
+ // Remove from source
+ Status removeStatus = _preparedState->fromElemFound.remove();
+ if (!removeStatus.isOK()) {
+ return removeStatus;
+ }
- _preparedState->applyCalled = true;
+ // If there's no need to create any further field part, the op is simply a value
+ // assignment.
+ const bool destExists = _preparedState->toElemFound.ok() &&
+ (_preparedState->toIdxFound == (_toFieldRef.numParts() - 1));
- // Remove from source
- Status removeStatus = _preparedState->fromElemFound.remove();
+ if (destExists) {
+ removeStatus = _preparedState->toElemFound.remove();
if (!removeStatus.isOK()) {
return removeStatus;
}
+ }
- // If there's no need to create any further field part, the op is simply a value
- // assignment.
- const bool destExists = _preparedState->toElemFound.ok() &&
- (_preparedState->toIdxFound == (_toFieldRef.numParts()-1));
-
- if (destExists) {
- removeStatus = _preparedState->toElemFound.remove();
- if (!removeStatus.isOK()) {
- return removeStatus;
- }
- }
+ // Creates the final element that's going to be the in 'doc'.
+ mutablebson::Document& doc = _preparedState->doc;
+ StringData lastPart = _toFieldRef.getPart(_toFieldRef.numParts() - 1);
+ mutablebson::Element elemToSet =
+ doc.makeElementWithNewFieldName(lastPart, _preparedState->fromElemFound);
+ if (!elemToSet.ok()) {
+ return Status(ErrorCodes::InternalError, "can't create new element");
+ }
- // Creates the final element that's going to be the in 'doc'.
- mutablebson::Document& doc = _preparedState->doc;
- StringData lastPart = _toFieldRef.getPart(_toFieldRef.numParts()-1);
- mutablebson::Element elemToSet = doc.makeElementWithNewFieldName(
- lastPart,
- _preparedState->fromElemFound);
- if (!elemToSet.ok()) {
- return Status(ErrorCodes::InternalError, "can't create new element");
- }
+ // Find the new place to put the "to" element:
+ // createPathAt does not use existing prefix elements so we
+ // need to get the prefix match position for createPathAt below
+ size_t tempIdx = 0;
+ mutablebson::Element tempElem = doc.end();
+ Status status = pathsupport::findLongestPrefix(_toFieldRef, doc.root(), &tempIdx, &tempElem);
+
+ // createPathAt will complete the path and attach 'elemToSet' at the end of it.
+ return pathsupport::createPathAt(_toFieldRef,
+ tempElem == doc.end() ? 0 : tempIdx + 1,
+ tempElem == doc.end() ? doc.root() : tempElem,
+ elemToSet);
+}
+
+Status ModifierRename::log(LogBuilder* logBuilder) const {
+ // If there was no element found then it was a noop, so return immediately
+ if (!_preparedState->fromElemFound.ok())
+ return Status::OK();
- // Find the new place to put the "to" element:
- // createPathAt does not use existing prefix elements so we
- // need to get the prefix match position for createPathAt below
- size_t tempIdx = 0;
- mutablebson::Element tempElem = doc.end();
- Status status = pathsupport::findLongestPrefix(_toFieldRef,
- doc.root(),
- &tempIdx,
- &tempElem);
-
- // createPathAt will complete the path and attach 'elemToSet' at the end of it.
- return pathsupport::createPathAt(_toFieldRef,
- tempElem == doc.end() ? 0 : tempIdx + 1,
- tempElem == doc.end() ? doc.root() : tempElem,
- elemToSet);
- }
+ // debug assert if apply not called, since we found an element to move.
+ dassert(_preparedState->applyCalled);
- Status ModifierRename::log(LogBuilder* logBuilder) const {
+ const bool isPrefix = _fromFieldRef.isPrefixOf(_toFieldRef);
+ const StringData setPath = (isPrefix ? _fromFieldRef : _toFieldRef).dottedField();
+ const StringData unsetPath = isPrefix ? StringData() : _fromFieldRef.dottedField();
+ const bool doUnset = !isPrefix;
- // If there was no element found then it was a noop, so return immediately
- if (!_preparedState->fromElemFound.ok())
- return Status::OK();
+ // We'd like to create an entry such as {$set: {<fieldname>: <value>}} under 'logRoot'.
+ // We start by creating the {$set: ...} Element.
+ mutablebson::Document& doc = logBuilder->getDocument();
- // debug assert if apply not called, since we found an element to move.
- dassert(_preparedState->applyCalled);
+ // 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());
- const bool isPrefix = _fromFieldRef.isPrefixOf(_toFieldRef);
- const StringData setPath =
- (isPrefix ? _fromFieldRef : _toFieldRef).dottedField();
- const StringData unsetPath =
- isPrefix ? StringData() : _fromFieldRef.dottedField();
- const bool doUnset = !isPrefix;
+ if (!logElement.ok()) {
+ return Status(ErrorCodes::InternalError, "cannot create details for $rename mod");
+ }
- // We'd like to create an entry such as {$set: {<fieldname>: <value>}} under 'logRoot'.
- // We start by creating the {$set: ...} Element.
- mutablebson::Document& doc = logBuilder->getDocument();
+ // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} section.
+ Status status = logBuilder->addToSets(logElement);
+ 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 logElement = doc.makeElementWithNewFieldName(
- setPath, _preparedState->fromElemFound.getValue());
-
- if (!logElement.ok()) {
- return Status(ErrorCodes::InternalError, "cannot create details for $rename mod");
- }
-
- // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} section.
- Status status = logBuilder->addToSets(logElement);
-
- 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.
- status = logBuilder->addToUnsets(unsetPath);
- }
-
- return status;
+ status = logBuilder->addToUnsets(unsetPath);
}
-} // namespace mongo
+ return status;
+}
+
+} // namespace mongo