diff options
Diffstat (limited to 'src/mongo/db/ops/modifier_current_date.cpp')
-rw-r--r-- | src/mongo/db/ops/modifier_current_date.cpp | 358 |
1 files changed, 167 insertions, 191 deletions
diff --git a/src/mongo/db/ops/modifier_current_date.cpp b/src/mongo/db/ops/modifier_current_date.cpp index f38cc4e4441..75d0be014e3 100644 --- a/src/mongo/db/ops/modifier_current_date.cpp +++ b/src/mongo/db/ops/modifier_current_date.cpp @@ -38,239 +38,215 @@ namespace mongo { - namespace str = mongoutils::str; +namespace str = mongoutils::str; - namespace { - const char kType[] = "$type"; - const char kDate[] = "date"; - const char kTimestamp[] = "timestamp"; - } +namespace { +const char kType[] = "$type"; +const char kDate[] = "date"; +const char kTimestamp[] = "timestamp"; +} - struct ModifierCurrentDate::PreparedState { +struct ModifierCurrentDate::PreparedState { + PreparedState(mutablebson::Document& doc) : doc(doc), elemFound(doc.end()), idxFound(0) {} - PreparedState(mutablebson::Document& doc) - : doc(doc) - , elemFound(doc.end()) - , idxFound(0) { - } + // Document that is going to be changed. + mutablebson::Document& doc; - // Document that is going to be changed. - mutablebson::Document& doc; + // Element corresponding to _fieldRef[0.._idxFound]. + mutablebson::Element elemFound; - // Element corresponding to _fieldRef[0.._idxFound]. - mutablebson::Element elemFound; + // Index in _fieldRef for which an Element exist in the document. + size_t idxFound; +}; - // Index in _fieldRef for which an Element exist in the document. - size_t idxFound; - }; +ModifierCurrentDate::ModifierCurrentDate() : _pathReplacementPosition(0), _typeIsDate(true) {} - ModifierCurrentDate::ModifierCurrentDate() - : _pathReplacementPosition(0) - , _typeIsDate(true) { - } +ModifierCurrentDate::~ModifierCurrentDate() {} - ModifierCurrentDate::~ModifierCurrentDate() { +Status ModifierCurrentDate::init(const BSONElement& modExpr, + const Options& opts, + bool* positional) { + _updatePath.parse(modExpr.fieldName()); + Status status = fieldchecker::isUpdatable(_updatePath); + if (!status.isOK()) { + return status; } - Status ModifierCurrentDate::init(const BSONElement& modExpr, const Options& opts, - bool* positional) { - - _updatePath.parse(modExpr.fieldName()); - Status status = fieldchecker::isUpdatable(_updatePath); - 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(_updatePath, - &_pathReplacementPosition, - &foundCount); + // 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(_updatePath, &_pathReplacementPosition, &foundCount); - if (positional) - *positional = foundDollar; + if (positional) + *positional = foundDollar; - if (foundDollar && foundCount > 1) { - return Status(ErrorCodes::BadValue, - str::stream() << "Too many positional (i.e. '$') elements found in path '" - << _updatePath.dottedField() << "'"); - } + if (foundDollar && foundCount > 1) { + return Status(ErrorCodes::BadValue, + str::stream() << "Too many positional (i.e. '$') elements found in path '" + << _updatePath.dottedField() << "'"); + } - // Validate and store the type to produce - switch (modExpr.type()) { - case Bool: - _typeIsDate = true; - break; - case Object: { - const BSONObj argObj = modExpr.embeddedObject(); - const BSONElement typeElem = argObj.getField(kType); - bool badInput = typeElem.eoo() || !(typeElem.type() == String); + // Validate and store the type to produce + switch (modExpr.type()) { + case Bool: + _typeIsDate = true; + break; + case Object: { + const BSONObj argObj = modExpr.embeddedObject(); + const BSONElement typeElem = argObj.getField(kType); + bool badInput = typeElem.eoo() || !(typeElem.type() == String); + + if (!badInput) { + std::string typeVal = typeElem.String(); + badInput = !(typeElem.String() == kDate || typeElem.String() == kTimestamp); + if (!badInput) + _typeIsDate = (typeVal == kDate); if (!badInput) { - std::string typeVal = typeElem.String(); - badInput = !(typeElem.String() == kDate || typeElem.String() == kTimestamp); - if (!badInput) - _typeIsDate = (typeVal == kDate); - - if (!badInput) { - // Check to make sure only the $type field was given as an arg - BSONObjIterator i( argObj ); - const bool onlyHasTypeField = ((i.next().fieldNameStringData() == kType) - && i.next().eoo()); - if (!onlyHasTypeField) { - return Status(ErrorCodes::BadValue, - str::stream() << - "The only valid field of the option is '$type': " - "{$currentDate: {field : {$type: 'date/timestamp'}}}; " + // Check to make sure only the $type field was given as an arg + BSONObjIterator i(argObj); + const bool onlyHasTypeField = + ((i.next().fieldNameStringData() == kType) && i.next().eoo()); + if (!onlyHasTypeField) { + return Status(ErrorCodes::BadValue, + str::stream() + << "The only valid field of the option is '$type': " + "{$currentDate: {field : {$type: 'date/timestamp'}}}; " << "arg: " << argObj); - } - } - - } - - if (badInput) { - return Status(ErrorCodes::BadValue, - "The '$type' string field is required " - "to be 'date' or 'timestamp': " - "{$currentDate: {field : {$type: 'date'}}}"); } - break; } - default: - return Status(ErrorCodes::BadValue, - str::stream() << typeName(modExpr.type()) - << " is not valid type for $currentDate." - " Please use a boolean ('true')" - " or a $type expression ({$type: 'timestamp/date'})."); - } - - return Status::OK(); - } - - Status ModifierCurrentDate::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 (_pathReplacementPosition) { - if (matchedField.empty()) { + if (badInput) { return Status(ErrorCodes::BadValue, - str::stream() << "The positional operator did not find the match " - "needed from the query. Unexpanded update: " - << _updatePath.dottedField()); + "The '$type' string field is required " + "to be 'date' or 'timestamp': " + "{$currentDate: {field : {$type: 'date'}}}"); } - _updatePath.setPart(_pathReplacementPosition, matchedField); + break; } + default: + return Status(ErrorCodes::BadValue, + str::stream() << typeName(modExpr.type()) + << " is not valid type for $currentDate." + " Please use a boolean ('true')" + " or a $type expression ({$type: 'timestamp/date'})."); + } - // 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(_updatePath, - root, - &_preparedState->idxFound, - &_preparedState->elemFound); - - // 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; - } + return Status::OK(); +} - // 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] = &_updatePath; +Status ModifierCurrentDate::prepare(mutablebson::Element root, + StringData matchedField, + ExecInfo* execInfo) { + _preparedState.reset(new PreparedState(root.getDocument())); - return Status::OK(); + // If we have a $-positional field, it is time to bind it to an actual field part. + if (_pathReplacementPosition) { + if (matchedField.empty()) { + return Status(ErrorCodes::BadValue, + str::stream() << "The positional operator did not find the match " + "needed from the query. Unexpanded update: " + << _updatePath.dottedField()); + } + _updatePath.setPart(_pathReplacementPosition, matchedField); } - Status ModifierCurrentDate::apply() const { + // 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( + _updatePath, root, &_preparedState->idxFound, &_preparedState->elemFound); + + // 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; + } - const bool destExists = (_preparedState->elemFound.ok() && - _preparedState->idxFound == (_updatePath.numParts() - 1)); + // 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] = &_updatePath; - mutablebson::Document& doc = _preparedState->doc; - StringData lastPart = _updatePath.getPart(_updatePath.numParts() - 1); - // If the element exists and is the same type, then that is what we want to work with - mutablebson::Element elemToSet = destExists ? - _preparedState->elemFound: - doc.end() ; + return Status::OK(); +} - if (!destExists) { - // Creates the final element that's going to be $set in 'doc'. - // fills in the value with place-holder/empty +Status ModifierCurrentDate::apply() const { + const bool destExists = (_preparedState->elemFound.ok() && + _preparedState->idxFound == (_updatePath.numParts() - 1)); - elemToSet = _typeIsDate ? - doc.makeElementDate(lastPart, Date_t()) : - doc.makeElementTimestamp(lastPart, Timestamp()); + mutablebson::Document& doc = _preparedState->doc; + StringData lastPart = _updatePath.getPart(_updatePath.numParts() - 1); + // If the element exists and is the same type, then that is what we want to work with + mutablebson::Element elemToSet = destExists ? _preparedState->elemFound : doc.end(); - if (!elemToSet.ok()) { - return Status(ErrorCodes::InternalError, "can't create new element"); - } + if (!destExists) { + // Creates the final element that's going to be $set in 'doc'. + // fills in the value with place-holder/empty - // 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++; - } + elemToSet = _typeIsDate ? doc.makeElementDate(lastPart, Date_t()) + : doc.makeElementTimestamp(lastPart, Timestamp()); - // createPathAt() will complete the path and attach 'elemToSet' at the end of it. - Status s = pathsupport::createPathAt(_updatePath, - _preparedState->idxFound, - _preparedState->elemFound, - elemToSet); - if (!s.isOK()) - return s; + if (!elemToSet.ok()) { + return Status(ErrorCodes::InternalError, "can't create new element"); } - dassert(elemToSet.ok()); - - // By the time we are here the element is in place and we just need to update the value - if (_typeIsDate) { - const mongo::Date_t now = mongo::jsTime(); - Status s = elemToSet.setValueDate(now); - if (!s.isOK()) - return s; - } - else { - Status s = elemToSet.setValueTimestamp(getNextGlobalTimestamp()); - if (!s.isOK()) - return s; + // 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++; } - // Set the elemFound, idxFound to the changed element for oplog logging. - _preparedState->elemFound = elemToSet; - _preparedState->idxFound = (_updatePath.numParts() - 1); + // createPathAt() will complete the path and attach 'elemToSet' at the end of it. + Status s = pathsupport::createPathAt( + _updatePath, _preparedState->idxFound, _preparedState->elemFound, elemToSet); + if (!s.isOK()) + return s; + } - return Status::OK(); + dassert(elemToSet.ok()); + + // By the time we are here the element is in place and we just need to update the value + if (_typeIsDate) { + const mongo::Date_t now = mongo::jsTime(); + Status s = elemToSet.setValueDate(now); + if (!s.isOK()) + return s; + } else { + Status s = elemToSet.setValueTimestamp(getNextGlobalTimestamp()); + if (!s.isOK()) + return s; } - Status ModifierCurrentDate::log(LogBuilder* logBuilder) const { - // TODO: None of this checks should be needed unless someone calls if we are a noOp - // When we cleanup we should build in testing that no-one calls in the noOp case - const bool destExists = (_preparedState->elemFound.ok() && - _preparedState->idxFound == (_updatePath.numParts() - 1)); + // Set the elemFound, idxFound to the changed element for oplog logging. + _preparedState->elemFound = elemToSet; + _preparedState->idxFound = (_updatePath.numParts() - 1); - // If the destination doesn't exist then we have nothing to log. - // This would only happen if apply isn't called or fails when it had to create an element - if (!destExists) - return Status::OK(); + return Status::OK(); +} - return logBuilder->addToSetsWithNewFieldName(_updatePath.dottedField(), - _preparedState->elemFound); - } +Status ModifierCurrentDate::log(LogBuilder* logBuilder) const { + // TODO: None of this checks should be needed unless someone calls if we are a noOp + // When we cleanup we should build in testing that no-one calls in the noOp case + const bool destExists = (_preparedState->elemFound.ok() && + _preparedState->idxFound == (_updatePath.numParts() - 1)); + + // If the destination doesn't exist then we have nothing to log. + // This would only happen if apply isn't called or fails when it had to create an element + if (!destExists) + return Status::OK(); + + return logBuilder->addToSetsWithNewFieldName(_updatePath.dottedField(), + _preparedState->elemFound); +} -} // namespace mongo +} // namespace mongo |