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.cpp304
1 files changed, 0 insertions, 304 deletions
diff --git a/src/mongo/db/ops/modifier_rename.cpp b/src/mongo/db/ops/modifier_rename.cpp
deleted file mode 100644
index 375edd4d812..00000000000
--- a/src/mongo/db/ops/modifier_rename.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/**
- * 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/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/db/ops/modifier_rename.h"
-
-#include "mongo/base/error_codes.h"
-#include "mongo/bson/mutable/algorithm.h"
-#include "mongo/bson/mutable/document.h"
-#include "mongo/db/update/field_checker.h"
-#include "mongo/db/update/log_builder.h"
-#include "mongo/db/update/path_support.h"
-#include "mongo/util/mongoutils/str.h"
-
-namespace mongo {
-
-namespace str = mongoutils::str;
-
-struct ModifierRename::PreparedState {
- 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;
-
- // The element to rename
- mutablebson::Element fromElemFound;
-
- // Index in _fieldRef for which an Element exist in the document.
- size_t toIdxFound;
-
- // Element to remove (in the destination position)
- mutablebson::Element toElemFound;
-
- // Was apply called?
- bool applyCalled;
-};
-
-ModifierRename::ModifierRename() : _fromFieldRef(), _toFieldRef() {}
-
-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);
- }
-
- if (modExpr.valueStringData().find('\0') != std::string::npos) {
- return Status(ErrorCodes::BadValue,
- "The 'to' field for $rename cannot contain an embedded null byte");
- }
-
- // Extract the field names from the mod expression
-
- _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;
-
- // 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;
- }
-
- return Status::OK();
- }
-
- // 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;
- }
-
- 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();
- }
- }
-
- // 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;
-
- execInfo->noOp = false;
-
- return Status::OK();
-}
-
-Status ModifierRename::apply() const {
- dassert(_preparedState->fromElemFound.ok());
-
- _preparedState->applyCalled = true;
-
- // Remove from source
- Status removeStatus = _preparedState->fromElemFound.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) {
- // Set destination element to the value of the source element.
- return _preparedState->toElemFound.setValueElement(_preparedState->fromElemFound);
- }
-
- // 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)
- .getStatus();
-}
-
-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();
-
- // debug assert if apply not called, since we found an element to move.
- dassert(_preparedState->applyCalled);
-
- const bool isPrefix = _fromFieldRef.isPrefixOf(_toFieldRef);
- const StringData setPath = (isPrefix ? _fromFieldRef : _toFieldRef).dottedField();
- const StringData unsetPath = isPrefix ? StringData() : _fromFieldRef.dottedField();
- const bool doUnset = !isPrefix;
-
- // 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();
-
- // 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;
-}
-
-} // namespace mongo