// static Status WiredTigerUtil::checkTableCreationOptions(const BSONElement& configElem) { invariant(configElem.fieldNameStringData() == "configString"); if (configElem.type() != String) { return {ErrorCodes::TypeMismatch, "'configString' must be a string."}; } std::vector<std::string> errors; ErrorAccumulator eventHandler(&errors); StringData config = configElem.valueStringData(); // Do NOT allow embedded null characters if (config.size() != strlen(config.rawData())) { return {ErrorCodes::FailedToParse, "malformed 'configString' value."}; } Status status = wtRCToStatus( wiredtiger_config_validate(nullptr, &eventHandler, "WT_SESSION.create", config.rawData())); if (!status.isOK()) { StringBuilder errorMsg; errorMsg << status.reason(); for (std::string error : errors) { errorMsg << ". " << error; } errorMsg << "."; return status.withReason(errorMsg.stringData()); } return Status::OK(); }
list<intrusive_ptr<DocumentSource>> DocumentSourceCount::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) { uassert(40156, str::stream() << "the count field must be a non-empty string", elem.type() == String); StringData elemString = elem.valueStringData(); uassert( 40157, str::stream() << "the count field must be a non-empty string", !elemString.empty()); uassert(40158, str::stream() << "the count field cannot be a $-prefixed path", elemString[0] != '$'); uassert(40159, str::stream() << "the count field cannot contain a null byte", elemString.find('\0') == string::npos); uassert(40160, str::stream() << "the count field cannot contain '.'", elemString.find('.') == string::npos); BSONObj groupObj = BSON("$group" << BSON("_id" << BSONNULL << elemString << BSON("$sum" << 1))); BSONObj projectObj = BSON("$project" << BSON("_id" << 0 << elemString << 1)); auto groupSource = DocumentSourceGroup::createFromBson(groupObj.firstElement(), pExpCtx); auto projectSource = DocumentSourceProject::createFromBson(projectObj.firstElement(), pExpCtx); return {groupSource, projectSource}; }
vector<intrusive_ptr<DocumentSource>> DocumentSourceSortByCount::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) { if (elem.type() == Object) { // Make sure that the sortByCount field is an expression inside an object BSONObj innerObj = elem.embeddedObject(); uassert(40147, str::stream() << "the sortByCount field must be defined as a $-prefixed path or an " "expression inside an object", innerObj.firstElementFieldName()[0] == '$'); } else if (elem.type() == String) { // Make sure that the sortByCount field is a $-prefixed path uassert(40148, str::stream() << "the sortByCount field must be defined as a $-prefixed path or an " "expression inside an object", (elem.valueStringData()[0] == '$')); } else { uasserted( 40149, str::stream() << "the sortByCount field must be specified as a string or as an object"); } BSONObjBuilder groupExprBuilder; groupExprBuilder.appendAs(elem, "_id"); groupExprBuilder.append("count", BSON("$sum" << 1)); BSONObj groupObj = BSON("$group" << groupExprBuilder.obj()); BSONObj sortObj = BSON("$sort" << BSON("count" << -1)); auto groupSource = DocumentSourceGroup::createFromBson(groupObj.firstElement(), pExpCtx); auto sortSource = DocumentSourceSort::createFromBson(sortObj.firstElement(), pExpCtx); return {groupSource, sortSource}; }
std::unique_ptr<DocumentSourceOut::LiteParsed> DocumentSourceOut::LiteParsed::parse( const AggregationRequest& request, const BSONElement& spec) { uassert(ErrorCodes::TypeMismatch, str::stream() << "$out stage requires a string or object argument, but found " << typeName(spec.type()), spec.type() == BSONType::String || spec.type() == BSONType::Object); NamespaceString targetNss; bool allowSharded; WriteModeEnum mode; if (spec.type() == BSONType::String) { targetNss = NamespaceString(request.getNamespaceString().db(), spec.valueStringData()); allowSharded = false; mode = WriteModeEnum::kModeReplaceCollection; } else if (spec.type() == BSONType::Object) { auto outSpec = DocumentSourceOutSpec::parse(IDLParserErrorContext("$out"), spec.embeddedObject()); if (auto targetDb = outSpec.getTargetDb()) { targetNss = NamespaceString(*targetDb, outSpec.getTargetCollection()); } else { targetNss = NamespaceString(request.getNamespaceString().db(), outSpec.getTargetCollection()); } mode = outSpec.getMode(); // Sharded output collections are not allowed with mode "replaceCollection". allowSharded = mode != WriteModeEnum::kModeReplaceCollection; } uassert(ErrorCodes::InvalidNamespace, str::stream() << "Invalid $out target namespace, " << targetNss.ns(), targetNss.isValid()); // All modes require the "insert" action. ActionSet actions{ActionType::insert}; switch (mode) { case WriteModeEnum::kModeReplaceCollection: actions.addAction(ActionType::remove); break; case WriteModeEnum::kModeReplaceDocuments: actions.addAction(ActionType::update); break; case WriteModeEnum::kModeInsertDocuments: // "insertDocuments" mode only requires the "insert" action. break; } if (request.shouldBypassDocumentValidation()) { actions.addAction(ActionType::bypassDocumentValidation); } PrivilegeVector privileges{Privilege(ResourcePattern::forExactNamespace(targetNss), actions)}; return stdx::make_unique<DocumentSourceOut::LiteParsed>( std::move(targetNss), std::move(privileges), allowSharded); }
void ShardingConnectionHook::onCreate(DBClientBase* conn) { // Authenticate as the first thing we do // NOTE: Replica set authentication allows authentication against *any* online host if (getGlobalAuthorizationManager()->isAuthEnabled()) { LOG(2) << "calling onCreate auth for " << conn->toString(); bool result = conn->authenticateInternalUser(); uassert(15847, str::stream() << "can't authenticate to server " << conn->getServerAddress(), result); } if (_shardedConnections) { conn->setReplyMetadataReader(_shardingReplyMetadataReader); } conn->setRequestMetadataWriter( [this](BSONObjBuilder* metadataBob, StringData hostStringData) -> Status { return _shardingRequestMetadataWriter(_shardedConnections, metadataBob, hostStringData); }); // For every SCC created, add a hook that will allow fastest-config-first config reads if // the appropriate server options are set. if (conn->type() == ConnectionString::SYNC) { SyncClusterConnection* scc = dynamic_cast<SyncClusterConnection*>(conn); if (scc) { scc->attachQueryHandler(new SCCFastQueryHandler); } } else if (conn->type() == ConnectionString::MASTER) { BSONObj isMasterResponse; if (!conn->runCommand("admin", BSON("ismaster" << 1), isMasterResponse)) { uassertStatusOK(getStatusFromCommandResult(isMasterResponse)); } long long configServerModeNumber; Status status = bsonExtractIntegerField(isMasterResponse, "configsvr", &configServerModeNumber); if (status == ErrorCodes::NoSuchKey) { // This isn't a config server we're talking to. return; } uassert(28785, str::stream() << "Unrecognized configsvr version number: " << configServerModeNumber << ". Expected either 0 or 1", configServerModeNumber == 0 || configServerModeNumber == 1); BSONElement setName = isMasterResponse["setName"]; status = grid.forwardingCatalogManager()->scheduleReplaceCatalogManagerIfNeeded( configServerModeNumber == 0 ? CatalogManager::ConfigServerMode::SCCC : CatalogManager::ConfigServerMode::CSRS, setName.type() == String ? setName.valueStringData() : StringData(), static_cast<DBClientConnection*>(conn)->getServerHostAndPort()); uassertStatusOK(status); } }
Status ModifierRename::init(const BSONElement& modExpr, const Options& opts, bool* positional) { if (modExpr.type() != String) { return Status(ErrorCodes::BadValue, str::stream() << "The 'to' field for $rename must be a string: " << modExpr); } if (modExpr.valueStringData().find('\0') != std::string::npos) { return Status(ErrorCodes::BadValue, "The 'to' field for $rename cannot contain an embedded null byte"); } // Extract the field names from the mod expression _fromFieldRef.parse(modExpr.fieldName()); Status status = fieldchecker::isUpdatable(_fromFieldRef); if (!status.isOK()) return status; _toFieldRef.parse(modExpr.String()); status = fieldchecker::isUpdatable(_toFieldRef); if (!status.isOK()) return status; // TODO: Remove this restriction and make a noOp to lift restriction // Old restriction is that if the fields are the same then it is not allowed. if (_fromFieldRef == _toFieldRef) return Status(ErrorCodes::BadValue, str::stream() << "The source and target field for $rename must differ: " << modExpr); // TODO: Remove this restriction by allowing moving deeping from the 'from' path // Old restriction is that if the to/from is on the same path it fails if (_fromFieldRef.isPrefixOf(_toFieldRef) || _toFieldRef.isPrefixOf(_fromFieldRef)) { return Status(ErrorCodes::BadValue, str::stream() << "The source and target field for $rename must " "not be on the same path: " << modExpr); } // TODO: We can remove this restriction as long as there is only one, // or it is the same array -- should think on this a bit. // // If a $-positional operator was used it is an error size_t dummyPos; if (fieldchecker::isPositional(_fromFieldRef, &dummyPos)) return Status(ErrorCodes::BadValue, str::stream() << "The source field for $rename may not be dynamic: " << _fromFieldRef.dottedField()); else if (fieldchecker::isPositional(_toFieldRef, &dummyPos)) return Status(ErrorCodes::BadValue, str::stream() << "The destination field for $rename may not be dynamic: " << _toFieldRef.dottedField()); if (positional) *positional = false; return Status::OK(); }
Status ShardingNetworkConnectionHook::validateHostImpl( const HostAndPort& remoteHost, const executor::RemoteCommandResponse& isMasterReply) { auto shard = grid.shardRegistry()->getShardNoReload(remoteHost.toString()); if (!shard) { return {ErrorCodes::ShardNotFound, str::stream() << "No shard found for host: " << remoteHost.toString()}; } long long configServerModeNumber; auto status = bsonExtractIntegerField(isMasterReply.data, "configsvr", &configServerModeNumber); switch (status.code()) { case ErrorCodes::OK: { // The ismaster response indicates remoteHost is a config server. if (!shard->isConfig()) { return {ErrorCodes::InvalidOptions, str::stream() << "Surprised to discover that " << remoteHost.toString() << " believes it is a config server"}; } using ConfigServerMode = CatalogManager::ConfigServerMode; const BSONElement setName = isMasterReply.data["setName"]; return grid.forwardingCatalogManager()->scheduleReplaceCatalogManagerIfNeeded( (configServerModeNumber == 0 ? ConfigServerMode::SCCC : ConfigServerMode::CSRS), (setName.type() == String ? setName.valueStringData() : StringData()), remoteHost); } case ErrorCodes::NoSuchKey: { // The ismaster response indicates that remoteHost is not a config server, or that // the config server is running a version prior to the 3.1 development series. if (!shard->isConfig()) { return Status::OK(); } long long remoteMaxWireVersion; status = bsonExtractIntegerFieldWithDefault(isMasterReply.data, "maxWireVersion", RELEASE_2_4_AND_BEFORE, &remoteMaxWireVersion); if (!status.isOK()) { return status; } if (remoteMaxWireVersion < FIND_COMMAND) { // Prior to the introduction of the find command and the 3.1 release series, it was // not possible to distinguish a config server from a shard server from its ismaster // response. As such, we must assume that the system is properly configured. return Status::OK(); } return {ErrorCodes::InvalidOptions, str::stream() << "Surprised to discover that " << remoteHost.toString() << " does not believe it is a config server"}; } default: // The ismaster response was malformed. return status; } }
string Command::parseNsFullyQualified(const string& dbname, const BSONObj& cmdObj) { BSONElement first = cmdObj.firstElement(); uassert(ErrorCodes::BadValue, str::stream() << "collection name has invalid type " << typeName(first.type()), first.canonicalType() == canonicalizeBSONType(mongo::String)); const NamespaceString nss(first.valueStringData()); uassert(ErrorCodes::InvalidNamespace, str::stream() << "Invalid namespace specified '" << nss.ns() << "'", nss.isValid()); return nss.ns(); }
BSONObj Cloner::getIdIndexSpec(const std::list<BSONObj>& indexSpecs) { for (auto&& indexSpec : indexSpecs) { BSONElement indexName; uassertStatusOK(bsonExtractTypedField( indexSpec, IndexDescriptor::kIndexNameFieldName, String, &indexName)); if (indexName.valueStringData() == "_id_"_sd) { return indexSpec; } } return BSONObj(); }
NamespaceString Command::parseNsCollectionRequired(const string& dbname, const BSONObj& cmdObj) { // Accepts both BSON String and Symbol for collection name per SERVER-16260 // TODO(kangas) remove Symbol support in MongoDB 3.0 after Ruby driver audit BSONElement first = cmdObj.firstElement(); uassert(ErrorCodes::BadValue, str::stream() << "collection name has invalid type " << typeName(first.type()), first.canonicalType() == canonicalizeBSONType(mongo::String)); const NamespaceString nss(dbname, first.valueStringData()); uassert(ErrorCodes::InvalidNamespace, str::stream() << "Invalid namespace specified '" << nss.ns() << "'", nss.isValid()); return nss; }
Status RenameNode::init(BSONElement modExpr, const boost::intrusive_ptr<ExpressionContext>& expCtx) { invariant(modExpr.ok()); invariant(BSONType::String == modExpr.type()); FieldRef fromFieldRef(modExpr.fieldName()); FieldRef toFieldRef(modExpr.String()); if (modExpr.valueStringData().find('\0') != std::string::npos) { return Status(ErrorCodes::BadValue, "The 'to' field for $rename cannot contain an embedded null byte"); } // Parsing {$rename: {'from': 'to'}} places nodes in the UpdateNode tree for both the "from" and // "to" paths via UpdateObjectNode::parseAndMerge(), which will enforce this isUpdatable // property. dassert(fieldchecker::isUpdatable(fromFieldRef).isOK()); dassert(fieldchecker::isUpdatable(toFieldRef).isOK()); // Though we could treat this as a no-op, it is illegal in the current implementation. if (fromFieldRef == toFieldRef) { return Status(ErrorCodes::BadValue, str::stream() << "The source and target field for $rename must differ: " << modExpr); } if (fromFieldRef.isPrefixOf(toFieldRef) || toFieldRef.isPrefixOf(fromFieldRef)) { return Status(ErrorCodes::BadValue, str::stream() << "The source and target field for $rename must " "not be on the same path: " << modExpr); } size_t dummyPos; if (fieldchecker::isPositional(fromFieldRef, &dummyPos) || fieldchecker::hasArrayFilter(fromFieldRef)) { return Status(ErrorCodes::BadValue, str::stream() << "The source field for $rename may not be dynamic: " << fromFieldRef.dottedField()); } else if (fieldchecker::isPositional(toFieldRef, &dummyPos) || fieldchecker::hasArrayFilter(toFieldRef)) { return Status(ErrorCodes::BadValue, str::stream() << "The destination field for $rename may not be dynamic: " << toFieldRef.dottedField()); } _val = modExpr; return Status::OK(); }
std::unique_ptr<LiteParsedDocumentSourceForeignCollections> DocumentSourceOut::liteParse( const AggregationRequest& request, const BSONElement& spec) { uassert(ErrorCodes::TypeMismatch, str::stream() << "$out stage requires a string argument, but found " << typeName(spec.type()), spec.type() == BSONType::String); NamespaceString targetNss(request.getNamespaceString().db(), spec.valueStringData()); uassert(ErrorCodes::InvalidNamespace, str::stream() << "Invalid $out target namespace, " << targetNss.ns(), targetNss.isValid()); ActionSet actions{ActionType::remove, ActionType::insert}; if (request.shouldBypassDocumentValidation()) { actions.addAction(ActionType::bypassDocumentValidation); } PrivilegeVector privileges{Privilege(ResourcePattern::forExactNamespace(targetNss), actions)}; return stdx::make_unique<LiteParsedDocumentSourceForeignCollections>(std::move(targetNss), std::move(privileges)); }
// static Status WiredTigerUtil::checkTableCreationOptions(const BSONElement& configElem) { invariant(configElem.fieldNameStringData() == "configString"); if (configElem.type() != String) { return {ErrorCodes::TypeMismatch, "'configString' must be a string."}; } std::vector<std::string> errors; ErrorAccumulator eventHandler(&errors); StringData config = configElem.valueStringData(); Status status = wtRCToStatus( wiredtiger_config_validate(nullptr, &eventHandler, "WT_SESSION.create", config.rawData())); if (!status.isOK()) { StringBuilder errorMsg; errorMsg << status.reason(); for (std::string error : errors) { errorMsg << ". " << error; } errorMsg << "."; return {status.code(), errorMsg.str()}; } return Status::OK(); }
Status V2UserDocumentParser::checkValidUserDocument(const BSONObj& doc) const { BSONElement userElement = doc[AuthorizationManager::USER_NAME_FIELD_NAME]; BSONElement userDBElement = doc[AuthorizationManager::USER_DB_FIELD_NAME]; BSONElement credentialsElement = doc[CREDENTIALS_FIELD_NAME]; BSONElement rolesElement = doc[ROLES_FIELD_NAME]; // Validate the "user" element. if (userElement.type() != String) return _badValue("User document needs 'user' field to be a string"); if (userElement.valueStringData().empty()) return _badValue("User document needs 'user' field to be non-empty"); // Validate the "db" element if (userDBElement.type() != String || userDBElement.valueStringData().empty()) { return _badValue("User document needs 'db' field to be a non-empty string"); } StringData userDBStr = userDBElement.valueStringData(); if (!NamespaceString::validDBName(userDBStr, NamespaceString::DollarInDbNameBehavior::Allow) && userDBStr != "$external") { return _badValue(mongoutils::str::stream() << "'" << userDBStr << "' is not a valid value for the db field."); } // Validate the "credentials" element if (credentialsElement.eoo()) { return _badValue("User document needs 'credentials' object"); } if (credentialsElement.type() != Object) { return _badValue("User document needs 'credentials' field to be an object"); } BSONObj credentialsObj = credentialsElement.Obj(); if (credentialsObj.isEmpty()) { return _badValue("User document needs 'credentials' field to be a non-empty object"); } if (userDBStr == "$external") { BSONElement externalElement = credentialsObj[MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME]; if (externalElement.eoo() || externalElement.type() != Bool || !externalElement.Bool()) { return _badValue( "User documents for users defined on '$external' must have " "'credentials' field set to {external: true}"); } } else { const auto validateScram = [&credentialsObj](const auto& fieldName) { auto scramElement = credentialsObj[fieldName]; if (scramElement.eoo()) { return Status(ErrorCodes::NoSuchKey, str::stream() << fieldName << " does not exist"); } if (scramElement.type() != Object) { return _badValue(str::stream() << fieldName << " credential must be an object, if present"); } return Status::OK(); }; const auto sha1status = validateScram(SCRAMSHA1_CREDENTIAL_FIELD_NAME); if (!sha1status.isOK() && (sha1status.code() != ErrorCodes::NoSuchKey)) { return sha1status; } const auto sha256status = validateScram(SCRAMSHA256_CREDENTIAL_FIELD_NAME); if (!sha256status.isOK() && (sha256status.code() != ErrorCodes::NoSuchKey)) { return sha256status; } if (!sha1status.isOK() && !sha256status.isOK()) { return _badValue( "User document must provide credentials for all " "non-external users"); } } // Validate the "roles" element. Status status = _checkV2RolesArray(rolesElement); if (!status.isOK()) return status; // Validate the "authenticationRestrictions" element. status = initializeAuthenticationRestrictionsFromUserDocument(doc, nullptr); if (!status.isOK()) { return status; } return Status::OK(); }
Status V2UserDocumentParser::checkValidUserDocument(const BSONObj& doc) const { BSONElement userElement = doc[AuthorizationManager::USER_NAME_FIELD_NAME]; BSONElement userDBElement = doc[AuthorizationManager::USER_DB_FIELD_NAME]; BSONElement credentialsElement = doc[CREDENTIALS_FIELD_NAME]; BSONElement rolesElement = doc[ROLES_FIELD_NAME]; // Validate the "user" element. if (userElement.type() != String) return _badValue("User document needs 'user' field to be a string", 0); if (userElement.valueStringData().empty()) return _badValue("User document needs 'user' field to be non-empty", 0); // Validate the "db" element if (userDBElement.type() != String || userDBElement.valueStringData().empty()) { return _badValue("User document needs 'db' field to be a non-empty string", 0); } StringData userDBStr = userDBElement.valueStringData(); if (!NamespaceString::validDBName(userDBStr) && userDBStr != "$external") { return _badValue(mongoutils::str::stream() << "'" << userDBStr << "' is not a valid value for the db field.", 0); } // Validate the "credentials" element if (credentialsElement.eoo()) { return _badValue("User document needs 'credentials' object", 0); } if (credentialsElement.type() != Object) { return _badValue("User document needs 'credentials' field to be an object", 0); } BSONObj credentialsObj = credentialsElement.Obj(); if (credentialsObj.isEmpty()) { return _badValue("User document needs 'credentials' field to be a non-empty object", 0); } if (userDBStr == "$external") { BSONElement externalElement = credentialsObj[MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME]; if (externalElement.eoo() || externalElement.type() != Bool || !externalElement.Bool()) { return _badValue("User documents for users defined on '$external' must have " "'credentials' field set to {external: true}", 0); } } else { BSONElement scramElement = credentialsObj[SCRAM_CREDENTIAL_FIELD_NAME]; BSONElement mongoCRElement = credentialsObj[MONGODB_CR_CREDENTIAL_FIELD_NAME]; if (!mongoCRElement.eoo()) { if (mongoCRElement.type() != String || mongoCRElement.valueStringData().empty()) { return _badValue("MONGODB-CR credential must to be a non-empty string" ", if present", 0); } } else if (!scramElement.eoo()) { if (scramElement.type() != Object) { return _badValue("SCRAM credential must be an object, if present", 0); } } else { return _badValue("User document must provide credentials for all " "non-external users", 0); } } // Validate the "roles" element. Status status = _checkV2RolesArray(rolesElement); if (!status.isOK()) return status; return Status::OK(); }
int BSONElement::compareElements(const BSONElement& l, const BSONElement& r, ComparisonRulesSet rules, const StringData::ComparatorInterface* comparator) { switch (l.type()) { case BSONType::EOO: case BSONType::Undefined: // EOO and Undefined are same canonicalType case BSONType::jstNULL: case BSONType::MaxKey: case BSONType::MinKey: { auto f = l.canonicalType() - r.canonicalType(); if (f < 0) return -1; return f == 0 ? 0 : 1; } case BSONType::Bool: return *l.value() - *r.value(); case BSONType::bsonTimestamp: // unsigned compare for timestamps - note they are not really dates but (ordinal + // time_t) if (l.timestamp() < r.timestamp()) return -1; return l.timestamp() == r.timestamp() ? 0 : 1; case BSONType::Date: // Signed comparisons for Dates. { const Date_t a = l.Date(); const Date_t b = r.Date(); if (a < b) return -1; return a == b ? 0 : 1; } case BSONType::NumberInt: { // All types can precisely represent all NumberInts, so it is safe to simply convert to // whatever rhs's type is. switch (r.type()) { case NumberInt: return compareInts(l._numberInt(), r._numberInt()); case NumberLong: return compareLongs(l._numberInt(), r._numberLong()); case NumberDouble: return compareDoubles(l._numberInt(), r._numberDouble()); case NumberDecimal: return compareIntToDecimal(l._numberInt(), r._numberDecimal()); default: MONGO_UNREACHABLE; } } case BSONType::NumberLong: { switch (r.type()) { case NumberLong: return compareLongs(l._numberLong(), r._numberLong()); case NumberInt: return compareLongs(l._numberLong(), r._numberInt()); case NumberDouble: return compareLongToDouble(l._numberLong(), r._numberDouble()); case NumberDecimal: return compareLongToDecimal(l._numberLong(), r._numberDecimal()); default: MONGO_UNREACHABLE; } } case BSONType::NumberDouble: { switch (r.type()) { case NumberDouble: return compareDoubles(l._numberDouble(), r._numberDouble()); case NumberInt: return compareDoubles(l._numberDouble(), r._numberInt()); case NumberLong: return compareDoubleToLong(l._numberDouble(), r._numberLong()); case NumberDecimal: return compareDoubleToDecimal(l._numberDouble(), r._numberDecimal()); default: MONGO_UNREACHABLE; } } case BSONType::NumberDecimal: { switch (r.type()) { case NumberDecimal: return compareDecimals(l._numberDecimal(), r._numberDecimal()); case NumberInt: return compareDecimalToInt(l._numberDecimal(), r._numberInt()); case NumberLong: return compareDecimalToLong(l._numberDecimal(), r._numberLong()); case NumberDouble: return compareDecimalToDouble(l._numberDecimal(), r._numberDouble()); default: MONGO_UNREACHABLE; } } case BSONType::jstOID: return memcmp(l.value(), r.value(), OID::kOIDSize); case BSONType::Code: return compareElementStringValues(l, r); case BSONType::Symbol: case BSONType::String: { if (comparator) { return comparator->compare(l.valueStringData(), r.valueStringData()); } else { return compareElementStringValues(l, r); } } case BSONType::Object: case BSONType::Array: { return l.embeddedObject().woCompare( r.embeddedObject(), BSONObj(), rules | BSONElement::ComparisonRules::kConsiderFieldName, comparator); } case BSONType::DBRef: { int lsz = l.valuesize(); int rsz = r.valuesize(); if (lsz - rsz != 0) return lsz - rsz; return memcmp(l.value(), r.value(), lsz); } case BSONType::BinData: { int lsz = l.objsize(); // our bin data size in bytes, not including the subtype byte int rsz = r.objsize(); if (lsz - rsz != 0) return lsz - rsz; return memcmp(l.value() + 4, r.value() + 4, lsz + 1 /*+1 for subtype byte*/); } case BSONType::RegEx: { int c = strcmp(l.regex(), r.regex()); if (c) return c; return strcmp(l.regexFlags(), r.regexFlags()); } case BSONType::CodeWScope: { int cmp = StringData(l.codeWScopeCode(), l.codeWScopeCodeLen() - 1) .compare(StringData(r.codeWScopeCode(), r.codeWScopeCodeLen() - 1)); if (cmp) return cmp; // When comparing the scope object, we should consider field names. Special string // comparison semantics do not apply to strings nested inside the CodeWScope scope // object, so we do not pass through the string comparator. return l.codeWScopeObject().woCompare( r.codeWScopeObject(), BSONObj(), rules | BSONElement::ComparisonRules::kConsiderFieldName); } } MONGO_UNREACHABLE; }
Status V2UserDocumentParser::initializeUserCredentialsFromUserDocument( User* user, const BSONObj& privDoc) const { User::CredentialData credentials; std::string userDB = privDoc[AuthorizationManager::USER_DB_FIELD_NAME].String(); BSONElement credentialsElement = privDoc[CREDENTIALS_FIELD_NAME]; if (!credentialsElement.eoo()) { if (credentialsElement.type() != Object) { return Status(ErrorCodes::UnsupportedFormat, "'credentials' field in user documents must be an object"); } if (userDB == "$external") { BSONElement externalCredentialElement = credentialsElement.Obj()[MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME]; if (!externalCredentialElement.eoo()) { if (externalCredentialElement.type() != Bool || !externalCredentialElement.Bool()) { return Status(ErrorCodes::UnsupportedFormat, "'external' field in credentials object must be set to true"); } else { credentials.isExternal = true; } } else { return Status(ErrorCodes::UnsupportedFormat, "User documents defined on '$external' must provide set " "credentials to {external:true}"); } } else { BSONElement scramElement = credentialsElement.Obj()[SCRAM_CREDENTIAL_FIELD_NAME]; BSONElement mongoCRCredentialElement = credentialsElement.Obj()[MONGODB_CR_CREDENTIAL_FIELD_NAME]; if (scramElement.eoo() && mongoCRCredentialElement.eoo()) { return Status(ErrorCodes::UnsupportedFormat, "User documents must provide credentials for SCRAM-SHA-1 " "or MONGODB-CR authentication"); } if (!scramElement.eoo()) { // We are asserting rather then returning errors since these // fields should have been prepopulated by the calling code. credentials.scram.iterationCount = scramElement.Obj()["iterationCount"].numberInt(); uassert(17501, "Invalid or missing SCRAM iteration count", credentials.scram.iterationCount > 0); credentials.scram.salt = scramElement.Obj()["salt"].str(); uassert(17502, "Missing SCRAM salt", !credentials.scram.salt.empty()); credentials.scram.serverKey = scramElement["serverKey"].str(); uassert(17503, "Missing SCRAM serverKey", !credentials.scram.serverKey.empty()); credentials.scram.storedKey = scramElement["storedKey"].str(); uassert(17504, "Missing SCRAM storedKey", !credentials.scram.storedKey.empty()); } if (!mongoCRCredentialElement.eoo()) { if (mongoCRCredentialElement.type() != String || mongoCRCredentialElement.valueStringData().empty()) { return Status(ErrorCodes::UnsupportedFormat, "MONGODB-CR credentials must be non-empty strings"); } else { credentials.password = mongoCRCredentialElement.String(); if (credentials.password.empty()) { return Status(ErrorCodes::UnsupportedFormat, "User documents must provide authentication credentials"); } } } credentials.isExternal = false; } } else { return Status(ErrorCodes::UnsupportedFormat, "Cannot extract credentials from user documents without a " "'credentials' field"); } user->setCredentials(credentials); return Status::OK(); }
// static bool QueryPlannerTestLib::solutionMatches(const BSONObj& testSoln, const QuerySolutionNode* trueSoln) { // // leaf nodes // if (STAGE_COLLSCAN == trueSoln->getType()) { const CollectionScanNode* csn = static_cast<const CollectionScanNode*>(trueSoln); BSONElement el = testSoln["cscan"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj csObj = el.Obj(); BSONElement dir = csObj["dir"]; if (dir.eoo() || !dir.isNumber()) { return false; } if (dir.numberInt() != csn->direction) { return false; } BSONElement filter = csObj["filter"]; if (filter.eoo()) { return true; } else if (filter.isNull()) { return NULL == csn->filter; } else if (!filter.isABSONObj()) { return false; } BSONObj collation; if (BSONElement collationElt = csObj["collation"]) { if (!collationElt.isABSONObj()) { return false; } collation = collationElt.Obj(); } return filterMatches(filter.Obj(), collation, trueSoln); } else if (STAGE_IXSCAN == trueSoln->getType()) { const IndexScanNode* ixn = static_cast<const IndexScanNode*>(trueSoln); BSONElement el = testSoln["ixscan"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj ixscanObj = el.Obj(); BSONElement pattern = ixscanObj["pattern"]; if (!pattern.eoo()) { if (!pattern.isABSONObj()) { return false; } if (SimpleBSONObjComparator::kInstance.evaluate(pattern.Obj() != ixn->index.keyPattern)) { return false; } } BSONElement name = ixscanObj["name"]; if (!name.eoo()) { if (name.type() != BSONType::String) { return false; } if (name.valueStringData() != ixn->index.name) { return false; } } if (name.eoo() && pattern.eoo()) { return false; } BSONElement bounds = ixscanObj["bounds"]; if (!bounds.eoo()) { if (!bounds.isABSONObj()) { return false; } else if (!boundsMatch(bounds.Obj(), ixn->bounds)) { return false; } } BSONElement dir = ixscanObj["dir"]; if (!dir.eoo() && NumberInt == dir.type()) { if (dir.numberInt() != ixn->direction) { return false; } } BSONElement filter = ixscanObj["filter"]; if (filter.eoo()) { return true; } else if (filter.isNull()) { return NULL == ixn->filter; } else if (!filter.isABSONObj()) { return false; } BSONObj collation; if (BSONElement collationElt = ixscanObj["collation"]) { if (!collationElt.isABSONObj()) { return false; } collation = collationElt.Obj(); } return filterMatches(filter.Obj(), collation, trueSoln); } else if (STAGE_GEO_NEAR_2D == trueSoln->getType()) { const GeoNear2DNode* node = static_cast<const GeoNear2DNode*>(trueSoln); BSONElement el = testSoln["geoNear2d"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj geoObj = el.Obj(); return SimpleBSONObjComparator::kInstance.evaluate(geoObj == node->index.keyPattern); } else if (STAGE_GEO_NEAR_2DSPHERE == trueSoln->getType()) { const GeoNear2DSphereNode* node = static_cast<const GeoNear2DSphereNode*>(trueSoln); BSONElement el = testSoln["geoNear2dsphere"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj geoObj = el.Obj(); BSONElement pattern = geoObj["pattern"]; if (pattern.eoo() || !pattern.isABSONObj()) { return false; } if (SimpleBSONObjComparator::kInstance.evaluate(pattern.Obj() != node->index.keyPattern)) { return false; } BSONElement bounds = geoObj["bounds"]; if (!bounds.eoo()) { if (!bounds.isABSONObj()) { return false; } else if (!boundsMatch(bounds.Obj(), node->baseBounds)) { return false; } } return true; } else if (STAGE_TEXT == trueSoln->getType()) { // {text: {search: "somestr", language: "something", filter: {blah: 1}}} const TextNode* node = static_cast<const TextNode*>(trueSoln); BSONElement el = testSoln["text"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj textObj = el.Obj(); BSONElement searchElt = textObj["search"]; if (!searchElt.eoo()) { if (searchElt.String() != node->ftsQuery->getQuery()) { return false; } } BSONElement languageElt = textObj["language"]; if (!languageElt.eoo()) { if (languageElt.String() != node->ftsQuery->getLanguage()) { return false; } } BSONElement caseSensitiveElt = textObj["caseSensitive"]; if (!caseSensitiveElt.eoo()) { if (caseSensitiveElt.trueValue() != node->ftsQuery->getCaseSensitive()) { return false; } } BSONElement diacriticSensitiveElt = textObj["diacriticSensitive"]; if (!diacriticSensitiveElt.eoo()) { if (diacriticSensitiveElt.trueValue() != node->ftsQuery->getDiacriticSensitive()) { return false; } } BSONElement indexPrefix = textObj["prefix"]; if (!indexPrefix.eoo()) { if (!indexPrefix.isABSONObj()) { return false; } if (0 != indexPrefix.Obj().woCompare(node->indexPrefix)) { return false; } } BSONObj collation; if (BSONElement collationElt = textObj["collation"]) { if (!collationElt.isABSONObj()) { return false; } collation = collationElt.Obj(); } BSONElement filter = textObj["filter"]; if (!filter.eoo()) { if (filter.isNull()) { if (NULL != node->filter) { return false; } } else if (!filter.isABSONObj()) { return false; } else if (!filterMatches(filter.Obj(), collation, trueSoln)) { return false; } } return true; } // // internal nodes // if (STAGE_FETCH == trueSoln->getType()) { const FetchNode* fn = static_cast<const FetchNode*>(trueSoln); BSONElement el = testSoln["fetch"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj fetchObj = el.Obj(); BSONObj collation; if (BSONElement collationElt = fetchObj["collation"]) { if (!collationElt.isABSONObj()) { return false; } collation = collationElt.Obj(); } BSONElement filter = fetchObj["filter"]; if (!filter.eoo()) { if (filter.isNull()) { if (NULL != fn->filter) { return false; } } else if (!filter.isABSONObj()) { return false; } else if (!filterMatches(filter.Obj(), collation, trueSoln)) { return false; } } BSONElement child = fetchObj["node"]; if (child.eoo() || !child.isABSONObj()) { return false; } return solutionMatches(child.Obj(), fn->children[0]); } else if (STAGE_OR == trueSoln->getType()) { const OrNode* orn = static_cast<const OrNode*>(trueSoln); BSONElement el = testSoln["or"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj orObj = el.Obj(); return childrenMatch(orObj, orn); } else if (STAGE_AND_HASH == trueSoln->getType()) { const AndHashNode* ahn = static_cast<const AndHashNode*>(trueSoln); BSONElement el = testSoln["andHash"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj andHashObj = el.Obj(); BSONObj collation; if (BSONElement collationElt = andHashObj["collation"]) { if (!collationElt.isABSONObj()) { return false; } collation = collationElt.Obj(); } BSONElement filter = andHashObj["filter"]; if (!filter.eoo()) { if (filter.isNull()) { if (NULL != ahn->filter) { return false; } } else if (!filter.isABSONObj()) { return false; } else if (!filterMatches(filter.Obj(), collation, trueSoln)) { return false; } } return childrenMatch(andHashObj, ahn); } else if (STAGE_AND_SORTED == trueSoln->getType()) { const AndSortedNode* asn = static_cast<const AndSortedNode*>(trueSoln); BSONElement el = testSoln["andSorted"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj andSortedObj = el.Obj(); BSONObj collation; if (BSONElement collationElt = andSortedObj["collation"]) { if (!collationElt.isABSONObj()) { return false; } collation = collationElt.Obj(); } BSONElement filter = andSortedObj["filter"]; if (!filter.eoo()) { if (filter.isNull()) { if (NULL != asn->filter) { return false; } } else if (!filter.isABSONObj()) { return false; } else if (!filterMatches(filter.Obj(), collation, trueSoln)) { return false; } } return childrenMatch(andSortedObj, asn); } else if (STAGE_PROJECTION == trueSoln->getType()) { const ProjectionNode* pn = static_cast<const ProjectionNode*>(trueSoln); BSONElement el = testSoln["proj"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj projObj = el.Obj(); BSONElement projType = projObj["type"]; if (!projType.eoo()) { string projTypeStr = projType.str(); if (!((pn->projType == ProjectionNode::DEFAULT && projTypeStr == "default") || (pn->projType == ProjectionNode::SIMPLE_DOC && projTypeStr == "simple") || (pn->projType == ProjectionNode::COVERED_ONE_INDEX && projTypeStr == "coveredIndex"))) { return false; } } BSONElement spec = projObj["spec"]; if (spec.eoo() || !spec.isABSONObj()) { return false; } BSONElement child = projObj["node"]; if (child.eoo() || !child.isABSONObj()) { return false; } return SimpleBSONObjComparator::kInstance.evaluate(spec.Obj() == pn->projection) && solutionMatches(child.Obj(), pn->children[0]); } else if (STAGE_SORT == trueSoln->getType()) { const SortNode* sn = static_cast<const SortNode*>(trueSoln); BSONElement el = testSoln["sort"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj sortObj = el.Obj(); BSONElement patternEl = sortObj["pattern"]; if (patternEl.eoo() || !patternEl.isABSONObj()) { return false; } BSONElement limitEl = sortObj["limit"]; if (!limitEl.isNumber()) { return false; } BSONElement child = sortObj["node"]; if (child.eoo() || !child.isABSONObj()) { return false; } size_t expectedLimit = limitEl.numberInt(); return SimpleBSONObjComparator::kInstance.evaluate(patternEl.Obj() == sn->pattern) && (expectedLimit == sn->limit) && solutionMatches(child.Obj(), sn->children[0]); } else if (STAGE_SORT_KEY_GENERATOR == trueSoln->getType()) { const SortKeyGeneratorNode* keyGenNode = static_cast<const SortKeyGeneratorNode*>(trueSoln); BSONElement el = testSoln["sortKeyGen"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj keyGenObj = el.Obj(); BSONElement child = keyGenObj["node"]; if (child.eoo() || !child.isABSONObj()) { return false; } return solutionMatches(child.Obj(), keyGenNode->children[0]); } else if (STAGE_SORT_MERGE == trueSoln->getType()) { const MergeSortNode* msn = static_cast<const MergeSortNode*>(trueSoln); BSONElement el = testSoln["mergeSort"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj mergeSortObj = el.Obj(); return childrenMatch(mergeSortObj, msn); } else if (STAGE_SKIP == trueSoln->getType()) { const SkipNode* sn = static_cast<const SkipNode*>(trueSoln); BSONElement el = testSoln["skip"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj sortObj = el.Obj(); BSONElement skipEl = sortObj["n"]; if (!skipEl.isNumber()) { return false; } BSONElement child = sortObj["node"]; if (child.eoo() || !child.isABSONObj()) { return false; } return (skipEl.numberInt() == sn->skip) && solutionMatches(child.Obj(), sn->children[0]); } else if (STAGE_LIMIT == trueSoln->getType()) { const LimitNode* ln = static_cast<const LimitNode*>(trueSoln); BSONElement el = testSoln["limit"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj sortObj = el.Obj(); BSONElement limitEl = sortObj["n"]; if (!limitEl.isNumber()) { return false; } BSONElement child = sortObj["node"]; if (child.eoo() || !child.isABSONObj()) { return false; } return (limitEl.numberInt() == ln->limit) && solutionMatches(child.Obj(), ln->children[0]); } else if (STAGE_KEEP_MUTATIONS == trueSoln->getType()) { const KeepMutationsNode* kn = static_cast<const KeepMutationsNode*>(trueSoln); BSONElement el = testSoln["keep"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj keepObj = el.Obj(); // Doesn't have any parameters really. BSONElement child = keepObj["node"]; if (child.eoo() || !child.isABSONObj()) { return false; } return solutionMatches(child.Obj(), kn->children[0]); } else if (STAGE_SHARDING_FILTER == trueSoln->getType()) { const ShardingFilterNode* fn = static_cast<const ShardingFilterNode*>(trueSoln); BSONElement el = testSoln["sharding_filter"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj keepObj = el.Obj(); BSONElement child = keepObj["node"]; if (child.eoo() || !child.isABSONObj()) { return false; } return solutionMatches(child.Obj(), fn->children[0]); } else if (STAGE_ENSURE_SORTED == trueSoln->getType()) { const EnsureSortedNode* esn = static_cast<const EnsureSortedNode*>(trueSoln); BSONElement el = testSoln["ensureSorted"]; if (el.eoo() || !el.isABSONObj()) { return false; } BSONObj esObj = el.Obj(); BSONElement patternEl = esObj["pattern"]; if (patternEl.eoo() || !patternEl.isABSONObj()) { return false; } BSONElement child = esObj["node"]; if (child.eoo() || !child.isABSONObj()) { return false; } return SimpleBSONObjComparator::kInstance.evaluate(patternEl.Obj() == esn->pattern) && solutionMatches(child.Obj(), esn->children[0]); } return false; }
bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result) { BSONElement first = cmdObj.firstElement(); uassert(28528, str::stream() << "Argument to listIndexes must be of type String, not " << typeName(first.type()), first.type() == String); StringData collectionName = first.valueStringData(); uassert(28529, str::stream() << "Argument to listIndexes must be a collection name, " << "not the empty string", !collectionName.empty()); const NamespaceString ns(dbname, collectionName); const long long defaultBatchSize = std::numeric_limits<long long>::max(); long long batchSize; Status parseCursorStatus = parseCommandCursorOptions(cmdObj, defaultBatchSize, &batchSize); if (!parseCursorStatus.isOK()) { return appendCommandStatus(result, parseCursorStatus); } AutoGetCollectionForRead autoColl(txn, ns); if (!autoColl.getDb()) { return appendCommandStatus(result, Status(ErrorCodes::NamespaceNotFound, "no database")); } const Collection* collection = autoColl.getCollection(); if (!collection) { return appendCommandStatus(result, Status(ErrorCodes::NamespaceNotFound, "no collection")); } const CollectionCatalogEntry* cce = collection->getCatalogEntry(); invariant(cce); vector<string> indexNames; MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { indexNames.clear(); cce->getAllIndexes(txn, &indexNames); } MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "listIndexes", ns.ns()); std::unique_ptr<WorkingSet> ws(new WorkingSet()); std::unique_ptr<QueuedDataStage> root(new QueuedDataStage(ws.get())); for (size_t i = 0; i < indexNames.size(); i++) { BSONObj indexSpec; MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { indexSpec = cce->getIndexSpec(txn, indexNames[i]); } MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "listIndexes", ns.ns()); WorkingSetID id = ws->allocate(); WorkingSetMember* member = ws->get(id); member->keyData.clear(); member->loc = RecordId(); member->obj = Snapshotted<BSONObj>(SnapshotId(), indexSpec.getOwned()); member->transitionToOwnedObj(); root->pushBack(id); } std::string cursorNamespace = str::stream() << dbname << ".$cmd." << name << "." << ns.coll(); dassert(NamespaceString(cursorNamespace).isValid()); dassert(NamespaceString(cursorNamespace).isListIndexesCursorNS()); dassert(ns == NamespaceString(cursorNamespace).getTargetNSForListIndexes()); auto statusWithPlanExecutor = PlanExecutor::make( txn, std::move(ws), std::move(root), cursorNamespace, PlanExecutor::YIELD_MANUAL); if (!statusWithPlanExecutor.isOK()) { return appendCommandStatus(result, statusWithPlanExecutor.getStatus()); } std::unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue()); BSONArrayBuilder firstBatch; const int byteLimit = MaxBytesToReturnToClientAtOnce; for (long long objCount = 0; objCount < batchSize && firstBatch.len() < byteLimit; objCount++) { BSONObj next; PlanExecutor::ExecState state = exec->getNext(&next, NULL); if (state == PlanExecutor::IS_EOF) { break; } invariant(state == PlanExecutor::ADVANCED); firstBatch.append(next); } CursorId cursorId = 0LL; if (!exec->isEOF()) { exec->saveState(); ClientCursor* cursor = new ClientCursor( CursorManager::getGlobalCursorManager(), exec.release(), cursorNamespace); cursorId = cursor->cursorid(); } appendCursorResponseObject(cursorId, cursorNamespace, firstBatch.arr(), &result); return true; }
void BSONComparatorInterfaceBase<T>::hashCombineBSONElement( size_t& hash, BSONElement elemToHash, bool considerFieldName, const StringData::ComparatorInterface* stringComparator) { boost::hash_combine(hash, elemToHash.canonicalType()); const StringData fieldName = elemToHash.fieldNameStringData(); if (considerFieldName && !fieldName.empty()) { SimpleStringDataComparator::kInstance.hash_combine(hash, fieldName); } switch (elemToHash.type()) { // Order of types is the same as in compareElementValues(). case mongo::EOO: case mongo::Undefined: case mongo::jstNULL: case mongo::MaxKey: case mongo::MinKey: // These are valueless types break; case mongo::Bool: boost::hash_combine(hash, elemToHash.boolean()); break; case mongo::bsonTimestamp: boost::hash_combine(hash, elemToHash.timestamp().asULL()); break; case mongo::Date: boost::hash_combine(hash, elemToHash.date().asInt64()); break; case mongo::NumberDecimal: { const Decimal128 dcml = elemToHash.numberDecimal(); if (dcml.toAbs().isGreater(Decimal128(std::numeric_limits<double>::max(), Decimal128::kRoundTo34Digits, Decimal128::kRoundTowardZero)) && !dcml.isInfinite() && !dcml.isNaN()) { // Normalize our decimal to force equivalent decimals // in the same cohort to hash to the same value Decimal128 dcmlNorm(dcml.normalize()); boost::hash_combine(hash, dcmlNorm.getValue().low64); boost::hash_combine(hash, dcmlNorm.getValue().high64); break; } // Else, fall through and convert the decimal to a double and hash. // At this point the decimal fits into the range of doubles, is infinity, or is NaN, // which doubles have a cheaper representation for. } case mongo::NumberDouble: case mongo::NumberLong: case mongo::NumberInt: { // This converts all numbers to doubles, which ignores the low-order bits of // NumberLongs > 2**53 and precise decimal numbers without double representations, // but that is ok since the hash will still be the same for equal numbers and is // still likely to be different for different numbers. (Note: this issue only // applies for decimals when they are outside of the valid double range. See // the above case.) // SERVER-16851 const double dbl = elemToHash.numberDouble(); if (std::isnan(dbl)) { boost::hash_combine(hash, std::numeric_limits<double>::quiet_NaN()); } else { boost::hash_combine(hash, dbl); } break; } case mongo::jstOID: elemToHash.__oid().hash_combine(hash); break; case mongo::String: { if (stringComparator) { stringComparator->hash_combine(hash, elemToHash.valueStringData()); } else { SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.valueStringData()); } break; } case mongo::Code: case mongo::Symbol: SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.valueStringData()); break; case mongo::Object: case mongo::Array: hashCombineBSONObj(hash, elemToHash.embeddedObject(), true, // considerFieldName stringComparator); break; case mongo::DBRef: case mongo::BinData: // All bytes of the value are required to be identical. SimpleStringDataComparator::kInstance.hash_combine( hash, StringData(elemToHash.value(), elemToHash.valuesize())); break; case mongo::RegEx: SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.regex()); SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.regexFlags()); break; case mongo::CodeWScope: { SimpleStringDataComparator::kInstance.hash_combine( hash, StringData(elemToHash.codeWScopeCode(), elemToHash.codeWScopeCodeLen())); hashCombineBSONObj(hash, elemToHash.codeWScopeObject(), true, // considerFieldName &SimpleStringDataComparator::kInstance); break; } } }
void ShardingConnectionHook::onCreate(DBClientBase* conn) { // Authenticate as the first thing we do // NOTE: Replica set authentication allows authentication against *any* online host if (getGlobalAuthorizationManager()->isAuthEnabled()) { LOG(2) << "calling onCreate auth for " << conn->toString(); bool result = conn->authenticateInternalUser(); uassert(15847, str::stream() << "can't authenticate to server " << conn->getServerAddress(), result); } if (_shardedConnections) { // For every DBClient created by mongos, add a hook that will capture the response from // commands we pass along from the client, so that we can target the correct node when // subsequent getLastError calls are made by mongos. conn->setReplyMetadataReader([](const BSONObj& metadataObj, StringData hostString) -> Status { saveGLEStats(metadataObj, hostString); return Status::OK(); }); } // For every DBClient created by mongos, add a hook that will append impersonated users // to the end of every runCommand. mongod uses this information to produce auditing // records attributed to the proper authenticated user(s). conn->setRequestMetadataWriter([](BSONObjBuilder* metadataBob) -> Status { audit::writeImpersonatedUsersToMetadata(metadataBob); return Status::OK(); }); // For every SCC created, add a hook that will allow fastest-config-first config reads if // the appropriate server options are set. if (conn->type() == ConnectionString::SYNC) { SyncClusterConnection* scc = dynamic_cast<SyncClusterConnection*>(conn); if (scc) { scc->attachQueryHandler(new SCCFastQueryHandler); } } else if (conn->type() == ConnectionString::MASTER) { BSONObj isMasterResponse; if (!conn->runCommand("admin", BSON("ismaster" << 1), isMasterResponse)) { uassertStatusOK(getStatusFromCommandResult(isMasterResponse)); } long long configServerModeNumber; Status status = bsonExtractIntegerField(isMasterResponse, "configsvr", &configServerModeNumber); if (status == ErrorCodes::NoSuchKey) { // This isn't a config server we're talking to. return; } uassert(28785, str::stream() << "Unrecognized configsvr version number: " << configServerModeNumber << ". Expected either 0 or 1", configServerModeNumber == 0 || configServerModeNumber == 1); BSONElement setName = isMasterResponse["setName"]; status = grid.catalogManager()->scheduleReplaceCatalogManagerIfNeeded( configServerModeNumber == 0 ? CatalogManager::ConfigServerMode::SCCC : CatalogManager::ConfigServerMode::CSRS, setName.type() == String ? setName.valueStringData() : StringData(), static_cast<DBClientConnection*>(conn)->getServerHostAndPort()); uassertStatusOK(status); } }
vector<intrusive_ptr<DocumentSource>> DocumentSourceBucket::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) { uassert(40201, str::stream() << "Argument to $bucket stage must be an object, but found type: " << typeName(elem.type()) << ".", elem.type() == BSONType::Object); const BSONObj bucketObj = elem.embeddedObject(); BSONObjBuilder groupObjBuilder; BSONObjBuilder switchObjBuilder; VariablesIdGenerator idGenerator; VariablesParseState vps(&idGenerator); vector<Value> boundaryValues; BSONElement groupByField; Value defaultValue; bool outputFieldSpecified = false; for (auto&& argument : bucketObj) { const auto argName = argument.fieldNameStringData(); if ("groupBy" == argName) { groupByField = argument; const bool groupByIsExpressionInObject = groupByField.type() == BSONType::Object && groupByField.embeddedObject().firstElementFieldName()[0] == '$'; const bool groupByIsPrefixedPath = groupByField.type() == BSONType::String && groupByField.valueStringData()[0] == '$'; uassert(40202, str::stream() << "The $bucket 'groupBy' field must be defined as a $-prefixed " "path or an expression, but found: " << groupByField.toString(false, false) << ".", groupByIsExpressionInObject || groupByIsPrefixedPath); } else if ("boundaries" == argName) { uassert( 40200, str::stream() << "The $bucket 'boundaries' field must be an array, but found type: " << typeName(argument.type()) << ".", argument.type() == BSONType::Array); for (auto&& boundaryElem : argument.embeddedObject()) { auto exprConst = getExpressionConstant(boundaryElem, vps); uassert(40191, str::stream() << "The $bucket 'boundaries' field must be an array of " "constant values, but found value: " << boundaryElem.toString(false, false) << ".", exprConst); boundaryValues.push_back(exprConst->getValue()); } uassert(40192, str::stream() << "The $bucket 'boundaries' field must have at least 2 values, but found " << boundaryValues.size() << " value(s).", boundaryValues.size() >= 2); // Make sure that the boundaries are unique, sorted in ascending order, and have the // same canonical type. for (size_t i = 1; i < boundaryValues.size(); ++i) { Value lower = boundaryValues[i - 1]; Value upper = boundaryValues[i]; int lowerCanonicalType = canonicalizeBSONType(lower.getType()); int upperCanonicalType = canonicalizeBSONType(upper.getType()); uassert(40193, str::stream() << "All values in the the 'boundaries' option to $bucket " "must have the same type. Found conflicting types " << typeName(lower.getType()) << " and " << typeName(upper.getType()) << ".", lowerCanonicalType == upperCanonicalType); uassert(40194, str::stream() << "The 'boundaries' option to $bucket must be sorted, but elements " << i - 1 << " and " << i << " are not in ascending order (" << lower.toString() << " is not less than " << upper.toString() << ").", pExpCtx->getValueComparator().evaluate(lower < upper)); } } else if ("default" == argName) { // If there is a default, make sure that it parses to a constant expression then add // default to switch. auto exprConst = getExpressionConstant(argument, vps); uassert(40195, str::stream() << "The $bucket 'default' field must be a constant expression, but found: " << argument.toString(false, false) << ".", exprConst); defaultValue = exprConst->getValue(); defaultValue.addToBsonObj(&switchObjBuilder, "default"); } else if ("output" == argName) { outputFieldSpecified = true; uassert( 40196, str::stream() << "The $bucket 'output' field must be an object, but found type: " << typeName(argument.type()) << ".", argument.type() == BSONType::Object); for (auto&& outputElem : argument.embeddedObject()) { groupObjBuilder.append(outputElem); } } else { uasserted(40197, str::stream() << "Unrecognized option to $bucket: " << argName << "."); } } const bool isMissingRequiredField = groupByField.eoo() || boundaryValues.empty(); uassert(40198, "$bucket requires 'groupBy' and 'boundaries' to be specified.", !isMissingRequiredField); Value lowerValue = boundaryValues.front(); Value upperValue = boundaryValues.back(); if (canonicalizeBSONType(defaultValue.getType()) == canonicalizeBSONType(lowerValue.getType())) { // If the default has the same canonical type as the bucket's boundaries, then make sure the // default is less than the lowest boundary or greater than or equal to the highest // boundary. const auto& valueCmp = pExpCtx->getValueComparator(); const bool hasValidDefault = valueCmp.evaluate(defaultValue < lowerValue) || valueCmp.evaluate(defaultValue >= upperValue); uassert(40199, "The $bucket 'default' field must be less than the lowest boundary or greater than " "or equal to the highest boundary.", hasValidDefault); } // Make the branches for the $switch expression. BSONArrayBuilder branchesBuilder; for (size_t i = 1; i < boundaryValues.size(); ++i) { Value lower = boundaryValues[i - 1]; Value upper = boundaryValues[i]; BSONObj caseExpr = BSON("$and" << BSON_ARRAY(BSON("$gte" << BSON_ARRAY(groupByField << lower)) << BSON("$lt" << BSON_ARRAY(groupByField << upper)))); branchesBuilder.append(BSON("case" << caseExpr << "then" << lower)); } // Add the $switch expression to the group BSON object. switchObjBuilder.append("branches", branchesBuilder.arr()); groupObjBuilder.append("_id", BSON("$switch" << switchObjBuilder.obj())); // If no output is specified, add a count field by default. if (!outputFieldSpecified) { groupObjBuilder.append("count", BSON("$sum" << 1)); } BSONObj groupObj = BSON("$group" << groupObjBuilder.obj()); BSONObj sortObj = BSON("$sort" << BSON("_id" << 1)); auto groupSource = DocumentSourceGroup::createFromBson(groupObj.firstElement(), pExpCtx); auto sortSource = DocumentSourceSort::createFromBson(sortObj.firstElement(), pExpCtx); return {groupSource, sortSource}; }
boost::intrusive_ptr<DocumentSource> DocumentSourceMergeCursors::createFromBson( BSONElement elem, const boost::intrusive_ptr<ExpressionContext>& expCtx) { if (elem.type() == BSONType::Object) { // This is the modern serialization format. We de-serialize using the IDL. auto ownedObj = elem.embeddedObject().getOwned(); auto armParams = AsyncResultsMergerParams::parse(IDLParserErrorContext(kStageName), ownedObj); return new DocumentSourceMergeCursors( Grid::get(expCtx->opCtx)->getExecutorPool()->getArbitraryExecutor(), std::move(armParams), expCtx, std::move(ownedObj)); } // This is the old serialization format which can still be generated by mongos processes // older than 4.0. // TODO SERVER-34009 Remove support for this format. uassert(17026, "$mergeCursors stage expected either an array or an object as argument", elem.type() == BSONType::Array); const auto serializedRemotes = elem.Array(); uassert(50729, "$mergeCursors stage expected array with at least one entry", serializedRemotes.size() > 0); boost::optional<NamespaceString> nss; std::vector<RemoteCursor> remotes; for (auto&& cursor : serializedRemotes) { BSONElement nsElem; BSONElement hostElem; BSONElement idElem; uassert(17027, "$mergeCursors stage requires each cursor in array to be an object", cursor.type() == BSONType::Object); for (auto&& cursorElem : cursor.Obj()) { const auto fieldName = cursorElem.fieldNameStringData(); if (fieldName == "ns"_sd) { nsElem = cursorElem; } else if (fieldName == "host"_sd) { hostElem = cursorElem; } else if (fieldName == "id"_sd) { idElem = cursorElem; } else { uasserted(50730, str::stream() << "Unrecognized option " << fieldName << " within cursor provided to $mergeCursors: " << cursor); } } uassert( 50731, "$mergeCursors stage requires \'ns\' field with type string for each cursor in array", nsElem.type() == BSONType::String); // We require each cursor to have the same namespace. This isn't a fundamental limit of the // system, but needs to be true due to the implementation of AsyncResultsMerger, which // tracks one namespace for all cursors. uassert(50720, "$mergeCursors requires each cursor to have the same namespace", !nss || nss->ns() == nsElem.valueStringData()); nss = NamespaceString(nsElem.String()); uassert( 50721, "$mergeCursors stage requires \'host\' field with type string for each cursor in array", hostElem.type() == BSONType::String); auto host = uassertStatusOK(HostAndPort::parse(hostElem.valueStringData())); uassert(50722, "$mergeCursors stage requires \'id\' field with type long for each cursor in array", idElem.type() == BSONType::NumberLong); auto cursorId = idElem.Long(); // We are assuming that none of the cursors have been iterated at all, and so will not have // any data in the initial batch. // TODO SERVER-33323 We use a fake shard id because the AsyncResultsMerger won't use it for // anything, and finding the real one is non-trivial. RemoteCursor remoteCursor; remoteCursor.setShardId(ShardId("fakeShardIdForMergeCursors")); remoteCursor.setHostAndPort(std::move(host)); std::vector<BSONObj> emptyBatch; remoteCursor.setCursorResponse(CursorResponse{*nss, cursorId, emptyBatch}); remotes.push_back(std::move(remoteCursor)); } invariant(nss); // We know there is at least one cursor in 'serializedRemotes', and we require // each cursor to have a 'ns' field. AsyncResultsMergerParams params; params.setRemotes(std::move(remotes)); params.setNss(*nss); return new DocumentSourceMergeCursors( Grid::get(expCtx->opCtx)->getExecutorPool()->getArbitraryExecutor(), std::move(params), expCtx, elem.embeddedObject().getOwned()); }