1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #pragma once
32 
33 #include <boost/optional.hpp>
34 #include <string>
35 
36 #include "mongo/db/catalog/collection_options.h"
37 #include "mongo/db/jsobj.h"
38 #include "mongo/db/namespace_string.h"
39 #include "mongo/db/operation_context.h"
40 #include "mongo/db/query/tailable_mode.h"
41 
42 namespace mongo {
43 
44 class QueryMessage;
45 class Status;
46 template <typename T>
47 class StatusWith;
48 
49 /**
50  * Parses the QueryMessage or find command received from the user and makes the various fields
51  * more easily accessible.
52  */
53 class QueryRequest {
54 public:
55     static const char kFindCommandName[];
56     static const char kShardVersionField[];
57 
58     QueryRequest(NamespaceString nss);
59 
60     QueryRequest(CollectionUUID uuid);
61 
62     /**
63      * Returns a non-OK status if any property of the QR has a bad value (e.g. a negative skip
64      * value) or if there is a bad combination of options (e.g. awaitData is illegal without
65      * tailable).
66      */
67     Status validate() const;
68 
69     /**
70      * Parses a find command object, 'cmdObj'. Caller must indicate whether or not this lite
71      * parsed query is an explained query or not via 'isExplain'. Accepts a NSS with which
72      * to initialize the QueryRequest if there is no UUID in cmdObj.
73      *
74      * Returns a heap allocated QueryRequest on success or an error if 'cmdObj' is not well
75      * formed.
76      */
77     static StatusWith<std::unique_ptr<QueryRequest>> makeFromFindCommand(NamespaceString nss,
78                                                                          const BSONObj& cmdObj,
79                                                                          bool isExplain);
80 
81     /**
82      * If _uuid exists for this QueryRequest, use it to update the value of _nss via the
83      * UUIDCatalog associated with opCtx. This should only be called when we hold a DBLock
84      * on the database to which _uuid belongs, if the _uuid is present in the UUIDCatalog.
85      */
86     void refreshNSS(OperationContext* opCtx);
87 
88     /**
89      * Converts this QR into a find command.
90      */
91     BSONObj asFindCommand() const;
92     void asFindCommand(BSONObjBuilder* cmdBuilder) const;
93 
94     /**
95      * Converts this QR into an aggregation using $match. If this QR has options that cannot be
96      * satisfied by aggregation, a non-OK status is returned and 'cmdBuilder' is not modified.
97      */
98     StatusWith<BSONObj> asAggregationCommand() const;
99 
100     /**
101      * Parses maxTimeMS from the BSONElement containing its value.
102      */
103     static StatusWith<int> parseMaxTimeMS(BSONElement maxTimeMSElt);
104 
105     /**
106      * Helper function to identify text search sort key
107      * Example: {a: {$meta: "textScore"}}
108      */
109     static bool isTextScoreMeta(BSONElement elt);
110 
111     /**
112      * Helper function to validate a sort object.
113      * Returns true if each element satisfies one of:
114      * 1. a number with value 1
115      * 2. a number with value -1
116      * 3. isTextScoreMeta
117      */
118     static bool isValidSortOrder(const BSONObj& sortObj);
119 
120     /**
121      * Returns true if the query described by "query" should execute
122      * at an elevated level of isolation (i.e., $isolated was specified).
123      */
124     static bool isQueryIsolated(const BSONObj& query);
125 
126     // Read preference is attached to commands in "wrapped" form, e.g.
127     //   { $query: { <cmd>: ... } , <kWrappedReadPrefField>: { ... } }
128     //
129     // However, mongos internally "unwraps" the read preference and adds it as a parameter to the
130     // command, e.g.
131     //  { <cmd>: ... , <kUnwrappedReadPrefField>: { <kWrappedReadPrefField>: { ... } } }
132     static const std::string kWrappedReadPrefField;
133     static const std::string kUnwrappedReadPrefField;
134 
135     // Names of the maxTimeMS command and query option.
136     // Char arrays because they are used in static initialization.
137     static const char cmdOptionMaxTimeMS[];
138     static const char queryOptionMaxTimeMS[];
139 
140     // Names of the $meta projection values.
141     static const std::string metaGeoNearDistance;
142     static const std::string metaGeoNearPoint;
143     static const std::string metaIndexKey;
144     static const std::string metaRecordId;
145     static const std::string metaSortKey;
146     static const std::string metaTextScore;
147 
nss()148     const NamespaceString& nss() const {
149         invariant(!_nss.isEmpty());
150         return _nss;
151     }
152 
getFilter()153     const BSONObj& getFilter() const {
154         return _filter;
155     }
156 
setFilter(BSONObj filter)157     void setFilter(BSONObj filter) {
158         _filter = filter.getOwned();
159     }
160 
getProj()161     const BSONObj& getProj() const {
162         return _proj;
163     }
164 
setProj(BSONObj proj)165     void setProj(BSONObj proj) {
166         _proj = proj.getOwned();
167     }
168 
getSort()169     const BSONObj& getSort() const {
170         return _sort;
171     }
172 
setSort(BSONObj sort)173     void setSort(BSONObj sort) {
174         _sort = sort.getOwned();
175     }
176 
getHint()177     const BSONObj& getHint() const {
178         return _hint;
179     }
180 
setHint(BSONObj hint)181     void setHint(BSONObj hint) {
182         _hint = hint.getOwned();
183     }
184 
getReadConcern()185     const BSONObj& getReadConcern() const {
186         return _readConcern;
187     }
188 
setReadConcern(BSONObj readConcern)189     void setReadConcern(BSONObj readConcern) {
190         _readConcern = readConcern.getOwned();
191     }
192 
getCollation()193     const BSONObj& getCollation() const {
194         return _collation;
195     }
196 
setCollation(BSONObj collation)197     void setCollation(BSONObj collation) {
198         _collation = collation.getOwned();
199     }
200 
201     static const long long kDefaultBatchSize;
202 
getSkip()203     boost::optional<long long> getSkip() const {
204         return _skip;
205     }
206 
setSkip(boost::optional<long long> skip)207     void setSkip(boost::optional<long long> skip) {
208         _skip = skip;
209     }
210 
getLimit()211     boost::optional<long long> getLimit() const {
212         return _limit;
213     }
214 
setLimit(boost::optional<long long> limit)215     void setLimit(boost::optional<long long> limit) {
216         _limit = limit;
217     }
218 
getBatchSize()219     boost::optional<long long> getBatchSize() const {
220         return _batchSize;
221     }
222 
setBatchSize(boost::optional<long long> batchSize)223     void setBatchSize(boost::optional<long long> batchSize) {
224         _batchSize = batchSize;
225     }
226 
getNToReturn()227     boost::optional<long long> getNToReturn() const {
228         return _ntoreturn;
229     }
230 
setNToReturn(boost::optional<long long> ntoreturn)231     void setNToReturn(boost::optional<long long> ntoreturn) {
232         _ntoreturn = ntoreturn;
233     }
234 
235     /**
236      * Returns batchSize or ntoreturn value if either is set. If neither is set,
237      * returns boost::none.
238      */
239     boost::optional<long long> getEffectiveBatchSize() const;
240 
wantMore()241     bool wantMore() const {
242         return _wantMore;
243     }
244 
setWantMore(bool wantMore)245     void setWantMore(bool wantMore) {
246         _wantMore = wantMore;
247     }
248 
isExplain()249     bool isExplain() const {
250         return _explain;
251     }
252 
setExplain(bool explain)253     void setExplain(bool explain) {
254         _explain = explain;
255     }
256 
getComment()257     const std::string& getComment() const {
258         return _comment;
259     }
260 
setComment(const std::string & comment)261     void setComment(const std::string& comment) {
262         _comment = comment;
263     }
264 
getUnwrappedReadPref()265     const BSONObj& getUnwrappedReadPref() const {
266         return _unwrappedReadPref;
267     }
268 
setUnwrappedReadPref(BSONObj unwrappedReadPref)269     void setUnwrappedReadPref(BSONObj unwrappedReadPref) {
270         _unwrappedReadPref = unwrappedReadPref.getOwned();
271     }
272 
getMaxScan()273     int getMaxScan() const {
274         return _maxScan;
275     }
276 
setMaxScan(int maxScan)277     void setMaxScan(int maxScan) {
278         _maxScan = maxScan;
279     }
280 
getMaxTimeMS()281     int getMaxTimeMS() const {
282         return _maxTimeMS;
283     }
284 
setMaxTimeMS(int maxTimeMS)285     void setMaxTimeMS(int maxTimeMS) {
286         _maxTimeMS = maxTimeMS;
287     }
288 
getMin()289     const BSONObj& getMin() const {
290         return _min;
291     }
292 
setMin(BSONObj min)293     void setMin(BSONObj min) {
294         _min = min.getOwned();
295     }
296 
getMax()297     const BSONObj& getMax() const {
298         return _max;
299     }
300 
setMax(BSONObj max)301     void setMax(BSONObj max) {
302         _max = max.getOwned();
303     }
304 
returnKey()305     bool returnKey() const {
306         return _returnKey;
307     }
308 
setReturnKey(bool returnKey)309     void setReturnKey(bool returnKey) {
310         _returnKey = returnKey;
311     }
312 
showRecordId()313     bool showRecordId() const {
314         return _showRecordId;
315     }
316 
setShowRecordId(bool showRecordId)317     void setShowRecordId(bool showRecordId) {
318         _showRecordId = showRecordId;
319     }
320 
isSnapshot()321     bool isSnapshot() const {
322         return _snapshot;
323     }
324 
setSnapshot(bool snapshot)325     void setSnapshot(bool snapshot) {
326         _snapshot = snapshot;
327     }
328 
hasReadPref()329     bool hasReadPref() const {
330         return _hasReadPref;
331     }
332 
setHasReadPref(bool hasReadPref)333     void setHasReadPref(bool hasReadPref) {
334         _hasReadPref = hasReadPref;
335     }
336 
isTailable()337     bool isTailable() const {
338         return _tailableMode == TailableMode::kTailable ||
339             _tailableMode == TailableMode::kTailableAndAwaitData;
340     }
341 
isTailableAndAwaitData()342     bool isTailableAndAwaitData() const {
343         return _tailableMode == TailableMode::kTailableAndAwaitData;
344     }
345 
setTailableMode(TailableMode tailableMode)346     void setTailableMode(TailableMode tailableMode) {
347         _tailableMode = tailableMode;
348     }
349 
getTailableMode()350     TailableMode getTailableMode() const {
351         return _tailableMode;
352     }
353 
isSlaveOk()354     bool isSlaveOk() const {
355         return _slaveOk;
356     }
357 
setSlaveOk(bool slaveOk)358     void setSlaveOk(bool slaveOk) {
359         _slaveOk = slaveOk;
360     }
361 
isOplogReplay()362     bool isOplogReplay() const {
363         return _oplogReplay;
364     }
365 
setOplogReplay(bool oplogReplay)366     void setOplogReplay(bool oplogReplay) {
367         _oplogReplay = oplogReplay;
368     }
369 
isNoCursorTimeout()370     bool isNoCursorTimeout() const {
371         return _noCursorTimeout;
372     }
373 
setNoCursorTimeout(bool noCursorTimeout)374     void setNoCursorTimeout(bool noCursorTimeout) {
375         _noCursorTimeout = noCursorTimeout;
376     }
377 
isExhaust()378     bool isExhaust() const {
379         return _exhaust;
380     }
381 
setExhaust(bool exhaust)382     void setExhaust(bool exhaust) {
383         _exhaust = exhaust;
384     }
385 
isAllowPartialResults()386     bool isAllowPartialResults() const {
387         return _allowPartialResults;
388     }
389 
setAllowPartialResults(bool allowPartialResults)390     void setAllowPartialResults(bool allowPartialResults) {
391         _allowPartialResults = allowPartialResults;
392     }
393 
getReplicationTerm()394     boost::optional<long long> getReplicationTerm() const {
395         return _replicationTerm;
396     }
397 
setReplicationTerm(boost::optional<long long> replicationTerm)398     void setReplicationTerm(boost::optional<long long> replicationTerm) {
399         _replicationTerm = replicationTerm;
400     }
401 
402     /**
403      * Return options as a bit vector.
404      */
405     int getOptions() const;
406 
407     //
408     // Old parsing code: SOON TO BE DEPRECATED.
409     //
410 
411     /**
412      * Parse the provided QueryMessage and return a heap constructed QueryRequest, which
413      * represents it or an error.
414      */
415     static StatusWith<std::unique_ptr<QueryRequest>> fromLegacyQueryMessage(const QueryMessage& qm);
416 
417     /**
418      * Parse the provided legacy query object and parameters to construct a QueryRequest.
419      */
420     static StatusWith<std::unique_ptr<QueryRequest>> fromLegacyQuery(NamespaceString nss,
421                                                                      const BSONObj& queryObj,
422                                                                      const BSONObj& proj,
423                                                                      int ntoskip,
424                                                                      int ntoreturn,
425                                                                      int queryOptions);
426 
427 private:
428     static StatusWith<std::unique_ptr<QueryRequest>> parseFromFindCommand(
429         std::unique_ptr<QueryRequest> qr, const BSONObj& cmdObj, bool isExplain);
430     Status init(int ntoskip,
431                 int ntoreturn,
432                 int queryOptions,
433                 const BSONObj& queryObj,
434                 const BSONObj& proj,
435                 bool fromQueryMessage);
436 
437     Status initFullQuery(const BSONObj& top);
438 
439     /**
440      * Updates the projection object with a $meta projection for the returnKey option.
441      */
442     void addReturnKeyMetaProj();
443 
444     /**
445      * Updates the projection object with a $meta projection for the showRecordId option.
446      */
447     void addShowRecordIdMetaProj();
448 
449     /**
450      * Initializes options based on the value of the 'options' bit vector.
451      *
452      * This contains flags such as tailable, exhaust, and noCursorTimeout.
453      */
454     void initFromInt(int options);
455 
456     /**
457      * Add the meta projection to this object if needed.
458      */
459     void addMetaProjection();
460 
461     NamespaceString _nss;
462     OptionalCollectionUUID _uuid;
463 
464     BSONObj _filter;
465     BSONObj _proj;
466     BSONObj _sort;
467     // The hint provided, if any.  If the hint was by index key pattern, the value of '_hint' is
468     // the key pattern hinted.  If the hint was by index name, the value of '_hint' is
469     // {$hint: <String>}, where <String> is the index name hinted.
470     BSONObj _hint;
471     // The read concern is parsed elsewhere.
472     BSONObj _readConcern;
473     // The collation is parsed elsewhere.
474     BSONObj _collation;
475 
476     // The unwrapped readPreference object, if one was given to us by the mongos command processor.
477     // This object will be empty when no readPreference is specified or if the request does not
478     // originate from mongos.
479     BSONObj _unwrappedReadPref;
480 
481     bool _wantMore = true;
482 
483     // Must be either unset or positive. Negative skip is illegal and a skip of zero received from
484     // the client is interpreted as the absence of a skip value.
485     boost::optional<long long> _skip;
486 
487     // Must be either unset or positive. Negative limit is illegal and a limit value of zero
488     // received from the client is interpreted as the absence of a limit value.
489     boost::optional<long long> _limit;
490 
491     // Must be either unset or non-negative. Negative batchSize is illegal but batchSize of 0 is
492     // allowed.
493     boost::optional<long long> _batchSize;
494 
495     // Set only when parsed from an OP_QUERY find message. The value is computed by driver or shell
496     // and is set to be a min of batchSize and limit provided by user. QR can have set either
497     // ntoreturn or batchSize / limit.
498     boost::optional<long long> _ntoreturn;
499 
500     bool _explain = false;
501 
502     std::string _comment;
503 
504     int _maxScan = 0;
505 
506     // A user-specified maxTimeMS limit, or a value of '0' if not specified.
507     int _maxTimeMS = 0;
508 
509     BSONObj _min;
510     BSONObj _max;
511 
512     bool _returnKey = false;
513     bool _showRecordId = false;
514     bool _snapshot = false;
515     bool _hasReadPref = false;
516 
517     // Options that can be specified in the OP_QUERY 'flags' header.
518     TailableMode _tailableMode = TailableMode::kNormal;
519     bool _slaveOk = false;
520     bool _oplogReplay = false;
521     bool _noCursorTimeout = false;
522     bool _exhaust = false;
523     bool _allowPartialResults = false;
524 
525     boost::optional<long long> _replicationTerm;
526 };
527 
528 }  // namespace mongo
529