Status ModifierInc::log(mutablebson::Element logRoot) const { // We'd like to create an entry such as {$set: {<fieldname>: <value>}} under 'logRoot'. // We start by creating the {$set: ...} Element. mutablebson::Document& doc = logRoot.getDocument(); mutablebson::Element setElement = doc.makeElementObject("$set"); if (!setElement.ok()) { return Status(ErrorCodes::InternalError, "cannot append log entry for $set mod"); } // Then we create the {<fieldname>: <value>} Element. mutablebson::Element logElement = doc.makeElementSafeNum( _fieldRef.dottedField(), _preparedState->newValue); if (!logElement.ok()) { return Status(ErrorCodes::InternalError, "cannot append details for $set mod"); } // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} one. Status status = setElement.pushBack(logElement); if (!status.isOK()) { return status; } // And attach the result under the 'logRoot' Element provided. return logRoot.pushBack(setElement); }
Status ModifierSet::log(mutablebson::Element logRoot) const { // We'd like to create an entry such as {$set: {<fieldname>: <value>}} under 'logRoot'. // We start by creating the {$set: ...} Element. mutablebson::Document& doc = logRoot.getDocument(); mutablebson::Element setElement = doc.makeElementObject("$set"); if (!setElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create log entry for $set mod"); } // Then we create the {<fieldname>: <value>} Element. Note that we log the mod with a // dotted field, if it was applied over a dotted field. The rationale is that the // secondary may be in a different state than the primary and thus make different // decisions about creating the intermediate path in _fieldRef or not. mutablebson::Element logElement = doc.makeElementWithNewFieldName(_fieldRef.dottedField(), _val); if (!logElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create details for $set mod"); } // Now, we attach the {<fieldname>: <value>} Element under the {$set: ...} one. Status status = setElement.pushBack(logElement); if (!status.isOK()) { return status; } // And attach the result under the 'logRoot' Element provided. return logRoot.pushBack(setElement); }
Status ModifierUnset::log(mutablebson::Element logRoot) const { // We'd like to create an entry such as {$unset: {<fieldname>: 1}} under 'logRoot'. // We start by creating the {$unset: ...} Element. mutablebson::Document& doc = logRoot.getDocument(); mutablebson::Element unsetElement = doc.makeElementObject("$unset"); if (!unsetElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create log entry for $unset mod"); } // Then we create the {<fieldname>: <value>} Element. Note that <fieldname> must be a // dotted field, and not only the last part of that field. The rationale here is that // somoene picking up this log entry -- e.g., a secondary -- must be capable of doing // the same path find/creation that was done in the previous calls here. mutablebson::Element logElement = doc.makeElementInt(_fieldRef.dottedField(), 1); if (!logElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create log details for $unset mod"); } // Now, we attach the {<fieldname>: `} Element under the {$unset: ...} one. Status status = unsetElement.pushBack(logElement); if (!status.isOK()) { return status; } // And attach the result under the 'logRoot' Element provided. return logRoot.pushBack(unsetElement); }
Status ModifierAddToSet::log(mb::Element logRoot) const { // TODO: This is copied more or less identically from $push. As a result, it copies the // behavior in $push that relies on 'apply' having been called unless this is a no-op. // TODO We can log just a positional set in several cases. For now, let's just log the // full resulting array. // We'd like to create an entry such as {$set: {<fieldname>: [<resulting aray>]}} under // 'logRoot'. We start by creating the {$set: ...} Element. mb::Document& doc = logRoot.getDocument(); mb::Element setElement = doc.makeElementObject("$set"); if (!setElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create log entry for $addToSet mod"); } // Then we create the {<fieldname>:[]} Element, that is, an empty array. mb::Element logElement = doc.makeElementArray(_fieldRef.dottedField()); if (!logElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create details for $addToSet mod"); } // Fill up the empty array. mb::Element curr = _preparedState->elemFound.leftChild(); while (curr.ok()) { dassert(curr.hasValue()); // We need to copy each array entry from the resulting document to the log // document. mb::Element currCopy = doc.makeElementWithNewFieldName( StringData(), curr.getValue()); if (!currCopy.ok()) { return Status(ErrorCodes::InternalError, "could create copy element"); } Status status = logElement.pushBack(currCopy); if (!status.isOK()) { return Status(ErrorCodes::BadValue, "could not append entry for $addToSet log"); } curr = curr.rightSibling(); } // Now, we attach the {<fieldname>: [<filled array>]} Element under the {$set: ...} // one. Status status = setElement.pushBack(logElement); if (!status.isOK()) { return status; } // And attach the result under the 'logRoot' Element provided. return logRoot.pushBack(setElement); }
Status AuthorizationManager::getBSONForRole(RoleGraph* graph, const RoleName& roleName, mutablebson::Element result) { if (!graph->roleExists(roleName)) { return Status(ErrorCodes::RoleNotFound, mongoutils::str::stream() << roleName.getFullName() << "does not name an existing role"); } std::string id = mongoutils::str::stream() << roleName.getDB() << "." << roleName.getRole(); result.appendString("_id", id); result.appendString(ROLE_NAME_FIELD_NAME, roleName.getRole()); result.appendString(ROLE_SOURCE_FIELD_NAME, roleName.getDB()); // Build privileges array mutablebson::Element privilegesArrayElement = result.getDocument().makeElementArray("privileges"); result.pushBack(privilegesArrayElement); const PrivilegeVector& privileges = graph->getDirectPrivileges(roleName); Status status = getBSONForPrivileges(privileges, privilegesArrayElement); if (!status.isOK()) { return status; } // Build roles array mutablebson::Element rolesArrayElement = result.getDocument().makeElementArray("roles"); result.pushBack(rolesArrayElement); for (RoleNameIterator roles = graph->getDirectSubordinates(roleName); roles.more(); roles.next()) { const RoleName& subRole = roles.get(); mutablebson::Element roleObj = result.getDocument().makeElementObject(""); roleObj.appendString(ROLE_NAME_FIELD_NAME, subRole.getRole()); roleObj.appendString(ROLE_SOURCE_FIELD_NAME, subRole.getDB()); rolesArrayElement.pushBack(roleObj); } return Status::OK(); }
Status createPathAt(const FieldRef& prefix, size_t idxFound, mutablebson::Element elemFound, mutablebson::Element newElem) { Status status = Status::OK(); // Sanity check that 'idxField' is an actual part. const size_t size = prefix.numParts(); if (idxFound >= size) { return Status(ErrorCodes::BadValue, "index larger than path size"); } mutablebson::Document& doc = elemFound.getDocument(); // If we are creating children under an array and a numeric index is next, then perhaps // we need padding. size_t i = idxFound; bool inArray = false; if (elemFound.getType() == mongol::Array) { size_t newIdx = 0; if (!isNumeric(prefix.getPart(idxFound), &newIdx)) { return Status(ErrorCodes::InvalidPath, "Array require numeric fields"); } status = maybePadTo(&elemFound, newIdx); if (!status.isOK()) { return status; } // If there is a next field, that would be an array element. We'd like to mark that // field because we create array elements differently than we do regular objects. if (++i < size) { inArray = true; } } // Create all the remaining parts but the last one. for (; i < size - 1; i++) { mutablebson::Element elem = doc.makeElementObject(prefix.getPart(i)); if (!elem.ok()) { return Status(ErrorCodes::InternalError, "cannot create path"); } // If this field is an array element, we wrap it in an object (because array // elements are wraped in { "N": <element> } objects. if (inArray) { // TODO pass empty StringData to makeElementObject, when that's supported. mutablebson::Element arrayObj = doc.makeElementObject("" /* it's an array */); if (!arrayObj.ok()) { return Status(ErrorCodes::InternalError, "cannot create item on array"); } status = arrayObj.pushBack(elem); if (!status.isOK()) { return status; } status = elemFound.pushBack(arrayObj); if (!status.isOK()) { return status; } inArray = false; } else { status = elemFound.pushBack(elem); if (!status.isOK()) { return status; } } elemFound = elem; } // Attach the last element. Here again, if we're in a field that is an array element, // we wrap it in an object first. if (inArray) { // TODO pass empty StringData to makeElementObject, when that's supported. mutablebson::Element arrayObj = doc.makeElementObject("" /* it's an array */); if (!arrayObj.ok()) { return Status(ErrorCodes::InternalError, "cannot create item on array"); } status = arrayObj.pushBack(newElem); if (!status.isOK()) { return status; } status = elemFound.pushBack(arrayObj); if (!status.isOK()) { return status; } } else { status = elemFound.pushBack(newElem); if (!status.isOK()) { return status; } } return Status::OK(); }
Status ModifierPull::log(mb::Element logRoot) const { mb::Document& doc = logRoot.getDocument(); mb::Element opElement = doc.end(); mb::Element logElement = doc.end(); if (!_preparedState->elemFound.ok() || _preparedState->idxFound < static_cast<int32_t>(_fieldRef.numParts() - 1)) { // If we didn't find the element that we wanted to pull from, we log an unset for // that element. opElement = doc.makeElementObject("$unset"); if (!opElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create log entry for $pull mod"); } logElement = doc.makeElementInt(_fieldRef.dottedField(), 1); } else { // TODO: This is copied more or less identically from $push. As a result, it copies the // behavior in $push that relies on 'apply' having been called unless this is a no-op. // TODO We can log just a positional unset in several cases. For now, let's just log // the full resulting array. // We'd like to create an entry such as {$set: {<fieldname>: [<resulting aray>]}} under // 'logRoot'. We start by creating the {$set: ...} Element. opElement = doc.makeElementObject("$set"); if (!opElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create log entry for $pull mod"); } // Then we create the {<fieldname>:[]} Element, that is, an empty array. logElement = doc.makeElementArray(_fieldRef.dottedField()); if (!logElement.ok()) { return Status(ErrorCodes::InternalError, "cannot create details for $pull mod"); } mb::Element curr = _preparedState->elemFound.leftChild(); while (curr.ok()) { dassert(curr.hasValue()); // We need to copy each array entry from the resulting document to the log // document. mb::Element currCopy = doc.makeElementWithNewFieldName( StringData(), curr.getValue()); if (!currCopy.ok()) { return Status(ErrorCodes::InternalError, "could create copy element"); } Status status = logElement.pushBack(currCopy); if (!status.isOK()) { return Status(ErrorCodes::BadValue, "could not append entry for $pull log"); } curr = curr.rightSibling(); } } // Now, we attach log element under the op element. Status status = opElement.pushBack(logElement); if (!status.isOK()) { return status; } // And attach the result under the 'logRoot' Element provided by the caller. return logRoot.pushBack(opElement); }