summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBernard Gorman <bernard.gorman@gmail.com>2019-05-13 20:02:34 +0100
committerBernard Gorman <bernard.gorman@gmail.com>2019-05-19 11:25:13 +0100
commit209ea38bcc2cfb68e0374e14814fc18461cebc58 (patch)
tree8bd4b6b66901c35b6514918d1c70032f8dc17f42 /src
parentb7a30e03816491a43887ed69a99b95481e83f0fd (diff)
downloadmongo-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.cpp8
-rw-r--r--src/mongo/db/ops/delete_request.h8
-rw-r--r--src/mongo/db/ops/parsed_delete.cpp17
-rw-r--r--src/mongo/db/ops/parsed_delete.h3
-rw-r--r--src/mongo/db/ops/update_request.h8
-rw-r--r--src/mongo/db/query/find_and_modify_request.cpp15
-rw-r--r--src/mongo/db/query/find_and_modify_request.h16
-rw-r--r--src/mongo/db/query/find_and_modify_request_test.cpp33
-rw-r--r--src/mongo/s/commands/cluster_find_and_modify_cmd.cpp25
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