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); } }
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); }
/** * 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(¤t, &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(); }