summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorBernard Gorman <bernard.gorman@gmail.com>2019-05-22 13:10:17 +0100
committerBernard Gorman <bernard.gorman@gmail.com>2019-05-24 15:27:25 +0100
commitec6f2325a99b6ddd13af6903eed12ced95e4bea8 (patch)
treef225a28dc123f6e1ca0f32b1fee4202cee051db5 /src/mongo
parent8d5727273585524a38d4a663b61403a263f7cf3d (diff)
downloadmongo-ec6f2325a99b6ddd13af6903eed12ced95e4bea8.tar.gz
SERVER-41238 Allow $$NOW and $$CLUSTER_TIME to be used in the find command
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/commands/find_cmd.cpp30
-rw-r--r--src/mongo/db/ops/delete_request.h4
-rw-r--r--src/mongo/db/ops/parsed_delete.cpp20
-rw-r--r--src/mongo/db/ops/parsed_delete.h3
-rw-r--r--src/mongo/db/ops/parsed_update.cpp8
-rw-r--r--src/mongo/db/ops/update_request.h4
-rw-r--r--src/mongo/db/pipeline/variables.cpp2
-rw-r--r--src/mongo/db/query/SConscript3
-rw-r--r--src/mongo/db/query/canonical_query.cpp2
-rw-r--r--src/mongo/db/query/find_and_modify_request.h4
-rw-r--r--src/mongo/db/query/query_request.cpp20
-rw-r--r--src/mongo/db/query/query_request.h12
-rw-r--r--src/mongo/db/query/query_request_test.cpp127
-rw-r--r--src/mongo/s/commands/cluster_find_cmd.cpp25
-rw-r--r--src/mongo/s/write_ops/batched_command_request.h4
15 files changed, 188 insertions, 80 deletions
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index 67f73d6e6ee..4d79ab118cd 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/db_raii.h"
#include "mongo/db/exec/working_set_common.h"
#include "mongo/db/matcher/extensions_callback_real.h"
+#include "mongo/db/pipeline/variables.h"
#include "mongo/db/query/cursor_response.h"
#include "mongo/db/query/explain.h"
#include "mongo/db/query/find.h"
@@ -62,6 +63,20 @@ namespace {
const auto kTermField = "term"_sd;
+// Parses the command object to a QueryRequest. If the client request did not specify any runtime
+// constants, make them available to the query here.
+std::unique_ptr<QueryRequest> parseCmdObjectToQueryRequest(OperationContext* opCtx,
+ NamespaceString nss,
+ BSONObj cmdObj,
+ bool isExplain) {
+ auto qr = uassertStatusOK(
+ QueryRequest::makeFromFindCommand(std::move(nss), std::move(cmdObj), isExplain));
+ if (!qr->getRuntimeConstants()) {
+ qr->setRuntimeConstants(Variables::generateRuntimeConstants(opCtx));
+ }
+ return qr;
+}
+
/**
* A command for running .find() queries.
*/
@@ -168,8 +183,7 @@ public:
// Parse the command BSON to a QueryRequest.
const bool isExplain = true;
- auto qr =
- uassertStatusOK(QueryRequest::makeFromFindCommand(nss, _request.body, isExplain));
+ auto qr = parseCmdObjectToQueryRequest(opCtx, nss, _request.body, isExplain);
// Finish the parsing step by using the QueryRequest to create a CanonicalQuery.
const ExtensionsCallbackReal extensionsCallback(opCtx, &nss);
@@ -239,13 +253,13 @@ public:
ServerReadConcernMetrics::get(opCtx)->recordReadConcern(
repl::ReadConcernArgs::get(opCtx));
- // Parse the command BSON to a QueryRequest.
+ // Parse the command BSON to a QueryRequest. Pass in the parsedNss in case _request.body
+ // does not have a UUID.
+ auto parsedNss =
+ NamespaceString{CommandHelpers::parseNsFromCommand(_dbName, _request.body)};
const bool isExplain = false;
- // Pass parseNs to makeFromFindCommand in case _request.body does not have a UUID.
- auto qr = uassertStatusOK(QueryRequest::makeFromFindCommand(
- NamespaceString(CommandHelpers::parseNsFromCommand(_dbName, _request.body)),
- _request.body,
- isExplain));
+ auto qr =
+ parseCmdObjectToQueryRequest(opCtx, std::move(parsedNss), _request.body, isExplain);
// Only allow speculative majority for internal commands that specify the correct flag.
uassert(ErrorCodes::ReadConcernMajorityNotEnabled,
diff --git a/src/mongo/db/ops/delete_request.h b/src/mongo/db/ops/delete_request.h
index 937297b95ae..a49e6eb37b9 100644
--- a/src/mongo/db/ops/delete_request.h
+++ b/src/mongo/db/ops/delete_request.h
@@ -61,8 +61,8 @@ public:
void setSort(const BSONObj& sort) {
_sort = sort;
}
- void setRuntimeConstants(const RuntimeConstants& runtimeConstants) {
- _runtimeConstants = runtimeConstants;
+ void setRuntimeConstants(RuntimeConstants runtimeConstants) {
+ _runtimeConstants = std::move(runtimeConstants);
}
void setCollation(const BSONObj& collation) {
_collation = collation;
diff --git a/src/mongo/db/ops/parsed_delete.cpp b/src/mongo/db/ops/parsed_delete.cpp
index 0ce2cbe557a..c0b757fe69e 100644
--- a/src/mongo/db/ops/parsed_delete.cpp
+++ b/src/mongo/db/ops/parsed_delete.cpp
@@ -39,7 +39,6 @@
#include "mongo/db/matcher/extensions_callback_real.h"
#include "mongo/db/ops/delete_request.h"
#include "mongo/db/query/canonical_query.h"
-#include "mongo/db/query/collation/collator_factory_interface.h"
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/query_planner_common.h"
#include "mongo/util/assert_util.h"
@@ -61,17 +60,6 @@ Status ParsedDelete::parseRequest() {
// DeleteStage would not return the deleted document.
invariant(_request->getProj().isEmpty() || _request->shouldReturnDeleted());
- // Parse the delete request's collation, if present. This will subsequently be used to
- // initialize an ExpressionContext for the query.
- if (!_request->getCollation().isEmpty()) {
- auto collator = CollatorFactoryInterface::get(_opCtx->getServiceContext())
- ->makeFromBSON(_request->getCollation());
- if (!collator.isOK()) {
- return collator.getStatus();
- }
- _collator = std::move(collator.getValue());
- }
-
if (CanonicalQuery::isSimpleIdQuery(_request->getQuery())) {
return Status::OK();
}
@@ -102,8 +90,12 @@ Status ParsedDelete::parseQueryToCQ() {
qr->setLimit(1);
}
- auto expCtx =
- make_intrusive<ExpressionContext>(_opCtx, _collator.get(), _request->getRuntimeConstants());
+ // If the delete request has runtime constants attached to it, pass them to the QueryRequest.
+ if (auto& runtimeConstants = _request->getRuntimeConstants()) {
+ qr->setRuntimeConstants(*runtimeConstants);
+ }
+
+ const boost::intrusive_ptr<ExpressionContext> expCtx;
auto statusWithCQ =
CanonicalQuery::canonicalize(_opCtx,
std::move(qr),
diff --git a/src/mongo/db/ops/parsed_delete.h b/src/mongo/db/ops/parsed_delete.h
index 724c4a48656..73f4bef19e4 100644
--- a/src/mongo/db/ops/parsed_delete.h
+++ b/src/mongo/db/ops/parsed_delete.h
@@ -107,9 +107,6 @@ private:
// Unowned pointer to the request object that this executor will process.
const DeleteRequest* const _request;
- // The collator for the parsed delete's expression context.
- std::unique_ptr<CollatorInterface> _collator;
-
// Parsed query object, or NULL if the query proves to be an id hack query.
std::unique_ptr<CanonicalQuery> _canonicalQuery;
};
diff --git a/src/mongo/db/ops/parsed_update.cpp b/src/mongo/db/ops/parsed_update.cpp
index 79e1d9ed692..67215128ce0 100644
--- a/src/mongo/db/ops/parsed_update.cpp
+++ b/src/mongo/db/ops/parsed_update.cpp
@@ -123,8 +123,12 @@ Status ParsedUpdate::parseQueryToCQ() {
allowedMatcherFeatures &= ~MatchExpressionParser::AllowedFeatures::kExpr;
}
- auto expCtx =
- make_intrusive<ExpressionContext>(_opCtx, _collator.get(), _request->getRuntimeConstants());
+ // If the update request has runtime constants attached to it, pass them to the QueryRequest.
+ if (auto& runtimeConstants = _request->getRuntimeConstants()) {
+ qr->setRuntimeConstants(*runtimeConstants);
+ }
+
+ boost::intrusive_ptr<ExpressionContext> expCtx;
auto statusWithCQ = CanonicalQuery::canonicalize(
_opCtx, std::move(qr), std::move(expCtx), _extensionsCallback, allowedMatcherFeatures);
if (statusWithCQ.isOK()) {
diff --git a/src/mongo/db/ops/update_request.h b/src/mongo/db/ops/update_request.h
index e0f99abf1d4..ee30cc6a827 100644
--- a/src/mongo/db/ops/update_request.h
+++ b/src/mongo/db/ops/update_request.h
@@ -119,8 +119,8 @@ public:
return _updateConstants;
}
- inline void setRuntimeConstants(const RuntimeConstants& runtimeConstants) {
- _runtimeConstants = runtimeConstants;
+ inline void setRuntimeConstants(RuntimeConstants runtimeConstants) {
+ _runtimeConstants = std::move(runtimeConstants);
}
inline const boost::optional<RuntimeConstants>& getRuntimeConstants() const {
diff --git a/src/mongo/db/pipeline/variables.cpp b/src/mongo/db/pipeline/variables.cpp
index 9e7e2c9256e..cf6b81e9605 100644
--- a/src/mongo/db/pipeline/variables.cpp
+++ b/src/mongo/db/pipeline/variables.cpp
@@ -151,7 +151,7 @@ Value Variables::getValue(Id id, const Document& root) const {
}
uasserted(51144,
- str::stream() << "Buildin variable '$$" << getBuiltinVariableName(id)
+ str::stream() << "Builtin variable '$$" << getBuiltinVariableName(id)
<< "' is not available");
MONGO_UNREACHABLE;
default:
diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript
index b520db9b29a..0ae841ab6de 100644
--- a/src/mongo/db/query/SConscript
+++ b/src/mongo/db/query/SConscript
@@ -209,7 +209,8 @@ env.Library(
LIBDEPS=[
"$BUILD_DIR/mongo/base",
"$BUILD_DIR/mongo/db/repl/read_concern_args",
- "$BUILD_DIR/mongo/db/catalog/collection_catalog"
+ "$BUILD_DIR/mongo/db/catalog/collection_catalog",
+ "$BUILD_DIR/mongo/db/pipeline/runtime_constants_idl"
],
)
diff --git a/src/mongo/db/query/canonical_query.cpp b/src/mongo/db/query/canonical_query.cpp
index 0be2f524798..ff77f314fa2 100644
--- a/src/mongo/db/query/canonical_query.cpp
+++ b/src/mongo/db/query/canonical_query.cpp
@@ -151,7 +151,7 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize(
// Make MatchExpression.
boost::intrusive_ptr<ExpressionContext> newExpCtx;
if (!expCtx.get()) {
- newExpCtx.reset(new ExpressionContext(opCtx, collator.get()));
+ newExpCtx.reset(new ExpressionContext(opCtx, collator.get(), qr->getRuntimeConstants()));
} else {
newExpCtx = expCtx;
invariant(CollatorInterface::collatorsMatch(collator.get(), expCtx->getCollator()));
diff --git a/src/mongo/db/query/find_and_modify_request.h b/src/mongo/db/query/find_and_modify_request.h
index 2ee73859d1b..a8b350e691f 100644
--- a/src/mongo/db/query/find_and_modify_request.h
+++ b/src/mongo/db/query/find_and_modify_request.h
@@ -167,8 +167,8 @@ public:
/**
* Sets any constant values which may be required by the query and/or update.
*/
- void setRuntimeConstants(const RuntimeConstants& runtimeConstants) {
- _runtimeConstants = runtimeConstants;
+ void setRuntimeConstants(RuntimeConstants runtimeConstants) {
+ _runtimeConstants = std::move(runtimeConstants);
}
/**
diff --git a/src/mongo/db/query/query_request.cpp b/src/mongo/db/query/query_request.cpp
index a8698404ea8..c43317b584b 100644
--- a/src/mongo/db/query/query_request.cpp
+++ b/src/mongo/db/query/query_request.cpp
@@ -98,6 +98,7 @@ const char kOplogReplayField[] = "oplogReplay";
const char kNoCursorTimeoutField[] = "noCursorTimeout";
const char kAwaitDataField[] = "awaitData";
const char kPartialResultsField[] = "allowPartialResults";
+const char kRuntimeConstantsField[] = "runtimeConstants";
const char kTermField[] = "term";
const char kOptionsField[] = "options";
const char kReadOnceField[] = "readOnce";
@@ -337,6 +338,14 @@ StatusWith<unique_ptr<QueryRequest>> QueryRequest::parseFromFindCommand(unique_p
}
qr->_allowPartialResults = el.boolean();
+ } else if (fieldName == kRuntimeConstantsField) {
+ Status status = checkFieldType(el, Object);
+ if (!status.isOK()) {
+ return status;
+ }
+ qr->_runtimeConstants =
+ RuntimeConstants::parse(IDLParserErrorContext(kRuntimeConstantsField),
+ cmdObj.getObjectField(kRuntimeConstantsField));
} else if (fieldName == kOptionsField) {
// 3.0.x versions of the shell may generate an explain of a find command with an
// 'options' field. We accept this only if the 'options' field is empty so that
@@ -544,6 +553,12 @@ void QueryRequest::asFindCommandInternal(BSONObjBuilder* cmdBuilder) const {
cmdBuilder->append(kPartialResultsField, true);
}
+ if (_runtimeConstants) {
+ BSONObjBuilder rtcBuilder(cmdBuilder->subobjStart(kRuntimeConstantsField));
+ _runtimeConstants->serialize(&rtcBuilder);
+ rtcBuilder.doneFast();
+ }
+
if (_replicationTerm) {
cmdBuilder->append(kTermField, *_replicationTerm);
}
@@ -1116,6 +1131,11 @@ StatusWith<BSONObj> QueryRequest::asAggregationCommand() const {
if (!_unwrappedReadPref.isEmpty()) {
aggregationBuilder.append(QueryRequest::kUnwrappedReadPrefField, _unwrappedReadPref);
}
+ if (_runtimeConstants) {
+ BSONObjBuilder rtcBuilder(aggregationBuilder.subobjStart(kRuntimeConstantsField));
+ _runtimeConstants->serialize(&rtcBuilder);
+ rtcBuilder.doneFast();
+ }
return StatusWith<BSONObj>(aggregationBuilder.obj());
}
} // namespace mongo
diff --git a/src/mongo/db/query/query_request.h b/src/mongo/db/query/query_request.h
index c87d9f951e3..ade62c52ddf 100644
--- a/src/mongo/db/query/query_request.h
+++ b/src/mongo/db/query/query_request.h
@@ -36,6 +36,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context.h"
+#include "mongo/db/pipeline/runtime_constants_gen.h"
#include "mongo/db/query/tailable_mode.h"
namespace mongo {
@@ -329,6 +330,14 @@ public:
return _tailableMode;
}
+ void setRuntimeConstants(RuntimeConstants runtimeConstants) {
+ _runtimeConstants = std::move(runtimeConstants);
+ }
+
+ const boost::optional<RuntimeConstants>& getRuntimeConstants() const {
+ return _runtimeConstants;
+ }
+
bool isSlaveOk() const {
return _slaveOk;
}
@@ -514,6 +523,9 @@ private:
bool _showRecordId = false;
bool _hasReadPref = false;
+ // Runtime constants which may be referenced by $expr, if present.
+ boost::optional<RuntimeConstants> _runtimeConstants;
+
// Options that can be specified in the OP_QUERY 'flags' header.
TailableModeEnum _tailableMode = TailableModeEnum::kNormal;
bool _slaveOk = false;
diff --git a/src/mongo/db/query/query_request_test.cpp b/src/mongo/db/query/query_request_test.cpp
index 3af9d96921e..0870bf245da 100644
--- a/src/mongo/db/query/query_request_test.cpp
+++ b/src/mongo/db/query/query_request_test.cpp
@@ -449,19 +449,22 @@ TEST(QueryRequestTest, ParseFromCommandCommentWithValidMinMax) {
}
TEST(QueryRequestTest, ParseFromCommandAllNonOptionFields) {
+ RuntimeConstants rtc{Date_t::now(), Timestamp(1, 1)};
+ BSONObj rtcObj = BSON("runtimeConstants" << rtc.toBSON());
BSONObj cmdObj = fromjson(
- "{find: 'testns',"
- "filter: {a: 1},"
- "sort: {b: 1},"
- "projection: {c: 1},"
- "hint: {d: 1},"
- "readConcern: {e: 1},"
- "$queryOptions: {$readPreference: 'secondary'},"
- "collation: {f: 1},"
- "limit: 3,"
- "skip: 5,"
- "batchSize: 90,"
- "singleBatch: false}");
+ "{find: 'testns',"
+ "filter: {a: 1},"
+ "sort: {b: 1},"
+ "projection: {c: 1},"
+ "hint: {d: 1},"
+ "readConcern: {e: 1},"
+ "$queryOptions: {$readPreference: 'secondary'},"
+ "collation: {f: 1},"
+ "limit: 3,"
+ "skip: 5,"
+ "batchSize: 90,"
+ "singleBatch: false}")
+ .addField(rtcObj["runtimeConstants"]);
const NamespaceString nss("test.testns");
bool isExplain = false;
unique_ptr<QueryRequest> qr(
@@ -486,6 +489,9 @@ TEST(QueryRequestTest, ParseFromCommandAllNonOptionFields) {
ASSERT_EQUALS(3, *qr->getLimit());
ASSERT_EQUALS(5, *qr->getSkip());
ASSERT_EQUALS(90, *qr->getBatchSize());
+ ASSERT(qr->getRuntimeConstants().has_value());
+ ASSERT_EQUALS(qr->getRuntimeConstants()->getLocalNow(), rtc.getLocalNow());
+ ASSERT_EQUALS(qr->getRuntimeConstants()->getClusterTime(), rtc.getClusterTime());
ASSERT(qr->wantMore());
}
@@ -788,6 +794,33 @@ TEST(QueryRequestTest, ParseFromCommandReadOnceWrongType) {
auto result = QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain);
ASSERT_EQ(ErrorCodes::FailedToParse, result.getStatus());
}
+
+TEST(QueryRequestTest, ParseFromCommandRuntimeConstantsWrongType) {
+ BSONObj cmdObj = BSON("find"
+ << "testns"
+ << "runtimeConstants"
+ << "shouldNotBeString");
+ const NamespaceString nss("test.testns");
+ bool isExplain = false;
+ auto result = QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain);
+ ASSERT_EQ(ErrorCodes::FailedToParse, result.getStatus());
+}
+
+TEST(QueryRequestTest, ParseFromCommandRuntimeConstantsSubfieldsWrongType) {
+ BSONObj cmdObj = BSON("find"
+ << "testns"
+ << "runtimeConstants"
+ << BSON("localNow"
+ << "shouldBeDate"
+ << "clusterTime"
+ << "shouldBeTimestamp"));
+ const NamespaceString nss("test.testns");
+ bool isExplain = false;
+ ASSERT_THROWS_CODE(QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain),
+ AssertionException,
+ ErrorCodes::TypeMismatch);
+}
+
//
// Parsing errors where a field has the right type but a bad value.
//
@@ -880,18 +913,21 @@ TEST(QueryRequestTest, ParseFromCommandDefaultBatchSize) {
//
TEST(QueryRequestTest, AsFindCommandAllNonOptionFields) {
+ BSONObj rtcObj =
+ BSON("runtimeConstants" << (RuntimeConstants{Date_t::now(), Timestamp(1, 1)}.toBSON()));
BSONObj cmdObj = fromjson(
- "{find: 'testns',"
- "filter: {a: 1},"
- "projection: {c: 1},"
- "sort: {b: 1},"
- "hint: {d: 1},"
- "readConcern: {e: 1},"
- "collation: {f: 1},"
- "skip: 5,"
- "limit: 3,"
- "batchSize: 90,"
- "singleBatch: true}");
+ "{find: 'testns',"
+ "filter: {a: 1},"
+ "projection: {c: 1},"
+ "sort: {b: 1},"
+ "hint: {d: 1},"
+ "readConcern: {e: 1},"
+ "collation: {f: 1},"
+ "skip: 5,"
+ "limit: 3,"
+ "batchSize: 90,"
+ "singleBatch: true}")
+ .addField(rtcObj["runtimeConstants"]);
const NamespaceString nss("test.testns");
bool isExplain = false;
unique_ptr<QueryRequest> qr(
@@ -900,19 +936,23 @@ TEST(QueryRequestTest, AsFindCommandAllNonOptionFields) {
}
TEST(QueryRequestTest, AsFindCommandWithUuidAllNonOptionFields) {
- BSONObj cmdObj = fromjson(
- // This binary value is UUID("01234567-89ab-cdef-edcb-a98765432101")
- "{find: { \"$binary\" : \"ASNFZ4mrze/ty6mHZUMhAQ==\", \"$type\" : \"04\" },"
- "filter: {a: 1},"
- "projection: {c: 1},"
- "sort: {b: 1},"
- "hint: {d: 1},"
- "readConcern: {e: 1},"
- "collation: {f: 1},"
- "skip: 5,"
- "limit: 3,"
- "batchSize: 90,"
- "singleBatch: true}");
+ BSONObj rtcObj =
+ BSON("runtimeConstants" << (RuntimeConstants{Date_t::now(), Timestamp(1, 1)}.toBSON()));
+ BSONObj cmdObj =
+ fromjson(
+ // This binary value is UUID("01234567-89ab-cdef-edcb-a98765432101")
+ "{find: { \"$binary\" : \"ASNFZ4mrze/ty6mHZUMhAQ==\", \"$type\" : \"04\" },"
+ "filter: {a: 1},"
+ "projection: {c: 1},"
+ "sort: {b: 1},"
+ "hint: {d: 1},"
+ "readConcern: {e: 1},"
+ "collation: {f: 1},"
+ "skip: 5,"
+ "limit: 3,"
+ "batchSize: 90,"
+ "singleBatch: true}")
+ .addField(rtcObj["runtimeConstants"]);
const NamespaceString nss("test.testns");
bool isExplain = false;
unique_ptr<QueryRequest> qr(
@@ -1049,6 +1089,7 @@ TEST(QueryRequestTest, DefaultQueryParametersCorrect) {
ASSERT_EQUALS(false, qr->isTailableAndAwaitData());
ASSERT_EQUALS(false, qr->isExhaust());
ASSERT_EQUALS(false, qr->isAllowPartialResults());
+ ASSERT_EQUALS(false, qr->getRuntimeConstants().has_value());
}
//
@@ -1330,6 +1371,20 @@ TEST(QueryRequestTest, ConvertToAggregationWithAllowSpeculativeMajorityReadFails
ASSERT_EQ(ErrorCodes::InvalidPipelineOperator, aggCmd.getStatus().code());
}
+TEST(QueryRequestTest, ConvertToAggregationWithRuntimeConstantsSucceeds) {
+ RuntimeConstants rtc{Date_t::now(), Timestamp(1, 1)};
+ QueryRequest qr(testns);
+ qr.setRuntimeConstants(rtc);
+ auto agg = qr.asAggregationCommand();
+ ASSERT_OK(agg);
+
+ auto ar = AggregationRequest::parseFromBSON(testns, agg.getValue());
+ ASSERT_OK(ar.getStatus());
+ ASSERT(ar.getValue().getRuntimeConstants().has_value());
+ ASSERT_EQ(ar.getValue().getRuntimeConstants()->getLocalNow(), rtc.getLocalNow());
+ ASSERT_EQ(ar.getValue().getRuntimeConstants()->getClusterTime(), rtc.getClusterTime());
+}
+
TEST(QueryRequestTest, ParseFromLegacyObjMetaOpComment) {
BSONObj queryObj = fromjson(
"{$query: {a: 1},"
diff --git a/src/mongo/s/commands/cluster_find_cmd.cpp b/src/mongo/s/commands/cluster_find_cmd.cpp
index e3c0461931f..dd726a64cc6 100644
--- a/src/mongo/s/commands/cluster_find_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_cmd.cpp
@@ -55,6 +55,20 @@ using std::vector;
const char kTermField[] = "term";
+// Parses the command object to a QueryRequest, validates that no runtime constants were supplied
+// with the command, and sets the constant runtime values that will be forwarded to each shard.
+std::unique_ptr<QueryRequest> parseCmdObjectToQueryRequest(OperationContext* opCtx,
+ NamespaceString nss,
+ BSONObj cmdObj,
+ bool isExplain) {
+ auto qr = uassertStatusOK(
+ QueryRequest::makeFromFindCommand(std::move(nss), std::move(cmdObj), isExplain));
+ uassert(
+ 51202, "Cannot specify runtime constants option to a mongos", !qr->getRuntimeConstants());
+ qr->setRuntimeConstants(Variables::generateRuntimeConstants(opCtx));
+ return qr;
+}
+
/**
* Implements the find command on mongos.
*/
@@ -122,12 +136,12 @@ public:
ExplainOptions::Verbosity verbosity,
rpc::ReplyBuilderInterface* result) override {
// Parse the command BSON to a QueryRequest.
- bool isExplain = true;
- auto qr =
- uassertStatusOK(QueryRequest::makeFromFindCommand(ns(), _request.body, isExplain));
+ const bool isExplain = true;
+ auto qr = parseCmdObjectToQueryRequest(opCtx, ns(), _request.body, isExplain);
try {
- const auto explainCmd = ClusterExplain::wrapAsExplain(_request.body, verbosity);
+ const auto explainCmd =
+ ClusterExplain::wrapAsExplain(qr->asFindCommand(), verbosity);
long long millisElapsed;
std::vector<AsyncRequestsSender::Response> shardResponses;
@@ -185,8 +199,7 @@ public:
globalOpCounters.gotQuery();
const bool isExplain = false;
- auto qr =
- uassertStatusOK(QueryRequest::makeFromFindCommand(ns(), _request.body, isExplain));
+ auto qr = parseCmdObjectToQueryRequest(opCtx, ns(), _request.body, isExplain);
const boost::intrusive_ptr<ExpressionContext> expCtx;
auto cq = uassertStatusOK(
diff --git a/src/mongo/s/write_ops/batched_command_request.h b/src/mongo/s/write_ops/batched_command_request.h
index 1c454adaecc..b49e893e5aa 100644
--- a/src/mongo/s/write_ops/batched_command_request.h
+++ b/src/mongo/s/write_ops/batched_command_request.h
@@ -115,9 +115,9 @@ public:
return *_shardVersion;
}
- void setRuntimeConstants(const RuntimeConstants& runtimeConstants) {
+ void setRuntimeConstants(RuntimeConstants runtimeConstants) {
invariant(_updateReq);
- _updateReq->setRuntimeConstants(runtimeConstants);
+ _updateReq->setRuntimeConstants(std::move(runtimeConstants));
}
bool hasRuntimeConstants() const {