Esempio n. 1
0
void replyToQuery(int queryResultFlags,
                  AbstractMessagingPort* p,
                  Message& requestMsg,
                  const void* data,
                  int size,
                  int nReturned,
                  int startingFrom,
                  long long cursorId) {
    OpQueryReplyBuilder reply;
    reply.bufBuilderForResults().appendBuf(data, size);
    reply.send(p, queryResultFlags, requestMsg, nReturned, startingFrom, cursorId);
}
Esempio n. 2
0
void replyToQuery(int queryResultFlags,
                  const transport::SessionHandle& session,
                  Message& requestMsg,
                  const void* data,
                  int size,
                  int nReturned,
                  int startingFrom,
                  long long cursorId) {
    OpQueryReplyBuilder reply;
    reply.bufBuilderForResults().appendBuf(data, size);
    reply.send(session, queryResultFlags, requestMsg, nReturned, startingFrom, cursorId);
}
Esempio n. 3
0
void Strategy::queryOp(OperationContext* txn, Request& request) {
    verify(!NamespaceString(request.getns()).isCommand());

    globalOpCounters.gotQuery();

    QueryMessage q(request.d());

    NamespaceString ns(q.ns);
    ClientBasic* client = txn->getClient();
    AuthorizationSession* authSession = AuthorizationSession::get(client);
    Status status = authSession->checkAuthForFind(ns, false);
    audit::logQueryAuthzCheck(client, ns, q.query, status.code());
    uassertStatusOK(status);

    LOG(3) << "query: " << q.ns << " " << q.query << " ntoreturn: " << q.ntoreturn
           << " options: " << q.queryOptions;

    if (q.ntoreturn == 1 && strstr(q.ns, ".$cmd"))
        throw UserException(8010, "something is wrong, shouldn't see a command here");

    if (q.queryOptions & QueryOption_Exhaust) {
        uasserted(18526,
                  string("the 'exhaust' query option is invalid for mongos queries: ") + q.ns +
                      " " + q.query.toString());
    }

    // Determine the default read preference mode based on the value of the slaveOk flag.
    ReadPreference readPreferenceOption = (q.queryOptions & QueryOption_SlaveOk)
        ? ReadPreference::SecondaryPreferred
        : ReadPreference::PrimaryOnly;
    ReadPreferenceSetting readPreference(readPreferenceOption, TagSet());

    BSONElement rpElem;
    auto readPrefExtractStatus = bsonExtractTypedField(
        q.query, LiteParsedQuery::kWrappedReadPrefField, mongo::Object, &rpElem);

    if (readPrefExtractStatus.isOK()) {
        auto parsedRps = ReadPreferenceSetting::fromBSON(rpElem.Obj());
        uassertStatusOK(parsedRps.getStatus());
        readPreference = parsedRps.getValue();
    } else if (readPrefExtractStatus != ErrorCodes::NoSuchKey) {
        uassertStatusOK(readPrefExtractStatus);
    }

    auto canonicalQuery = CanonicalQuery::canonicalize(q, ExtensionsCallbackNoop());
    uassertStatusOK(canonicalQuery.getStatus());

    // If the $explain flag was set, we must run the operation on the shards as an explain command
    // rather than a find command.
    if (canonicalQuery.getValue()->getParsed().isExplain()) {
        const LiteParsedQuery& lpq = canonicalQuery.getValue()->getParsed();
        BSONObj findCommand = lpq.asFindCommand();

        // We default to allPlansExecution verbosity.
        auto verbosity = ExplainCommon::EXEC_ALL_PLANS;

        const bool secondaryOk = (readPreference.pref != ReadPreference::PrimaryOnly);
        rpc::ServerSelectionMetadata metadata(secondaryOk, readPreference);

        BSONObjBuilder explainBuilder;
        uassertStatusOK(
            Strategy::explainFind(txn, findCommand, lpq, verbosity, metadata, &explainBuilder));

        BSONObj explainObj = explainBuilder.done();
        replyToQuery(0,  // query result flags
                     request.p(),
                     request.m(),
                     static_cast<const void*>(explainObj.objdata()),
                     explainObj.objsize(),
                     1,  // numResults
                     0,  // startingFrom
                     CursorId(0));
        return;
    }

    // Do the work to generate the first batch of results. This blocks waiting to get responses from
    // the shard(s).
    std::vector<BSONObj> batch;

    // 0 means the cursor is exhausted. Otherwise we assume that a cursor with the returned id can
    // be retrieved via the ClusterCursorManager.
    auto cursorId = ClusterFind::runQuery(txn, *canonicalQuery.getValue(), readPreference, &batch);
    uassertStatusOK(cursorId.getStatus());

    // Fill out the response buffer.
    int numResults = 0;
    OpQueryReplyBuilder reply;
    for (auto&& obj : batch) {
        obj.appendSelfToBufBuilder(reply.bufBuilderForResults());
        numResults++;
    }
    reply.send(request.p(),
               0,  // query result flags
               request.m(),
               numResults,
               0,  // startingFrom
               cursorId.getValue());
}
Esempio n. 4
0
void Strategy::clientCommandOp(OperationContext* txn, Request& request) {
    QueryMessage q(request.d());

    LOG(3) << "command: " << q.ns << " " << q.query << " ntoreturn: " << q.ntoreturn
           << " options: " << q.queryOptions;

    if (q.queryOptions & QueryOption_Exhaust) {
        uasserted(18527,
                  string("the 'exhaust' query option is invalid for mongos commands: ") + q.ns +
                      " " + q.query.toString());
    }

    NamespaceString nss(request.getns());
    // Regular queries are handled in strategy_shard.cpp
    verify(nss.isCommand() || nss.isSpecialCommand());

    if (handleSpecialNamespaces(txn, request, q))
        return;

    int loops = 5;
    bool cmChangeAttempted = false;

    while (true) {
        try {
            BSONObj cmdObj = q.query;
            {
                BSONElement e = cmdObj.firstElement();
                if (e.type() == Object &&
                    (e.fieldName()[0] == '$' ? str::equals("query", e.fieldName() + 1)
                                             : str::equals("query", e.fieldName()))) {
                    // Extract the embedded query object.

                    if (cmdObj.hasField(Query::ReadPrefField.name())) {
                        // The command has a read preference setting. We don't want
                        // to lose this information so we copy this to a new field
                        // called $queryOptions.$readPreference
                        BSONObjBuilder finalCmdObjBuilder;
                        finalCmdObjBuilder.appendElements(e.embeddedObject());

                        BSONObjBuilder queryOptionsBuilder(
                            finalCmdObjBuilder.subobjStart("$queryOptions"));
                        queryOptionsBuilder.append(cmdObj[Query::ReadPrefField.name()]);
                        queryOptionsBuilder.done();

                        cmdObj = finalCmdObjBuilder.obj();
                    } else {
                        cmdObj = e.embeddedObject();
                    }
                }
            }

            OpQueryReplyBuilder reply;
            {
                BSONObjBuilder builder(reply.bufBuilderForResults());
                Command::runAgainstRegistered(txn, q.ns, cmdObj, builder, q.queryOptions);
            }
            reply.sendCommandReply(request.p(), request.m());
            return;
        } catch (const StaleConfigException& e) {
            if (loops <= 0)
                throw e;

            loops--;
            log() << "retrying command: " << q.query;

            // For legacy reasons, ns may not actually be set in the exception :-(
            string staleNS = e.getns();
            if (staleNS.size() == 0)
                staleNS = q.ns;

            ShardConnection::checkMyConnectionVersions(txn, staleNS);
            if (loops < 4)
                versionManager.forceRemoteCheckShardVersionCB(txn, staleNS);
        } catch (const DBException& e) {
            if (e.getCode() == ErrorCodes::IncompatibleCatalogManager) {
                fassert(28791, !cmChangeAttempted);
                cmChangeAttempted = true;

                grid.forwardingCatalogManager()->waitForCatalogManagerChange(txn);
            } else {
                OpQueryReplyBuilder reply;
                {
                    BSONObjBuilder builder(reply.bufBuilderForResults());
                    Command::appendCommandStatus(builder, e.toStatus());
                }
                reply.sendCommandReply(request.p(), request.m());
                return;
            }
        }
    }
}
Esempio n. 5
0
void replyToQuery(int queryResultFlags, Message& response, const BSONObj& resultObj) {
    OpQueryReplyBuilder reply;
    resultObj.appendSelfToBufBuilder(reply.bufBuilderForResults());
    reply.putInMessage(&response, queryResultFlags, /*nReturned*/ 1);
}
Esempio n. 6
0
void Strategy::clientCommandOp(OperationContext* txn, Request& request) {
    const QueryMessage q(request.d());

    LOG(3) << "command: " << q.ns << " " << redact(q.query) << " ntoreturn: " << q.ntoreturn
           << " options: " << q.queryOptions;

    if (q.queryOptions & QueryOption_Exhaust) {
        uasserted(18527,
                  string("the 'exhaust' query option is invalid for mongos commands: ") + q.ns +
                      " " + q.query.toString());
    }

    const NamespaceString nss(request.getns());
    invariant(nss.isCommand() || nss.isSpecialCommand());

    if (handleSpecialNamespaces(txn, request, q))
        return;

    BSONObj cmdObj = q.query;

    {
        BSONElement e = cmdObj.firstElement();
        if (e.type() == Object && (e.fieldName()[0] == '$' ? str::equals("query", e.fieldName() + 1)
                                                           : str::equals("query", e.fieldName()))) {
            // Extract the embedded query object.
            if (cmdObj.hasField(Query::ReadPrefField.name())) {
                // The command has a read preference setting. We don't want to lose this information
                // so we copy this to a new field called $queryOptions.$readPreference
                BSONObjBuilder finalCmdObjBuilder;
                finalCmdObjBuilder.appendElements(e.embeddedObject());

                BSONObjBuilder queryOptionsBuilder(finalCmdObjBuilder.subobjStart("$queryOptions"));
                queryOptionsBuilder.append(cmdObj[Query::ReadPrefField.name()]);
                queryOptionsBuilder.done();

                cmdObj = finalCmdObjBuilder.obj();
            } else {
                cmdObj = e.embeddedObject();
            }
        }
    }

    // Handle command option maxTimeMS.
    uassert(ErrorCodes::InvalidOptions,
            "no such command option $maxTimeMs; use maxTimeMS instead",
            cmdObj[QueryRequest::queryOptionMaxTimeMS].eoo());

    const int maxTimeMS =
        uassertStatusOK(QueryRequest::parseMaxTimeMS(cmdObj[QueryRequest::cmdOptionMaxTimeMS]));
    if (maxTimeMS > 0) {
        txn->setDeadlineAfterNowBy(Milliseconds{maxTimeMS});
    }

    int loops = 5;

    while (true) {
        try {
            OpQueryReplyBuilder reply;
            {
                BSONObjBuilder builder(reply.bufBuilderForResults());
                runAgainstRegistered(txn, q.ns, cmdObj, builder, q.queryOptions);
            }
            reply.sendCommandReply(request.session(), request.m());
            return;
        } catch (const StaleConfigException& e) {
            if (loops <= 0)
                throw e;

            loops--;

            log() << "Retrying command " << redact(q.query) << causedBy(e);

            // For legacy reasons, ns may not actually be set in the exception :-(
            string staleNS = e.getns();
            if (staleNS.size() == 0)
                staleNS = q.ns;

            ShardConnection::checkMyConnectionVersions(txn, staleNS);
            if (loops < 4)
                versionManager.forceRemoteCheckShardVersionCB(txn, staleNS);
        } catch (const DBException& e) {
            OpQueryReplyBuilder reply;
            {
                BSONObjBuilder builder(reply.bufBuilderForResults());
                Command::appendCommandStatus(builder, e.toStatus());
            }
            reply.sendCommandReply(request.session(), request.m());
            return;
        }
    }
}