summaryrefslogtreecommitdiff
path: root/src/mongo/db/ops/modifier_inc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/ops/modifier_inc.cpp')
-rw-r--r--src/mongo/db/ops/modifier_inc.cpp283
1 files changed, 0 insertions, 283 deletions
diff --git a/src/mongo/db/ops/modifier_inc.cpp b/src/mongo/db/ops/modifier_inc.cpp
deleted file mode 100644
index e708afeb941..00000000000
--- a/src/mongo/db/ops/modifier_inc.cpp
+++ /dev/null
@@ -1,283 +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_inc.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 mb = mutablebson;
-namespace str = mongoutils::str;
-
-struct ModifierInc::PreparedState {
- PreparedState(mutablebson::Document& doc)
- : doc(doc), idxFound(0), elemFound(doc.end()), newValue(), noOp(false) {}
-
- // Document that is going to be changed.
- mutablebson::Document& doc;
-
- // Index in _fieldRef for which an Element exist in the document.
- size_t idxFound;
-
- // Element corresponding to _fieldRef[0.._idxFound].
- mutablebson::Element elemFound;
-
- // Value to be applied
- SafeNum newValue;
-
- // This $inc is a no-op?
- bool noOp;
-};
-
-ModifierInc::ModifierInc(ModifierIncMode mode)
- : ModifierInterface(), _mode(mode), _fieldRef(), _posDollar(0), _val() {}
-
-ModifierInc::~ModifierInc() {}
-
-Status ModifierInc::init(const BSONElement& modExpr, const Options& opts, bool* positional) {
- //
- // field name analysis
- //
-
- // Perform standard field name and updateable checks.
- _fieldRef.parse(modExpr.fieldName());
- Status status = fieldchecker::isUpdatable(_fieldRef);
- if (!status.isOK()) {
- return status;
- }
-
- // If a $-positional operator was used, get the index in which it occurred
- // and ensure only one occurrence.
- size_t foundCount;
- bool foundDollar = fieldchecker::isPositional(_fieldRef, &_posDollar, &foundCount);
-
- if (positional)
- *positional = foundDollar;
-
- if (foundDollar && foundCount > 1) {
- return Status(ErrorCodes::BadValue,
- str::stream() << "Too many positional (i.e. '$') elements found in path '"
- << _fieldRef.dottedField()
- << "'");
- }
-
- //
- // value analysis
- //
-
- if (!modExpr.isNumber()) {
- // TODO: Context for mod error messages would be helpful
- // include mod code, etc.
- return Status(ErrorCodes::TypeMismatch,
- str::stream() << "Cannot " << (_mode == MODE_INC ? "increment" : "multiply")
- << " with non-numeric argument: {"
- << modExpr
- << "}");
- }
-
- _val = modExpr;
- dassert(_val.isValid());
-
- return Status::OK();
-}
-
-Status ModifierInc::prepare(mutablebson::Element root,
- StringData matchedField,
- ExecInfo* execInfo) {
- _preparedState.reset(new PreparedState(root.getDocument()));
-
- // If we have a $-positional field, it is time to bind it to an actual field part.
- if (_posDollar) {
- if (matchedField.empty()) {
- return Status(ErrorCodes::BadValue,
- str::stream() << "The positional operator did not find the match "
- "needed from the query. Unexpanded update: "
- << _fieldRef.dottedField());
- }
- _fieldRef.setPart(_posDollar, matchedField);
- }
-
- // Locate the field name in 'root'. Note that we may not have all the parts in the path
- // in the doc -- which is fine. Our goal now is merely to reason about whether this mod
- // apply is a noOp or whether is can be in place. The remaining path, if missing, will
- // be created during the apply.
- Status status = pathsupport::findLongestPrefix(
- _fieldRef, root, &_preparedState->idxFound, &_preparedState->elemFound);
- const auto elemFoundIsArray =
- _preparedState->elemFound.ok() && _preparedState->elemFound.getType() == BSONType::Array;
-
- // FindLongestPrefix may say the path does not exist at all, which is fine here, or
- // that the path was not viable or otherwise wrong, in which case, the mod cannot
- // proceed.
- if (status.code() == ErrorCodes::NonExistentPath) {
- _preparedState->elemFound = root.getDocument().end();
- } 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] = &_fieldRef;
-
- // Capture the value we are going to write. At this point, there may not be a value
- // against which to operate, so the result will be simply _val.
- _preparedState->newValue = _val;
-
- //
- // in-place and no-op logic
- //
- // If the field path is not fully present, then this mod cannot be in place, nor is a
- // noOp.
- if (!_preparedState->elemFound.ok() || _preparedState->idxFound < (_fieldRef.numParts() - 1)) {
- // For multiplication, we treat ops against missing as yielding zero. We take
- // advantage here of the promotion rules for SafeNum; the expression below will
- // always yield a zero of the same type of operand that the user provided
- // (e.g. double).
- if (_mode == MODE_MUL)
- _preparedState->newValue *= SafeNum(static_cast<int32_t>(0));
-
- if (elemFoundIsArray) {
- // Report that an existing array will gain a new element as a result of this mod.
- execInfo->indexOfArrayWithNewElement[0] = _preparedState->idxFound;
- }
-
- return Status::OK();
- }
-
- // If the value being $inc'ed is the same as the one already in the doc, than this is a
- // noOp.
- if (!_preparedState->elemFound.isNumeric()) {
- mb::Element idElem = mb::findFirstChildNamed(root, "_id");
- return Status(ErrorCodes::TypeMismatch,
- str::stream() << "Cannot apply " << (_mode == MODE_INC ? "$inc" : "$mul")
- << " to a value of non-numeric type. {"
- << idElem.toString()
- << "} has the field '"
- << _preparedState->elemFound.getFieldName()
- << "' of non-numeric type "
- << typeName(_preparedState->elemFound.getType()));
- }
- const SafeNum currentValue = _preparedState->elemFound.getValueSafeNum();
-
- // Update newValue w.r.t to the current value of the found element.
- if (_mode == MODE_INC)
- _preparedState->newValue += currentValue;
- else
- _preparedState->newValue *= currentValue;
-
- // If the result of the addition is invalid, we must return an error.
- if (!_preparedState->newValue.isValid()) {
- mb::Element idElem = mb::findFirstChildNamed(root, "_id");
- return Status(ErrorCodes::BadValue,
- str::stream() << "Failed to apply $inc operations to current value ("
- << currentValue.debugString()
- << ") for document {"
- << idElem.toString()
- << "}");
- }
-
- // If the values are identical (same type, same value), then this is a no-op.
- if (_preparedState->newValue.isIdentical(currentValue)) {
- _preparedState->noOp = execInfo->noOp = true;
- return Status::OK();
- }
-
- return Status::OK();
-}
-
-Status ModifierInc::apply() const {
- dassert(_preparedState->noOp == false);
-
- // If there's no need to create any further field part, the $inc is simply a value
- // assignment.
- if (_preparedState->elemFound.ok() && _preparedState->idxFound == (_fieldRef.numParts() - 1)) {
- return _preparedState->elemFound.setValueSafeNum(_preparedState->newValue);
- }
-
- //
- // Complete document path logic
- //
-
- // Creates the final element that's going to be $set in 'doc'.
- mutablebson::Document& doc = _preparedState->doc;
- StringData lastPart = _fieldRef.getPart(_fieldRef.numParts() - 1);
- mutablebson::Element elemToSet = doc.makeElementSafeNum(lastPart, _preparedState->newValue);
- if (!elemToSet.ok()) {
- return Status(ErrorCodes::InternalError, "can't create new element");
- }
-
- // Now, we can be in two cases here, as far as attaching the element being set goes:
- // (a) none of the parts in the element's path exist, or (b) some parts of the path
- // exist but not all.
- if (!_preparedState->elemFound.ok()) {
- _preparedState->elemFound = doc.root();
- _preparedState->idxFound = 0;
- } else {
- _preparedState->idxFound++;
- }
-
- // createPathAt() will complete the path and attach 'elemToSet' at the end of it.
- return pathsupport::createPathAt(
- _fieldRef, _preparedState->idxFound, _preparedState->elemFound, elemToSet)
- .getStatus();
-}
-
-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 = logBuilder->getDocument();
-
- // Then we create the {<fieldname>: <value>} Element.
- mutablebson::Element logElement =
- doc.makeElementSafeNum(_fieldRef.dottedField(), _preparedState->newValue);
-
- if (!logElement.ok()) {
- return Status(ErrorCodes::InternalError,
- str::stream() << "Could not append entry to "
- << (_mode == MODE_INC ? "$inc" : "$mul")
- << " oplog entry: "
- << "set '"
- << _fieldRef.dottedField()
- << "' -> "
- << _preparedState->newValue.debugString());
- }
-
- // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} segment.
- return logBuilder->addToSets(logElement);
-}
-
-} // namespace mongo