Example #1
0
// 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};
}
Example #4
0
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);
    }
}
Example #6
0
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;
    }
}
Example #8
0
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();
}
Example #9
0
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();
}
Example #10
0
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();
}
Example #12
0
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));
}
Example #13
0
// 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();
}
Example #14
0
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();
}
Example #15
0
    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;
}
Example #17
0
    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();
    }
Example #18
0
// 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;
}
Example #19
0
    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);
    }
}
Example #22
0
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());
}