bool match(const mutablebson::ConstElement& element) final {
     if (element.getType() == mongo::Object) {
         return _matchExpr->matchesBSON(element.getValueObject());
     } else {
         return false;
     }
 }
void storageValid(mutablebson::ConstElement elem, const bool deep, std::uint32_t recursionLevel) {
    uassert(ErrorCodes::BadValue, "Invalid elements cannot be stored.", elem.ok());

    uassert(ErrorCodes::Overflow,
            str::stream() << "Document exceeds maximum nesting depth of "
                          << BSONDepth::getMaxDepthForUserStorage(),
            recursionLevel <= BSONDepth::getMaxDepthForUserStorage());

    // Field names of elements inside arrays are not meaningful in mutable bson,
    // so we do not want to validate them.
    const mutablebson::ConstElement& parent = elem.parent();
    const bool childOfArray = parent.ok() ? (parent.getType() == BSONType::Array) : false;

    if (!childOfArray) {
        auto fieldName = elem.getFieldName();

        // Cannot start with "$", unless dbref.
        if (fieldName[0] == '$') {
            validateDollarPrefixElement(elem);
        }
    }

    if (deep) {

        // Check children if there are any.
        storageValidChildren(elem, deep, recursionLevel);
    }
}
void UnsetNode::validateUpdate(mutablebson::ConstElement updatedElement,
                               mutablebson::ConstElement leftSibling,
                               mutablebson::ConstElement rightSibling,
                               std::uint32_t recursionLevel,
                               ModifyResult modifyResult) const {
    invariant(modifyResult == ModifyResult::kNormalUpdate);

    // We only need to check the left and right sibling to see if the removed element was part of a
    // now invalid DBRef.
    const bool doRecursiveCheck = false;
    const uint32_t recursionLevelForCheck = 0;

    if (leftSibling.ok()) {
        storage_validation::storageValid(leftSibling, doRecursiveCheck, recursionLevelForCheck);
    }

    if (rightSibling.ok()) {
        storage_validation::storageValid(rightSibling, doRecursiveCheck, recursionLevelForCheck);
    }
}
Beispiel #4
0
bool ModifierPull::isMatch(mutablebson::ConstElement element) {
    // TODO: We are assuming that 'element' hasValue is true. That might be OK if the
    // conflict detection logic will prevent us from ever seeing a deserialized element,
    // but are we sure about that?

    dassert(element.hasValue());

    if (!_matchExpr)
        return (element.compareWithBSONElement(_exprElt, _collator, false) == 0);

    if (_matcherOnPrimitive) {
        // TODO: This is kinda slow.
        BSONObj candidate = element.getValue().wrap("");
        return _matchExpr->matchesBSON(candidate);
    }

    if (element.getType() != Object)
        return false;

    return _matchExpr->matchesBSON(element.getValueObject());
}
 bool match(const mutablebson::ConstElement& element) final {
     BSONObj candidate = element.getValue().wrap("");
     return _matchExpr->matchesBSON(candidate);
 }
 bool match(const mutablebson::ConstElement& element) final {
     return (element.compareWithBSONElement(_modExpr, _collator, false) == 0);
 }
Beispiel #7
0
/**
 * This will verify that all updated fields are
 *   1.) Valid for storage (checking parent to make sure things like DBRefs are valid)
 *   2.) Compare updated immutable fields do not change values
 *
 * If updateFields is empty then it was replacement and/or we need to check all fields
 */
