Exemplo n.º 1
0
    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();
    }
Exemplo n.º 2
0
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();
}
Exemplo n.º 3
0
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));
}
Exemplo n.º 4
0
    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();
    }
Exemplo n.º 5
0
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();
}
Exemplo n.º 6
0
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();
}
Exemplo n.º 7
0
    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();
    }
Exemplo n.º 8
0
void BitNode::setValueForNewElement(mutablebson::Element* element) const {
    SafeNum value = applyOpList(SafeNum(static_cast<int32_t>(0)));
    invariantOK(element->setValueSafeNum(value));
}