diff options
author | Bernard Gorman <bernard.gorman@gmail.com> | 2019-05-13 20:02:34 +0100 |
---|---|---|
committer | Bernard Gorman <bernard.gorman@gmail.com> | 2019-05-19 11:25:13 +0100 |
commit | 209ea38bcc2cfb68e0374e14814fc18461cebc58 (patch) | |
tree | 8bd4b6b66901c35b6514918d1c70032f8dc17f42 /src | |
parent | b7a30e03816491a43887ed69a99b95481e83f0fd (diff) | |
download | mongo-209ea38bcc2cfb68e0374e14814fc18461cebc58.tar.gz |
SERVER-40407 Add support for $$NOW and $$CLUSTER_TIME in the findAndModify command
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/commands/find_and_modify.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/ops/delete_request.h | 8 | ||||
-rw-r--r-- | src/mongo/db/ops/parsed_delete.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/ops/parsed_delete.h | 3 | ||||
-rw-r--r-- | src/mongo/db/ops/update_request.h | 8 | ||||
-rw-r--r-- | src/mongo/db/query/find_and_modify_request.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/query/find_and_modify_request.h | 16 | ||||
-rw-r--r-- | src/mongo/db/query/find_and_modify_request_test.cpp | 33 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_find_and_modify_cmd.cpp | 25 |
9 files changed, 119 insertions, 14 deletions
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index 4106430d999..3f6d65e5426 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -105,7 +105,7 @@ boost::optional<BSONObj> advanceExecutor(OperationContext* opCtx, return boost::none; } -void makeUpdateRequest(const OperationContext* opCtx, +void makeUpdateRequest(OperationContext* opCtx, const FindAndModifyRequest& args, bool explain, UpdateRequest* requestOut) { @@ -113,6 +113,8 @@ void makeUpdateRequest(const OperationContext* opCtx, requestOut->setProj(args.getFields()); invariant(args.getUpdate()); requestOut->setUpdateModification(*args.getUpdate()); + requestOut->setRuntimeConstants( + args.getRuntimeConstants().value_or(Variables::generateRuntimeConstants(opCtx))); requestOut->setSort(args.getSort()); requestOut->setCollation(args.getCollation()); requestOut->setArrayFilters(args.getArrayFilters()); @@ -129,12 +131,14 @@ void makeUpdateRequest(const OperationContext* opCtx, : PlanExecutor::YIELD_AUTO); } -void makeDeleteRequest(const OperationContext* opCtx, +void makeDeleteRequest(OperationContext* opCtx, const FindAndModifyRequest& args, bool explain, DeleteRequest* requestOut) { requestOut->setQuery(args.getQuery()); requestOut->setProj(args.getFields()); + requestOut->setRuntimeConstants( + args.getRuntimeConstants().value_or(Variables::generateRuntimeConstants(opCtx))); requestOut->setSort(args.getSort()); requestOut->setCollation(args.getCollation()); requestOut->setMulti(false); diff --git a/src/mongo/db/ops/delete_request.h b/src/mongo/db/ops/delete_request.h index 23b3bc81e42..937297b95ae 100644 --- a/src/mongo/db/ops/delete_request.h +++ b/src/mongo/db/ops/delete_request.h @@ -33,6 +33,7 @@ #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" +#include "mongo/db/pipeline/runtime_constants_gen.h" #include "mongo/db/query/plan_executor.h" namespace mongo { @@ -60,6 +61,9 @@ public: void setSort(const BSONObj& sort) { _sort = sort; } + void setRuntimeConstants(const RuntimeConstants& runtimeConstants) { + _runtimeConstants = runtimeConstants; + } void setCollation(const BSONObj& collation) { _collation = collation; } @@ -94,6 +98,9 @@ public: const BSONObj& getSort() const { return _sort; } + const boost::optional<RuntimeConstants>& getRuntimeConstants() const { + return _runtimeConstants; + } const BSONObj& getCollation() const { return _collation; } @@ -130,6 +137,7 @@ private: BSONObj _proj; BSONObj _sort; BSONObj _collation; + boost::optional<RuntimeConstants> _runtimeConstants; // The statement id of this request. StmtId _stmtId = kUninitializedStmtId; bool _multi; diff --git a/src/mongo/db/ops/parsed_delete.cpp b/src/mongo/db/ops/parsed_delete.cpp index ad6ec0c25e0..0ce2cbe557a 100644 --- a/src/mongo/db/ops/parsed_delete.cpp +++ b/src/mongo/db/ops/parsed_delete.cpp @@ -39,6 +39,7 @@ #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" @@ -60,6 +61,17 @@ 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(); } @@ -90,11 +102,12 @@ Status ParsedDelete::parseQueryToCQ() { qr->setLimit(1); } - const boost::intrusive_ptr<ExpressionContext> expCtx; + auto expCtx = + make_intrusive<ExpressionContext>(_opCtx, _collator.get(), _request->getRuntimeConstants()); auto statusWithCQ = CanonicalQuery::canonicalize(_opCtx, std::move(qr), - expCtx, + std::move(expCtx), extensionsCallback, MatchExpressionParser::kAllowAllSpecialFeatures); diff --git a/src/mongo/db/ops/parsed_delete.h b/src/mongo/db/ops/parsed_delete.h index 73f4bef19e4..724c4a48656 100644 --- a/src/mongo/db/ops/parsed_delete.h +++ b/src/mongo/db/ops/parsed_delete.h @@ -107,6 +107,9 @@ 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/update_request.h b/src/mongo/db/ops/update_request.h index 2e84c575f11..763307a9260 100644 --- a/src/mongo/db/ops/update_request.h +++ b/src/mongo/db/ops/update_request.h @@ -219,10 +219,6 @@ public: builder << " updateModification: " << _updateMod.toString(); builder << " stmtId: " << _stmtId; - if (_runtimeConstants) { - builder << "runtimeConstants: " << _runtimeConstants->toBSON().toString(); - } - builder << " arrayFilters: ["; bool first = true; for (auto arrayFilter : _arrayFilters) { @@ -234,6 +230,10 @@ public: } builder << "]"; + if (_runtimeConstants) { + builder << " runtimeConstants: " << _runtimeConstants->toBSON().toString(); + } + builder << " god: " << _god; builder << " upsert: " << _upsert; builder << " multi: " << _multi; diff --git a/src/mongo/db/query/find_and_modify_request.cpp b/src/mongo/db/query/find_and_modify_request.cpp index 29a4d3bb3ea..f98e8a00eb3 100644 --- a/src/mongo/db/query/find_and_modify_request.cpp +++ b/src/mongo/db/query/find_and_modify_request.cpp @@ -45,6 +45,7 @@ const char kQueryField[] = "query"; const char kSortField[] = "sort"; const char kCollationField[] = "collation"; const char kArrayFiltersField[] = "arrayFilters"; +const char kRuntimeConstantsField[] = "runtimeConstants"; const char kRemoveField[] = "remove"; const char kUpdateField[] = "update"; const char kNewField[] = "new"; @@ -122,6 +123,12 @@ BSONObj FindAndModifyRequest::toBSON(const BSONObj& commandPassthroughFields) co arrayBuilder.doneFast(); } + if (_runtimeConstants) { + BSONObjBuilder rtcBuilder(builder.subobjStart(kRuntimeConstantsField)); + _runtimeConstants->serialize(&rtcBuilder); + rtcBuilder.doneFast(); + } + if (_shouldReturnNew) { builder.append(kNewField, _shouldReturnNew); } @@ -155,6 +162,7 @@ StatusWith<FindAndModifyRequest> FindAndModifyRequest::parseFromBSON(NamespaceSt bool bypassDocumentValidation = false; bool arrayFiltersSet = false; std::vector<BSONObj> arrayFilters; + boost::optional<RuntimeConstants> runtimeConstants; bool writeConcernOptionsSet = false; WriteConcernOptions writeConcernOptions; @@ -203,6 +211,10 @@ StatusWith<FindAndModifyRequest> FindAndModifyRequest::parseFromBSON(NamespaceSt arrayFilters.push_back(arrayFilter.embeddedObject()); } } + } else if (field == kRuntimeConstantsField) { + runtimeConstants = + RuntimeConstants::parse(IDLParserErrorContext(kRuntimeConstantsField), + cmdObj.getObjectField(kRuntimeConstantsField)); } else if (field == kWriteConcernField) { BSONElement writeConcernElt; Status writeConcernEltStatus = bsonExtractTypedField( @@ -260,6 +272,9 @@ StatusWith<FindAndModifyRequest> FindAndModifyRequest::parseFromBSON(NamespaceSt if (arrayFiltersSet) { request.setArrayFilters(std::move(arrayFilters)); } + if (runtimeConstants) { + request.setRuntimeConstants(*runtimeConstants); + } if (writeConcernOptionsSet) { request.setWriteConcern(std::move(writeConcernOptions)); } diff --git a/src/mongo/db/query/find_and_modify_request.h b/src/mongo/db/query/find_and_modify_request.h index 3b7844d6c7e..2ee73859d1b 100644 --- a/src/mongo/db/query/find_and_modify_request.h +++ b/src/mongo/db/query/find_and_modify_request.h @@ -34,6 +34,7 @@ #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" #include "mongo/db/ops/write_ops_parsers.h" +#include "mongo/db/pipeline/runtime_constants_gen.h" #include "mongo/db/write_concern_options.h" namespace mongo { @@ -164,6 +165,20 @@ public: void setArrayFilters(const std::vector<BSONObj>& arrayFilters); /** + * Sets any constant values which may be required by the query and/or update. + */ + void setRuntimeConstants(const RuntimeConstants& runtimeConstants) { + _runtimeConstants = runtimeConstants; + } + + /** + * Returns the runtime constants associated with this findAndModify request, if present. + */ + const boost::optional<RuntimeConstants>& getRuntimeConstants() const { + return _runtimeConstants; + } + + /** * Sets the write concern for this request. */ void setWriteConcern(WriteConcernOptions writeConcern); @@ -187,6 +202,7 @@ private: boost::optional<BSONObj> _sort; boost::optional<BSONObj> _collation; boost::optional<std::vector<BSONObj>> _arrayFilters; + boost::optional<RuntimeConstants> _runtimeConstants; bool _shouldReturnNew{false}; boost::optional<WriteConcernOptions> _writeConcern; bool _bypassDocumentValidation{false}; diff --git a/src/mongo/db/query/find_and_modify_request_test.cpp b/src/mongo/db/query/find_and_modify_request_test.cpp index a904b47bca4..29f0fc4ac60 100644 --- a/src/mongo/db/query/find_and_modify_request_test.cpp +++ b/src/mongo/db/query/find_and_modify_request_test.cpp @@ -31,6 +31,7 @@ #include "mongo/bson/json.h" #include "mongo/db/commands/test_commands_enabled.h" +#include "mongo/db/pipeline/runtime_constants_gen.h" #include "mongo/db/query/find_and_modify_request.h" #include "mongo/unittest/unittest.h" @@ -223,6 +224,26 @@ TEST(FindAndModifyRequest, UpdateWithWriteConcern) { ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({})); } +TEST(FindAndModifyRequest, UpdateWithRuntimeConstants) { + const BSONObj query(BSON("x" << 1)); + const BSONObj update(BSON("y" << 1)); + + auto request = FindAndModifyRequest::makeUpdate(NamespaceString("test.user"), query, update); + request.setRuntimeConstants({Date_t(), Timestamp(1, 0)}); + + BSONObj expectedObj(fromjson(R"json({ + findAndModify: 'user', + query: { x: 1 }, + update: { y: 1 }, + runtimeConstants: { + localNow: new Date(0), + clusterTime: Timestamp(1, 0) + } + })json")); + + ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({})); +} + TEST(FindAndModifyRequest, UpdateWithFullSpec) { const BSONObj query(BSON("x" << 1)); const BSONObj update(BSON("y" << 1)); @@ -232,6 +253,7 @@ TEST(FindAndModifyRequest, UpdateWithFullSpec) { const std::vector<BSONObj> arrayFilters{BSON("i" << 0)}; const BSONObj field(BSON("x" << 1 << "y" << 1)); const WriteConcernOptions writeConcern(2, WriteConcernOptions::SyncMode::FSYNC, 150); + auto rtc = RuntimeConstants{Date_t(), Timestamp(1, 0)}; auto request = FindAndModifyRequest::makeUpdate(NamespaceString("test.user"), query, update); request.setFieldProjection(field); @@ -239,6 +261,7 @@ TEST(FindAndModifyRequest, UpdateWithFullSpec) { request.setSort(sort); request.setCollation(collation); request.setArrayFilters(arrayFilters); + request.setRuntimeConstants(rtc); request.setWriteConcern(writeConcern); request.setBypassDocumentValidation(true); request.setUpsert(true); @@ -252,6 +275,10 @@ TEST(FindAndModifyRequest, UpdateWithFullSpec) { sort: { z: -1 }, collation: { locale: 'en_US' }, arrayFilters: [ { i: 0 } ], + runtimeConstants: { + localNow: new Date(0), + clusterTime: Timestamp(1, 0) + }, new: true, writeConcern: { w: 2, fsync: true, wtimeout: 150 }, bypassDocumentValidation: true @@ -349,12 +376,14 @@ TEST(FindAndModifyRequest, RemoveWithFullSpec) { << "en_US")); const BSONObj field(BSON("x" << 1 << "y" << 1)); const WriteConcernOptions writeConcern(2, WriteConcernOptions::SyncMode::FSYNC, 150); + auto rtc = RuntimeConstants{Date_t(), Timestamp(1, 0)}; auto request = FindAndModifyRequest::makeRemove(NamespaceString("test.user"), query); request.setFieldProjection(field); request.setSort(sort); request.setCollation(collation); request.setWriteConcern(writeConcern); + request.setRuntimeConstants(rtc); BSONObj expectedObj(fromjson(R"json({ findAndModify: 'user', @@ -363,6 +392,10 @@ TEST(FindAndModifyRequest, RemoveWithFullSpec) { fields: { x: 1, y: 1 }, sort: { z: -1 }, collation: { locale: 'en_US' }, + runtimeConstants: { + localNow: new Date(0), + clusterTime: Timestamp(1, 0) + }, writeConcern: { w: 2, fsync: true, wtimeout: 150 } })json")); diff --git a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp index 54330b958db..940c9299aaf 100644 --- a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp +++ b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp @@ -58,6 +58,16 @@ namespace mongo { namespace { const ReadPreferenceSetting kPrimaryOnlyReadPreference(ReadPreference::PrimaryOnly); +const char kRuntimeConstantsField[] = "runtimeConstants"; + +BSONObj appendRuntimeConstantsToCommandObject(OperationContext* opCtx, const BSONObj& origCmdObj) { + uassert(51196, + "Cannot specify runtime constants option to a mongos", + !origCmdObj.getField(kRuntimeConstantsField)); + auto rtcBSON = + BSON(kRuntimeConstantsField << Variables::generateRuntimeConstants(opCtx).toBSON()); + return origCmdObj.addField(rtcBSON.getField(kRuntimeConstantsField)); +} BSONObj getCollation(const BSONObj& cmdObj) { BSONElement collationElement; @@ -183,7 +193,8 @@ public: Grid::get(opCtx)->shardRegistry()->getShard(opCtx, chunk.getShardId())); } - const auto explainCmd = ClusterExplain::wrapAsExplain(cmdObj, verbosity); + const auto explainCmd = ClusterExplain::wrapAsExplain( + appendRuntimeConstantsToCommandObject(opCtx, cmdObj), verbosity); // Time how long it takes to run the explain command on the shard. Timer timer; @@ -219,21 +230,24 @@ public: // that the parsing be pulled into this function. uassertStatusOK(createShardDatabase(opCtx, nss.db())); + // Append mongoS' runtime constants to the command object before forwarding it to the shard. + auto cmdObjForShard = appendRuntimeConstantsToCommandObject(opCtx, cmdObj); + const auto routingInfo = uassertStatusOK(getCollectionRoutingInfoForTxnCmd(opCtx, nss)); if (!routingInfo.cm()) { _runCommand(opCtx, routingInfo.db().primaryId(), ChunkVersion::UNSHARDED(), nss, - cmdObj, + cmdObjForShard, &result); return true; } const auto chunkMgr = routingInfo.cm(); - const BSONObj query = cmdObj.getObjectField("query"); - const BSONObj collation = getCollation(cmdObj); + const BSONObj query = cmdObjForShard.getObjectField("query"); + const BSONObj collation = getCollation(cmdObjForShard); const BSONObj shardKey = getShardKey(opCtx, *chunkMgr, query); auto chunk = chunkMgr->findIntersectingChunk(shardKey, collation); @@ -241,7 +255,7 @@ public: chunk.getShardId(), chunkMgr->getVersion(chunk.getShardId()), nss, - cmdObj, + cmdObjForShard, &result); return true; @@ -338,7 +352,6 @@ private: result->appendElementsUnique( CommandHelpers::filterCommandReplyForPassthrough(response.data)); } - } findAndModifyCmd; } // namespace |