/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Server Side Public License for more details. * * You should have received a copy of the Server Side Public License * along with this program. If not, see * . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the Server Side Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #pragma once #include #include #include "mongo/db/catalog/collection_options.h" #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" #include "mongo/db/query/tailable_mode.h" namespace mongo { class QueryMessage; class Status; template class StatusWith; /** * Parses the QueryMessage or find command received from the user and makes the various fields * more easily accessible. */ class QueryRequest { public: static const char kFindCommandName[]; static const char kShardVersionField[]; explicit QueryRequest(NamespaceStringOrUUID nss); /** * Returns a non-OK status if any property of the QR has a bad value (e.g. a negative skip * value) or if there is a bad combination of options (e.g. awaitData is illegal without * tailable). */ Status validate() const; /** * Parses a find command object, 'cmdObj'. Caller must indicate whether or not this lite * parsed query is an explained query or not via 'isExplain'. Accepts a NSS with which * to initialize the QueryRequest if there is no UUID in cmdObj. * * Returns a heap allocated QueryRequest on success or an error if 'cmdObj' is not well * formed. */ static StatusWith> makeFromFindCommand(NamespaceString nss, const BSONObj& cmdObj, bool isExplain); /** * If _uuid exists for this QueryRequest, use it to update the value of _nss via the * UUIDCatalog associated with opCtx. This should only be called when we hold a DBLock * on the database to which _uuid belongs, if the _uuid is present in the UUIDCatalog. */ void refreshNSS(OperationContext* opCtx); /** * Converts this QR into a find command. * The withUuid variants make a UUID-based find command instead of a namespace-based ones. */ BSONObj asFindCommand() const; BSONObj asFindCommandWithUuid() const; void asFindCommand(BSONObjBuilder* cmdBuilder) const; void asFindCommandWithUuid(BSONObjBuilder* cmdBuilder) const; /** * Converts this QR into an aggregation using $match. If this QR has options that cannot be * satisfied by aggregation, a non-OK status is returned and 'cmdBuilder' is not modified. */ StatusWith asAggregationCommand() const; /** * Parses maxTimeMS from the BSONElement containing its value. */ static StatusWith parseMaxTimeMS(BSONElement maxTimeMSElt); /** * Helper function to identify text search sort key * Example: {a: {$meta: "textScore"}} */ static bool isTextScoreMeta(BSONElement elt); /** * Helper function to validate a sort object. * Returns true if each element satisfies one of: * 1. a number with value 1 * 2. a number with value -1 * 3. isTextScoreMeta */ static bool isValidSortOrder(const BSONObj& sortObj); // Read preference is attached to commands in "wrapped" form, e.g. // { $query: { : ... } , : { ... } } // // However, mongos internally "unwraps" the read preference and adds it as a parameter to the // command, e.g. // { : ... , : { : { ... } } } static const std::string kWrappedReadPrefField; static const std::string kUnwrappedReadPrefField; // Names of the maxTimeMS command and query option. // Char arrays because they are used in static initialization. static const char cmdOptionMaxTimeMS[]; static const char queryOptionMaxTimeMS[]; // Names of the $meta projection values. static const std::string metaGeoNearDistance; static const std::string metaGeoNearPoint; static const std::string metaIndexKey; static const std::string metaRecordId; static const std::string metaSortKey; static const std::string metaTextScore; const NamespaceString& nss() const { invariant(!_nss.isEmpty()); return _nss; } const BSONObj& getFilter() const { return _filter; } void setFilter(BSONObj filter) { _filter = filter.getOwned(); } const BSONObj& getProj() const { return _proj; } void setProj(BSONObj proj) { _proj = proj.getOwned(); } const BSONObj& getSort() const { return _sort; } void setSort(BSONObj sort) { _sort = sort.getOwned(); } const BSONObj& getHint() const { return _hint; } void setHint(BSONObj hint) { _hint = hint.getOwned(); } const BSONObj& getReadConcern() const { return _readConcern; } void setReadConcern(BSONObj readConcern) { _readConcern = readConcern.getOwned(); } const BSONObj& getCollation() const { return _collation; } void setCollation(BSONObj collation) { _collation = collation.getOwned(); } static const long long kDefaultBatchSize; boost::optional getSkip() const { return _skip; } void setSkip(boost::optional skip) { _skip = skip; } boost::optional getLimit() const { return _limit; } void setLimit(boost::optional limit) { _limit = limit; } boost::optional getBatchSize() const { return _batchSize; } void setBatchSize(boost::optional batchSize) { _batchSize = batchSize; } boost::optional getNToReturn() const { return _ntoreturn; } void setNToReturn(boost::optional ntoreturn) { _ntoreturn = ntoreturn; } /** * Returns batchSize or ntoreturn value if either is set. If neither is set, * returns boost::none. */ boost::optional getEffectiveBatchSize() const; bool wantMore() const { return _wantMore; } void setWantMore(bool wantMore) { _wantMore = wantMore; } bool isExplain() const { return _explain; } void setExplain(bool explain) { _explain = explain; } const std::string& getComment() const { return _comment; } void setComment(const std::string& comment) { _comment = comment; } const BSONObj& getUnwrappedReadPref() const { return _unwrappedReadPref; } void setUnwrappedReadPref(BSONObj unwrappedReadPref) { _unwrappedReadPref = unwrappedReadPref.getOwned(); } int getMaxTimeMS() const { return _maxTimeMS; } void setMaxTimeMS(int maxTimeMS) { _maxTimeMS = maxTimeMS; } const BSONObj& getMin() const { return _min; } void setMin(BSONObj min) { _min = min.getOwned(); } const BSONObj& getMax() const { return _max; } void setMax(BSONObj max) { _max = max.getOwned(); } bool returnKey() const { return _returnKey; } void setReturnKey(bool returnKey) { _returnKey = returnKey; } bool showRecordId() const { return _showRecordId; } void setShowRecordId(bool showRecordId) { _showRecordId = showRecordId; } bool hasReadPref() const { return _hasReadPref; } void setHasReadPref(bool hasReadPref) { _hasReadPref = hasReadPref; } bool isTailable() const { return _tailableMode == TailableModeEnum::kTailable || _tailableMode == TailableModeEnum::kTailableAndAwaitData; } bool isTailableAndAwaitData() const { return _tailableMode == TailableModeEnum::kTailableAndAwaitData; } void setTailableMode(TailableModeEnum tailableMode) { _tailableMode = tailableMode; } TailableModeEnum getTailableMode() const { return _tailableMode; } bool isSlaveOk() const { return _slaveOk; } void setSlaveOk(bool slaveOk) { _slaveOk = slaveOk; } bool isOplogReplay() const { return _oplogReplay; } void setOplogReplay(bool oplogReplay) { _oplogReplay = oplogReplay; } bool isNoCursorTimeout() const { return _noCursorTimeout; } void setNoCursorTimeout(bool noCursorTimeout) { _noCursorTimeout = noCursorTimeout; } bool isExhaust() const { return _exhaust; } void setExhaust(bool exhaust) { _exhaust = exhaust; } bool isAllowPartialResults() const { return _allowPartialResults; } void setAllowPartialResults(bool allowPartialResults) { _allowPartialResults = allowPartialResults; } boost::optional getReplicationTerm() const { return _replicationTerm; } void setReplicationTerm(boost::optional replicationTerm) { _replicationTerm = replicationTerm; } bool isReadOnce() const { return _readOnce; } void setReadOnce(bool readOnce) { _readOnce = readOnce; } void setAllowSpeculativeMajorityRead(bool allowSpeculativeMajorityRead) { _allowSpeculativeMajorityRead = allowSpeculativeMajorityRead; } bool allowSpeculativeMajorityRead() const { return _allowSpeculativeMajorityRead; } boost::optional getReadAtClusterTime() const { return _internalReadAtClusterTime; } /** * Return options as a bit vector. */ int getOptions() const; // // Old parsing code: SOON TO BE DEPRECATED. // /** * Parse the provided QueryMessage and return a heap constructed QueryRequest, which * represents it or an error. */ static StatusWith> fromLegacyQueryMessage(const QueryMessage& qm); /** * Parse the provided legacy query object and parameters to construct a QueryRequest. */ static StatusWith> fromLegacyQuery(NamespaceStringOrUUID nsOrUuid, const BSONObj& queryObj, const BSONObj& proj, int ntoskip, int ntoreturn, int queryOptions); private: static StatusWith> parseFromFindCommand( std::unique_ptr qr, const BSONObj& cmdObj, bool isExplain); Status init(int ntoskip, int ntoreturn, int queryOptions, const BSONObj& queryObj, const BSONObj& proj, bool fromQueryMessage); Status initFullQuery(const BSONObj& top); /** * Updates the projection object with a $meta projection for the returnKey option. */ void addReturnKeyMetaProj(); /** * Updates the projection object with a $meta projection for the showRecordId option. */ void addShowRecordIdMetaProj(); /** * Initializes options based on the value of the 'options' bit vector. * * This contains flags such as tailable, exhaust, and noCursorTimeout. */ void initFromInt(int options); /** * Add the meta projection to this object if needed. */ void addMetaProjection(); /** * Common code for UUID and namespace-based find commands. */ void asFindCommandInternal(BSONObjBuilder* cmdBuilder) const; NamespaceString _nss; OptionalCollectionUUID _uuid; BSONObj _filter; BSONObj _proj; BSONObj _sort; // The hint provided, if any. If the hint was by index key pattern, the value of '_hint' is // the key pattern hinted. If the hint was by index name, the value of '_hint' is // {$hint: }, where is the index name hinted. BSONObj _hint; // The read concern is parsed elsewhere. BSONObj _readConcern; // The collation is parsed elsewhere. BSONObj _collation; // The unwrapped readPreference object, if one was given to us by the mongos command processor. // This object will be empty when no readPreference is specified or if the request does not // originate from mongos. BSONObj _unwrappedReadPref; bool _wantMore = true; // Must be either unset or positive. Negative skip is illegal and a skip of zero received from // the client is interpreted as the absence of a skip value. boost::optional _skip; // Must be either unset or positive. Negative limit is illegal and a limit value of zero // received from the client is interpreted as the absence of a limit value. boost::optional _limit; // Must be either unset or non-negative. Negative batchSize is illegal but batchSize of 0 is // allowed. boost::optional _batchSize; // Set only when parsed from an OP_QUERY find message. The value is computed by driver or shell // and is set to be a min of batchSize and limit provided by user. QR can have set either // ntoreturn or batchSize / limit. boost::optional _ntoreturn; bool _explain = false; std::string _comment; // A user-specified maxTimeMS limit, or a value of '0' if not specified. int _maxTimeMS = 0; BSONObj _min; BSONObj _max; bool _returnKey = false; bool _showRecordId = false; bool _hasReadPref = false; // Options that can be specified in the OP_QUERY 'flags' header. TailableModeEnum _tailableMode = TailableModeEnum::kNormal; bool _slaveOk = false; bool _oplogReplay = false; bool _noCursorTimeout = false; bool _exhaust = false; bool _allowPartialResults = false; bool _readOnce = false; bool _allowSpeculativeMajorityRead = false; boost::optional _replicationTerm; // The Timestamp that RecoveryUnit::setTimestampReadSource() should be called with. The optional // should only ever be engaged when testing commands are enabled. boost::optional _internalReadAtClusterTime; }; } // namespace mongo