SafeNum SafeNum::mulInternal(const SafeNum& lhs, const SafeNum& rhs) { BSONType lType = lhs._type; BSONType rType = rhs._type; if (lType == NumberInt && rType == NumberInt) { return mulInt32Int32(lhs._value.int32Val, rhs._value.int32Val); } if (lType == NumberInt && rType == NumberLong) { return mulInt64Int64(lhs._value.int32Val, rhs._value.int64Val); } if (lType == NumberLong && rType == NumberInt) { return mulInt64Int64(lhs._value.int64Val, rhs._value.int32Val); } if (lType == NumberLong && rType == NumberLong) { return mulInt64Int64(lhs._value.int64Val, rhs._value.int64Val); } if ((lType == NumberInt || lType == NumberLong || lType == NumberDouble) && (rType == NumberInt || rType == NumberLong || rType == NumberDouble)) { return mulFloats(getDouble(lhs), getDouble(rhs)); } return SafeNum(); }
Status BitNode::init(BSONElement modExpr, const CollatorInterface* collator) { invariant(modExpr.ok()); if (modExpr.type() != mongo::Object) { return Status(ErrorCodes::BadValue, str::stream() << "The $bit modifier is not compatible with a " << typeName(modExpr.type()) << ". You must pass in an embedded document: " "{$bit: {field: {and/or/xor: #}}"); } for (const auto& curOp : modExpr.embeddedObject()) { const StringData payloadFieldName = curOp.fieldNameStringData(); BitwiseOp parsedOp; if (payloadFieldName == "and") { parsedOp.bitOperator = &SafeNum::bitAnd; } else if (payloadFieldName == "or") { parsedOp.bitOperator = &SafeNum::bitOr; } else if (payloadFieldName == "xor") { parsedOp.bitOperator = &SafeNum::bitXor; } else { return Status(ErrorCodes::BadValue, str::stream() << "The $bit modifier only supports 'and', 'or', and 'xor', not '" << payloadFieldName << "' which is an unknown operator: {" << curOp << "}"); } if ((curOp.type() != mongo::NumberInt) && (curOp.type() != mongo::NumberLong)) { return Status(ErrorCodes::BadValue, str::stream() << "The $bit modifier field must be an Integer(32/64 bit); a '" << typeName(curOp.type()) << "' is not supported here: {" << curOp << "}"); } parsedOp.operand = SafeNum(curOp); _opList.push_back(parsedOp); } if (_opList.empty()) { return Status(ErrorCodes::BadValue, str::stream() << "You must pass in at least one bitwise operation. " << "The format is: " "{$bit: {field: {and/or/xor: #}}"); } return Status::OK(); }
void ArithmeticNode::setValueForNewElement(mutablebson::Element* element) const { SafeNum valueToSet = _val; switch (_op) { case ArithmeticOp::kAdd: // valueToSet += 0 break; case ArithmeticOp::kMultiply: // This results in a createdValue that has the same type as the original "element" // but has a 0 value. valueToSet *= SafeNum(static_cast<int32_t>(0)); break; } // This can fail if 'valueToSet' is not representable as a 64-bit integer. uassertStatusOK(element->setValueSafeNum(valueToSet)); }
SafeNum SafeNum::xorInternal(const SafeNum& lhs, const SafeNum& rhs) { const BSONType lType = lhs._type; const BSONType rType = rhs._type; if (lType == NumberInt && rType == NumberInt) { return (lhs._value.int32Val ^ rhs._value.int32Val); } if (lType == NumberInt && rType == NumberLong) { return (static_cast<long long int>(lhs._value.int32Val) ^ rhs._value.int64Val); } if (lType == NumberLong && rType == NumberInt) { return (lhs._value.int64Val ^ static_cast<long long int>(rhs._value.int32Val)); } if (lType == NumberLong && rType == NumberLong) { return (lhs._value.int64Val ^ rhs._value.int64Val); } return SafeNum(); }
Status ModifierBit::init(const BSONElement& modExpr, const Options& opts, bool* positional) { // 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() << "'"); } if (modExpr.type() != mongol::Object) return Status(ErrorCodes::BadValue, str::stream() << "The $bit modifier is not compatible with a " << typeName(modExpr.type()) << ". You must pass in an embedded document: " "{$bit: {field: {and/or/xor: #}}"); if (modExpr.embeddedObject().isEmpty()) { return Status(ErrorCodes::BadValue, str::stream() << "You must pass in at least one bitwise operation. " << "The format is: " "{$bit: {field: {and/or/xor: #}}"); } BSONObjIterator opsIterator(modExpr.embeddedObject()); while (opsIterator.more()) { BSONElement curOp = opsIterator.next(); const StringData payloadFieldName = curOp.fieldName(); SafeNumOp op = NULL; if (payloadFieldName == "and") { op = &SafeNum::bitAnd; } else if (payloadFieldName == "or") { op = &SafeNum::bitOr; } else if (payloadFieldName == "xor") { op = &SafeNum::bitXor; } else { return Status(ErrorCodes::BadValue, str::stream() << "The $bit modifier only supports 'and', 'or', and 'xor', not '" << payloadFieldName << "' which is an unknown operator: {" << curOp << "}"); } if ((curOp.type() != mongol::NumberInt) && (curOp.type() != mongol::NumberLong)) return Status(ErrorCodes::BadValue, str::stream() << "The $bit modifier field must be an Integer(32/64 bit); a '" << typeName(curOp.type()) << "' is not supported here: {" << curOp << "}"); const OpEntry entry = {SafeNum(curOp), op}; _ops.push_back(entry); } dassert(!_ops.empty()); return Status::OK(); }
Status ModifierBit::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'. Status status = pathsupport::findLongestPrefix( _fieldRef, 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; } // 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; // // 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)) { // If no target element exists, the value we will write is the result of applying // the operation to a zero-initialized integer element. _preparedState->newValue = apply(SafeNum(static_cast<int>(0))); return Status::OK(); } if (!_preparedState->elemFound.isIntegral()) { mb::Element idElem = mb::findElementNamed(root.leftChild(), "_id"); return Status(ErrorCodes::BadValue, str::stream() << "Cannot apply $bit to a value of non-integral type." << idElem.toString() << " has the field " << _preparedState->elemFound.getFieldName() << " of non-integer type " << typeName(_preparedState->elemFound.getType())); } const SafeNum currentValue = _preparedState->elemFound.getValueSafeNum(); // Apply the op over the existing value and the mod value, and capture the result. _preparedState->newValue = apply(currentValue); if (!_preparedState->newValue.isValid()) { // TODO: Include list of ops, if that is easy, at some future point. return Status(ErrorCodes::BadValue, str::stream() << "Failed to apply $bit operations to current value: " << currentValue.debugString()); } // 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::prepare(mutablebson::Element root, const 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); // 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<int>(0)); 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::BadValue, 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(); }
void BitNode::setValueForNewElement(mutablebson::Element* element) const { SafeNum value = applyOpList(SafeNum(static_cast<int32_t>(0))); invariantOK(element->setValueSafeNum(value)); }