inline Status validate(const BSONObj& original,
                       const FieldRefSet& updatedFields,
                       const mb::Document& updated,
                       const std::vector<FieldRef*>* immutableAndSingleValueFields,
                       const ModifierInterface::Options& opts) {
    LOG(3) << "update validate options -- "
           << " updatedFields: " << updatedFields << " immutableAndSingleValueFields.size:"
           << (immutableAndSingleValueFields ? immutableAndSingleValueFields->size() : 0)
           << " validate:" << opts.enforceOkForStorage;

    // 1.) Loop through each updated field and validate for storage
    // and detect immutable field updates

    // The set of possibly changed immutable fields -- we will need to check their vals
    FieldRefSet changedImmutableFields;

    // Check to see if there were no fields specified or if we are not validating
    // The case if a range query, or query that didn't result in saved fields
    if (updatedFields.empty() || !opts.enforceOkForStorage) {
        if (opts.enforceOkForStorage) {
            // No specific fields were updated so the whole doc must be checked
            Status s = storageValid(updated, true);
            if (!s.isOK())
                return s;
        }

        // Check all immutable fields
        if (immutableAndSingleValueFields)
            changedImmutableFields.fillFrom(*immutableAndSingleValueFields);
    } else {
        // TODO: Change impl so we don't need to create a new FieldRefSet
        //       -- move all conflict logic into static function on FieldRefSet?
        FieldRefSet immutableFieldRef;
        if (immutableAndSingleValueFields)
            immutableFieldRef.fillFrom(*immutableAndSingleValueFields);

        FieldRefSet::const_iterator where = updatedFields.begin();
        const FieldRefSet::const_iterator end = updatedFields.end();
        for (; where != end; ++where) {
            const FieldRef& current = **where;

            // Find the updated field in the updated document.
            mutablebson::ConstElement newElem = updated.root();
            size_t currentPart = 0;
            while (newElem.ok() && currentPart < current.numParts())
                newElem = newElem[current.getPart(currentPart++)];

            // newElem might be missing if $unset/$renamed-away
            if (newElem.ok()) {
                // Check element, and its children
                Status s = storageValid(newElem, true);
                if (!s.isOK())
                    return s;

                // Check parents to make sure they are valid as well.
                s = storageValidParents(newElem);
                if (!s.isOK())
                    return s;
            }
            // Check if the updated field conflicts with immutable fields
            immutableFieldRef.findConflicts(&current, &changedImmutableFields);
        }
    }

    const bool checkIdField = (updatedFields.empty() && !original.isEmpty()) ||
        updatedFields.findConflicts(&idFieldRef, NULL);

    // Add _id to fields to check since it too is immutable
    if (checkIdField)
        changedImmutableFields.keepShortest(&idFieldRef);
    else if (changedImmutableFields.empty()) {
        // Return early if nothing changed which is immutable
        return Status::OK();
    }

    LOG(4) << "Changed immutable fields: " << changedImmutableFields;
    // 2.) Now compare values of the changed immutable fields (to make sure they haven't)

    const mutablebson::ConstElement newIdElem = updated.root()[idFieldName];

    FieldRefSet::const_iterator where = changedImmutableFields.begin();
    const FieldRefSet::const_iterator end = changedImmutableFields.end();
    for (; where != end; ++where) {
        const FieldRef& current = **where;

        // Find the updated field in the updated document.
        mutablebson::ConstElement newElem = updated.root();
        size_t currentPart = 0;
        while (newElem.ok() && currentPart < current.numParts())
            newElem = newElem[current.getPart(currentPart++)];

        if (!newElem.ok()) {
            if (original.isEmpty()) {
                // If the _id is missing and not required, then skip this check
                if (!(current.dottedField() == idFieldName))
                    return Status(ErrorCodes::NoSuchKey,
                                  mongoutils::str::stream() << "After applying the update, the new"
                                                            << " document was missing the '"
                                                            << current.dottedField()
                                                            << "' (required and immutable) field.");

            } else {
                if (current.dottedField() != idFieldName)
                    return Status(ErrorCodes::ImmutableField,
                                  mongoutils::str::stream()
                                      << "After applying the update to the document with "
                                      << newIdElem.toString()
                                      << ", the '"
                                      << current.dottedField()
                                      << "' (required and immutable) field was "
                                         "found to have been removed --"
                                      << original);
            }
        } else {
            // Find the potentially affected field in the original document.
            const BSONElement oldElem = dps::extractElementAtPath(original, current.dottedField());
            const BSONElement oldIdElem = original.getField(idFieldName);

            // Ensure no arrays since neither _id nor shard keys can be in an array, or one.
            mb::ConstElement currElem = newElem;
            while (currElem.ok()) {
                if (currElem.getType() == Array) {
                    return Status(
                        ErrorCodes::NotSingleValueField,
                        mongoutils::str::stream()
                            << "After applying the update to the document {"
                            << (oldIdElem.ok() ? oldIdElem.toString() : newIdElem.toString())
                            << " , ...}, the (immutable) field '"
                            << current.dottedField()
                            << "' was found to be an array or array descendant.");
                }
                currElem = currElem.parent();
            }

            // If we have both (old and new), compare them. If we just have new we are good
            if (oldElem.ok() && newElem.compareWithBSONElement(oldElem, nullptr, false) != 0) {
                return Status(ErrorCodes::ImmutableField,
                              mongoutils::str::stream()
                                  << "After applying the update to the document {"
                                  << oldElem.toString()
                                  << " , ...}, the (immutable) field '"
                                  << current.dottedField()
                                  << "' was found to have been altered to "
                                  << newElem.toString());
            }
        }
    }

    return Status::OK();
}