summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArun Banala <arun.banala@mongodb.com>2020-11-16 17:38:10 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-12-02 18:37:38 +0000
commit864d81272c7abe7b411a569001e9f809b8407687 (patch)
tree8fda7a4b53cb93cf80803ea62b77e3f9459535da
parent6446fe2ed7298f0e91476384fc7fcf2abc9b9328 (diff)
downloadmongo-864d81272c7abe7b411a569001e9f809b8407687.tar.gz
SERVER-51620 Convert findAndModify input to IDL
-rw-r--r--jstests/core/command_json_schema_field.js2
-rw-r--r--jstests/core/explain_uuid.js2
-rw-r--r--jstests/core/find_and_modify_invalid_query_params.js85
-rw-r--r--jstests/replsets/lastop.js2
-rw-r--r--src/mongo/db/commands/find_and_modify.cpp180
-rw-r--r--src/mongo/db/ops/SConscript1
-rw-r--r--src/mongo/db/ops/find_and_modify_command.idl112
-rw-r--r--src/mongo/db/ops/write_ops.idl2
-rw-r--r--src/mongo/db/ops/write_ops_retryability.cpp27
-rw-r--r--src/mongo/db/ops/write_ops_retryability.h5
-rw-r--r--src/mongo/db/ops/write_ops_retryability_test.cpp67
-rw-r--r--src/mongo/db/query/SConscript2
-rw-r--r--src/mongo/db/query/find_and_modify_request.cpp441
-rw-r--r--src/mongo/db/query/find_and_modify_request.h236
-rw-r--r--src/mongo/db/query/find_and_modify_request_test.cpp786
-rw-r--r--src/mongo/db/s/shard_local_test.cpp15
-rw-r--r--src/mongo/s/catalog/dist_lock_catalog_impl.cpp59
-rw-r--r--src/mongo/s/catalog/dist_lock_catalog_impl.h4
-rw-r--r--src/mongo/s/catalog/dist_lock_catalog_impl_test.cpp1
19 files changed, 394 insertions, 1635 deletions
diff --git a/jstests/core/command_json_schema_field.js b/jstests/core/command_json_schema_field.js
index 0487e6ac451..7015cad289a 100644
--- a/jstests/core/command_json_schema_field.js
+++ b/jstests/core/command_json_schema_field.js
@@ -30,7 +30,7 @@ assertCommandFailsWithCorrectError({find: coll.getName(), jsonSchema: {}},
// FindAndModify
assertCommandFailsWithCorrectError(
{findAndModify: coll.getName(), query: {_id: 0}, remove: true, jsonSchema: {}},
- ErrorCodes.FailedToParse);
+ [ErrorCodes.FailedToParse, 4662500]);
// Count
assertCommandFailsWithCorrectError({count: coll.getName(), jsonSchema: {}}, 4662500);
diff --git a/jstests/core/explain_uuid.js b/jstests/core/explain_uuid.js
index bee56d4b2a9..84d67074c8d 100644
--- a/jstests/core/explain_uuid.js
+++ b/jstests/core/explain_uuid.js
@@ -36,7 +36,7 @@ assert.commandFailedWithCode(explainDB.runCommand({explain: {distinct: uuid, key
const expectedCode = TestData.auth ? 17137 : ErrorCodes.InvalidNamespace;
assert.commandFailedWithCode(
explainDB.runCommand({explain: {findAndModify: uuid, query: {a: 1}, remove: true}}),
- expectedCode);
+ [expectedCode, ErrorCodes.BadValue]);
assert.commandFailedWithCode(
explainDB.runCommand({explain: {delete: uuid, deletes: [{q: {}, limit: 1}]}}),
diff --git a/jstests/core/find_and_modify_invalid_query_params.js b/jstests/core/find_and_modify_invalid_query_params.js
index 6c5d16c94f1..ad8ae4439b5 100644
--- a/jstests/core/find_and_modify_invalid_query_params.js
+++ b/jstests/core/find_and_modify_invalid_query_params.js
@@ -1,7 +1,8 @@
/**
* Test that the 'findAndModify' command throws the expected errors for invalid query, sort and
* projection parameters. This test exercises the fix for SERVER-41829.
- * @tags: [assumes_unsharded_collection]
+ *
+ * @tags: [assumes_unsharded_collection, requires_fcv_49]
*/
(function() {
"use strict";
@@ -12,8 +13,8 @@ coll.insert({_id: 0});
coll.insert({_id: 1});
function assertFailedWithCode(cmd, errorCode) {
- const err = assert.throws(() => coll.findAndModify(cmd));
- assert.eq(err.code, errorCode);
+ assert.commandFailedWithCode(
+ coll.runCommand(Object.merge({findAndModify: coll.getName()}, cmd)), errorCode);
}
function assertWorked(cmd, expectedValue) {
@@ -24,12 +25,12 @@ function assertWorked(cmd, expectedValue) {
// Verify that the findAndModify command works when we supply a valid query.
let out = coll.findAndModify({query: {_id: 1}, update: {$set: {value: "basic"}}, new: true});
assert.eq(out, {_id: 1, value: "basic"});
+assertWorked({query: null, update: {value: 2}, new: true}, 2);
// Verify that invalid 'query' object fails.
-assertFailedWithCode({query: null, update: {value: 2}}, 31160);
-assertFailedWithCode({query: 1, update: {value: 2}}, 31160);
-assertFailedWithCode({query: "{_id: 1}", update: {value: 2}}, 31160);
-assertFailedWithCode({query: false, update: {value: 2}}, 31160);
+assertFailedWithCode({query: 1, update: {value: 2}}, ErrorCodes.TypeMismatch);
+assertFailedWithCode({query: "{_id: 1}", update: {value: 2}}, ErrorCodes.TypeMismatch);
+assertFailedWithCode({query: false, update: {value: 2}}, ErrorCodes.TypeMismatch);
// Verify that missing and empty query object is allowed.
assertWorked({update: {$set: {value: "missingQuery"}}, new: true}, "missingQuery");
@@ -37,12 +38,12 @@ assertWorked({query: {}, update: {$set: {value: "emptyQuery"}}, new: true}, "emp
// Verify that command works when we supply a valid sort specification.
assertWorked({sort: {_id: -1}, update: {$set: {value: "sort"}}, new: true}, "sort");
+assertWorked({sort: null, update: {value: 2}, new: true}, 2);
// Verify that invaid 'sort' object fails.
-assertFailedWithCode({sort: null, update: {value: 2}}, 31174);
-assertFailedWithCode({sort: 1, update: {value: 2}}, 31174);
-assertFailedWithCode({sort: "{_id: 1}", update: {value: 2}}, 31174);
-assertFailedWithCode({sort: false, update: {value: 2}}, 31174);
+assertFailedWithCode({sort: 1, update: {value: 2}}, ErrorCodes.TypeMismatch);
+assertFailedWithCode({sort: "{_id: 1}", update: {value: 2}}, ErrorCodes.TypeMismatch);
+assertFailedWithCode({sort: false, update: {value: 2}}, ErrorCodes.TypeMismatch);
// Verify that missing and empty 'sort' object is allowed.
assertWorked({update: {$set: {value: "missingSort"}}, new: true}, "missingSort");
@@ -50,12 +51,12 @@ assertWorked({sort: {}, update: {$set: {value: "emptySort"}}, new: true}, "empty
// Verify that the 'fields' projection works.
assertWorked({fields: {_id: 0}, update: {$set: {value: "project"}}, new: true}, "project");
+assertWorked({fields: null, update: {value: 2}, new: true}, 2);
// Verify that invaid 'fields' object fails.
-assertFailedWithCode({fields: null, update: {value: 2}}, 31175);
-assertFailedWithCode({fields: 1, update: {value: 2}}, 31175);
-assertFailedWithCode({fields: "{_id: 1}", update: {value: 2}}, 31175);
-assertFailedWithCode({fields: false, update: {value: 2}}, 31175);
+assertFailedWithCode({fields: 1, update: {value: 2}}, ErrorCodes.TypeMismatch);
+assertFailedWithCode({fields: "{_id: 1}", update: {value: 2}}, ErrorCodes.TypeMismatch);
+assertFailedWithCode({fields: false, update: {value: 2}}, ErrorCodes.TypeMismatch);
// Verify that missing and empty 'fields' object is allowed. Also verify that the command
// projects all the fields.
@@ -64,21 +65,21 @@ assertWorked({fields: {}, update: {$set: {value: "emptyFields"}}, new: true}, "e
// Verify that findOneAndDelete() shell helper throws the same errors as findAndModify().
let err = assert.throws(() => coll.findOneAndDelete("{_id: 1}"));
-assert.eq(err.code, 31160);
+assert(err.code == ErrorCodes.TypeMismatch, err);
err = assert.throws(() => coll.findOneAndDelete(null, {sort: 1}));
-assert.eq(err.code, 31174);
+assert(err.code == ErrorCodes.TypeMismatch, err);
// Verify that findOneAndReplace() shell helper throws the same errors as findAndModify().
err = assert.throws(() => coll.findOneAndReplace("{_id: 1}", {}));
-assert.eq(err.code, 31160);
+assert(err.code == ErrorCodes.TypeMismatch, err);
err = assert.throws(() => coll.findOneAndReplace(null, {}, {sort: 1}));
-assert.eq(err.code, 31174);
+assert(err.code == ErrorCodes.TypeMismatch, err);
// Verify that findOneAndUpdate() shell helper throws the same errors as findAndModify().
err = assert.throws(() => coll.findOneAndUpdate("{_id: 1}", {$set: {value: "new"}}));
-assert.eq(err.code, 31160);
+assert(err.code == ErrorCodes.TypeMismatch, err);
err = assert.throws(() => coll.findOneAndUpdate(null, {$set: {value: "new"}}, {sort: 1}));
-assert.eq(err.code, 31174);
+assert(err.code == ErrorCodes.TypeMismatch, err);
// Verify that find and modify shell helpers allow null query object.
out = coll.findOneAndUpdate(null, {$set: {value: "findOneAndUpdate"}}, {returnNewDocument: true});
@@ -89,4 +90,46 @@ assert.eq(out.value, "findOneAndReplace");
out = coll.findOneAndDelete(null);
assert.eq(out.value, "findOneAndReplace");
+
+// Incompatiable parameters with 'remove'.
+coll.drop();
+coll.insert({_id: 0});
+assertFailedWithCode({update: {value: 2}, remove: true}, ErrorCodes.FailedToParse);
+assertFailedWithCode({query: {_id: 1}, upsert: true, remove: true}, ErrorCodes.FailedToParse);
+assertFailedWithCode({new: true, remove: true}, ErrorCodes.FailedToParse);
+assertFailedWithCode({update: {}, arrayFilters: [{"x.a": {$gt: 85}}], remove: true},
+ ErrorCodes.FailedToParse);
+
+// Verify that the above updates succeed when the boolean values are set to 'false' or '0'.
+assertWorked({update: {value: "newVal"}, new: true, remove: false}, "newVal");
+assertWorked({update: {value: "newerVal"}, new: false, remove: 0}, "newVal");
+assertWorked({query: {_id: 1}, update: {value: "newerVal"}, new: 1, remove: false, upsert: 1},
+ "newerVal");
+assertWorked(
+ {update: {value: "newestVal"}, new: false, arrayFilters: [{"x.a": {$gt: 85}}], remove: 0.0},
+ "newerVal");
+
+out = coll.findAndModify(
+ {query: {_id: 2}, update: {value: "newVal"}, new: false, remove: false, upsert: true});
+assert.eq(out, null);
+
+// Pipeline update with 'arrayFilters'.
+assertFailedWithCode({update: [], arrayFilters: [{"x.a": {$gt: 85}}]}, ErrorCodes.FailedToParse);
+
+// Invalid value for hint.
+assertFailedWithCode({query: {_id: 0}, update: {value: "usedHint"}, hint: 1},
+ ErrorCodes.FailedToParse);
+
+// Hint with string and object.
+coll.createIndex({value: 1}, {name: 'value'});
+assertWorked({query: {_id: 0}, update: {value: "usedHint"}, new: 1.0, hint: "value", remove: 0.0},
+ "usedHint");
+assertWorked({
+ query: {_id: 0, value: "usedHint"},
+ update: {value: "newValue"},
+ new: 0,
+ hint: {_id: 1},
+ remove: 0
+},
+ "usedHint");
})();
diff --git a/jstests/replsets/lastop.js b/jstests/replsets/lastop.js
index 1abcd15abeb..87c52f4f04d 100644
--- a/jstests/replsets/lastop.js
+++ b/jstests/replsets/lastop.js
@@ -47,7 +47,7 @@ assert.commandWorked(m2.getCollection("test.foo").insert({m2: 97}));
var sixthOp = m2.getCollection("test.foo").getDB().getLastErrorObj().lastOp;
// No-op find-and-modify delete
-m1.getCollection("test.foo").findAndModify({query: {m1: 1}, remove: 'true'});
+m1.getCollection("test.foo").findAndModify({query: {m1: 1}, remove: true});
noOp = m1.getCollection("test.foo").getDB().getLastErrorObj().lastOp;
assert.eq(noOp, sixthOp);
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp
index 9f0c93060bf..fbfe5d10fd3 100644
--- a/src/mongo/db/commands/find_and_modify.cpp
+++ b/src/mongo/db/commands/find_and_modify.cpp
@@ -62,7 +62,6 @@
#include "mongo/db/ops/write_ops_retryability.h"
#include "mongo/db/query/collection_query_info.h"
#include "mongo/db/query/explain.h"
-#include "mongo/db/query/find_and_modify_request.h"
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/plan_executor.h"
#include "mongo/db/query/plan_summary_stats.h"
@@ -119,24 +118,54 @@ boost::optional<BSONObj> advanceExecutor(OperationContext* opCtx,
return boost::none;
}
+void validate(const write_ops::FindAndModifyCommand& request) {
+ uassert(ErrorCodes::FailedToParse,
+ "Either an update or remove=true must be specified",
+ request.getRemove().value_or(false) || request.getUpdate());
+ if (request.getRemove().value_or(false)) {
+ uassert(ErrorCodes::FailedToParse,
+ "Cannot specify both an update and remove=true",
+ !request.getUpdate());
+
+ uassert(ErrorCodes::FailedToParse,
+ "Cannot specify both upsert=true and remove=true ",
+ !request.getUpsert() || !*request.getUpsert());
+
+ uassert(ErrorCodes::FailedToParse,
+ "Cannot specify both new=true and remove=true; 'remove' always returns the deleted "
+ "document",
+ !request.getNew() || !*request.getNew());
+
+ uassert(ErrorCodes::FailedToParse,
+ "Cannot specify arrayFilters and remove=true",
+ !request.getArrayFilters());
+ }
+
+ if (request.getUpdate() &&
+ request.getUpdate()->type() == write_ops::UpdateModification::Type::kPipeline &&
+ request.getArrayFilters()) {
+ uasserted(ErrorCodes::FailedToParse, "Cannot specify arrayFilters and a pipeline update");
+ }
+}
+
void makeUpdateRequest(OperationContext* opCtx,
- const FindAndModifyRequest& args,
+ const write_ops::FindAndModifyCommand& request,
boost::optional<ExplainOptions::Verbosity> explain,
UpdateRequest* requestOut) {
- requestOut->setQuery(args.getQuery());
- requestOut->setProj(args.getFields());
- invariant(args.getUpdate());
- requestOut->setUpdateModification(*args.getUpdate());
+ requestOut->setQuery(request.getQuery());
+ requestOut->setProj(request.getFields().value_or(BSONObj()));
+ invariant(request.getUpdate());
+ requestOut->setUpdateModification(*request.getUpdate());
requestOut->setLegacyRuntimeConstants(
- args.getLegacyRuntimeConstants().value_or(Variables::generateRuntimeConstants(opCtx)));
- requestOut->setLetParameters(args.getLetParameters());
- requestOut->setSort(args.getSort());
- requestOut->setHint(args.getHint());
- requestOut->setCollation(args.getCollation());
- requestOut->setArrayFilters(args.getArrayFilters());
- requestOut->setUpsert(args.isUpsert());
- requestOut->setReturnDocs(args.shouldReturnNew() ? UpdateRequest::RETURN_NEW
- : UpdateRequest::RETURN_OLD);
+ request.getLegacyRuntimeConstants().value_or(Variables::generateRuntimeConstants(opCtx)));
+ requestOut->setLetParameters(request.getLet());
+ requestOut->setSort(request.getSort().value_or(BSONObj()));
+ requestOut->setHint(request.getHint());
+ requestOut->setCollation(request.getCollation().value_or(BSONObj()));
+ requestOut->setArrayFilters(request.getArrayFilters().value_or(std::vector<BSONObj>()));
+ requestOut->setUpsert(request.getUpsert().value_or(false));
+ requestOut->setReturnDocs((request.getNew().value_or(false)) ? UpdateRequest::RETURN_NEW
+ : UpdateRequest::RETURN_OLD);
requestOut->setMulti(false);
requestOut->setExplain(explain);
@@ -146,17 +175,17 @@ void makeUpdateRequest(OperationContext* opCtx,
}
void makeDeleteRequest(OperationContext* opCtx,
- const FindAndModifyRequest& args,
+ const write_ops::FindAndModifyCommand& request,
bool explain,
DeleteRequest* requestOut) {
- requestOut->setQuery(args.getQuery());
- requestOut->setProj(args.getFields());
+ requestOut->setQuery(request.getQuery());
+ requestOut->setProj(request.getFields().value_or(BSONObj()));
requestOut->setLegacyRuntimeConstants(
- args.getLegacyRuntimeConstants().value_or(Variables::generateRuntimeConstants(opCtx)));
- requestOut->setLet(args.getLetParameters());
- requestOut->setSort(args.getSort());
- requestOut->setHint(args.getHint());
- requestOut->setCollation(args.getCollation());
+ request.getLegacyRuntimeConstants().value_or(Variables::generateRuntimeConstants(opCtx)));
+ requestOut->setLet(request.getLet());
+ requestOut->setSort(request.getSort().value_or(BSONObj()));
+ requestOut->setHint(request.getHint());
+ requestOut->setCollation(request.getCollation().value_or(BSONObj()));
requestOut->setMulti(false);
requestOut->setReturnDeleted(true); // Always return the old value.
requestOut->setIsExplain(explain);
@@ -264,25 +293,27 @@ public:
}
Status explain(OperationContext* opCtx,
- const OpMsgRequest& request,
+ const OpMsgRequest& opMsgRequest,
ExplainOptions::Verbosity verbosity,
rpc::ReplyBuilderInterface* result) const override {
- std::string dbName = request.getDatabase().toString();
- const BSONObj& cmdObj = request.body;
- const auto args(uassertStatusOK(FindAndModifyRequest::parseFromBSON(
- CommandHelpers::parseNsCollectionRequired(dbName, cmdObj), cmdObj)));
- const NamespaceString& nsString = args.getNamespaceString();
+ std::string dbName = opMsgRequest.getDatabase().toString();
+ const BSONObj& cmdObj = opMsgRequest.body;
+ const auto request(
+ write_ops::FindAndModifyCommand::parse(IDLParserErrorContext("findAndModify"), cmdObj));
+ validate(request);
+
+ const NamespaceString& nsString = request.getNamespace();
uassertStatusOK(userAllowedWriteNS(nsString));
auto const curOp = CurOp::get(opCtx);
OpDebug* const opDebug = &curOp->debug();
- if (args.isRemove()) {
- auto request = DeleteRequest{};
- request.setNsString(nsString);
+ if (request.getRemove().value_or(false)) {
+ auto deleteRequest = DeleteRequest{};
+ deleteRequest.setNsString(nsString);
const bool isExplain = true;
- makeDeleteRequest(opCtx, args, isExplain, &request);
+ makeDeleteRequest(opCtx, request, isExplain, &deleteRequest);
- ParsedDelete parsedDelete(opCtx, &request);
+ ParsedDelete parsedDelete(opCtx, &deleteRequest);
uassertStatusOK(parsedDelete.parseRequest());
// Explain calls of the findAndModify command are read-only, but we take write
@@ -301,12 +332,13 @@ public:
Explain::explainStages(
exec.get(), collection.getCollection(), verbosity, BSONObj(), cmdObj, &bodyBuilder);
} else {
- auto request = UpdateRequest();
- request.setNamespaceString(nsString);
- makeUpdateRequest(opCtx, args, verbosity, &request);
+ auto updateRequest = UpdateRequest();
+ updateRequest.setNamespaceString(nsString);
+ makeUpdateRequest(opCtx, request, verbosity, &updateRequest);
- const ExtensionsCallbackReal extensionsCallback(opCtx, &request.getNamespaceString());
- ParsedUpdate parsedUpdate(opCtx, &request, extensionsCallback);
+ const ExtensionsCallbackReal extensionsCallback(opCtx,
+ &updateRequest.getNamespaceString());
+ ParsedUpdate parsedUpdate(opCtx, &updateRequest, extensionsCallback);
uassertStatusOK(parsedUpdate.parseRequest());
// Explain calls of the findAndModify command are read-only, but we take write
@@ -333,9 +365,11 @@ public:
const std::string& dbName,
const BSONObj& cmdObj,
BSONObjBuilder& result) override {
- const auto args(uassertStatusOK(FindAndModifyRequest::parseFromBSON(
- CommandHelpers::parseNsCollectionRequired(dbName, cmdObj), cmdObj)));
- const NamespaceString& nsString = args.getNamespaceString();
+ const auto request(
+ write_ops::FindAndModifyCommand::parse(IDLParserErrorContext("findAndModify"), cmdObj));
+ validate(request);
+
+ const NamespaceString& nsString = request.getNamespace();
uassertStatusOK(userAllowedWriteNS(nsString));
auto const curOp = CurOp::get(opCtx);
OpDebug* const opDebug = &curOp->debug();
@@ -367,7 +401,7 @@ public:
if (auto entry = txnParticipant.checkStatementExecuted(opCtx, stmtId)) {
RetryableWritesStats::get(opCtx)->incrementRetriedCommandsCount();
RetryableWritesStats::get(opCtx)->incrementRetriedStatementsCount();
- parseOplogEntryForFindAndModify(opCtx, args, *entry, &result);
+ parseOplogEntryForFindAndModify(opCtx, request, *entry, &result);
// Make sure to wait for writeConcern on the opTime that will include this write.
// Needs to set to the system last opTime to get the latest term in an event when
@@ -383,9 +417,16 @@ public:
// executing a findAndModify. This is done to ensure that we can always match, modify, and
// return the document under concurrency, if a matching document exists.
return writeConflictRetry(opCtx, "findAndModify", nsString.ns(), [&] {
- if (args.isRemove()) {
- return writeConflictRetryRemove(
- opCtx, nsString, args, stmtId, curOp, opDebug, inTransaction, cmdObj, result);
+ if (request.getRemove().value_or(false)) {
+ return writeConflictRetryRemove(opCtx,
+ nsString,
+ request,
+ stmtId,
+ curOp,
+ opDebug,
+ inTransaction,
+ cmdObj,
+ result);
} else {
if (MONGO_unlikely(hangBeforeFindAndModifyPerformsUpdate.shouldFail())) {
CurOpFailpointHelpers::waitWhileFailPointEnabled(
@@ -397,24 +438,24 @@ public:
// Nested retry loop to handle concurrent conflicting upserts with equality match.
int retryAttempts = 0;
for (;;) {
- auto request = UpdateRequest();
- request.setNamespaceString(nsString);
+ auto updateRequest = UpdateRequest();
+ updateRequest.setNamespaceString(nsString);
const auto verbosity = boost::none;
- makeUpdateRequest(opCtx, args, verbosity, &request);
+ makeUpdateRequest(opCtx, request, verbosity, &updateRequest);
if (opCtx->getTxnNumber()) {
- request.setStmtId(stmtId);
+ updateRequest.setStmtId(stmtId);
}
- const ExtensionsCallbackReal extensionsCallback(opCtx,
- &request.getNamespaceString());
- ParsedUpdate parsedUpdate(opCtx, &request, extensionsCallback);
+ const ExtensionsCallbackReal extensionsCallback(
+ opCtx, &updateRequest.getNamespaceString());
+ ParsedUpdate parsedUpdate(opCtx, &updateRequest, extensionsCallback);
uassertStatusOK(parsedUpdate.parseRequest());
try {
return writeConflictRetryUpsert(opCtx,
nsString,
- args,
+ request,
curOp,
opDebug,
inTransaction,
@@ -448,23 +489,23 @@ public:
static bool writeConflictRetryRemove(OperationContext* opCtx,
const NamespaceString& nsString,
- const FindAndModifyRequest& args,
+ const write_ops::FindAndModifyCommand& request,
const int stmtId,
CurOp* const curOp,
OpDebug* const opDebug,
const bool inTransaction,
const BSONObj& cmdObj,
BSONObjBuilder& result) {
- auto request = DeleteRequest{};
- request.setNsString(nsString);
+ auto deleteRequest = DeleteRequest{};
+ deleteRequest.setNsString(nsString);
const bool isExplain = false;
- makeDeleteRequest(opCtx, args, isExplain, &request);
+ makeDeleteRequest(opCtx, request, isExplain, &deleteRequest);
if (opCtx->getTxnNumber()) {
- request.setStmtId(stmtId);
+ deleteRequest.setStmtId(stmtId);
}
- ParsedDelete parsedDelete(opCtx, &request);
+ ParsedDelete parsedDelete(opCtx, &deleteRequest);
uassertStatusOK(parsedDelete.parseRequest());
AutoGetCollection collection(opCtx, nsString, MODE_IX);
@@ -488,7 +529,8 @@ public:
CurOp::get(opCtx)->setPlanSummary_inlock(exec->getPlanExplainer().getPlanSummary());
}
- auto docFound = advanceExecutor(opCtx, cmdObj, exec.get(), args.isRemove());
+ auto docFound =
+ advanceExecutor(opCtx, cmdObj, exec.get(), request.getRemove().value_or(false));
// Nothing after advancing the plan executor should throw a WriteConflictException,
// so the following bookkeeping with execution stats won't end up being done
// multiple times.
@@ -511,14 +553,14 @@ public:
}
recordStatsForTopCommand(opCtx);
- appendCommandResponse(exec.get(), args.isRemove(), docFound, &result);
+ appendCommandResponse(exec.get(), request.getRemove().value_or(false), docFound, &result);
return true;
}
static bool writeConflictRetryUpsert(OperationContext* opCtx,
const NamespaceString& nsString,
- const FindAndModifyRequest& args,
+ const write_ops::FindAndModifyCommand& request,
CurOp* const curOp,
OpDebug* const opDebug,
const bool inTransaction,
@@ -543,7 +585,7 @@ public:
// TODO SERVER-50983: Create abstraction for creating collection when using
// AutoGetCollection Create the collection if it does not exist when performing an upsert
// because the update stage does not create its own collection
- if (!*collectionPtr && args.isUpsert()) {
+ if (!*collectionPtr && request.getUpsert() && *request.getUpsert()) {
assertCanWrite(opCtx, nsString);
createdCollection =
@@ -576,7 +618,8 @@ public:
CurOp::get(opCtx)->setPlanSummary_inlock(exec->getPlanExplainer().getPlanSummary());
}
- auto docFound = advanceExecutor(opCtx, cmdObj, exec.get(), args.isRemove());
+ auto docFound =
+ advanceExecutor(opCtx, cmdObj, exec.get(), request.getRemove().value_or(false));
// Nothing after advancing the plan executor should throw a WriteConflictException,
// so the following bookkeeping with execution stats won't end up being done
// multiple times.
@@ -597,7 +640,7 @@ public:
}
recordStatsForTopCommand(opCtx);
- appendCommandResponse(exec.get(), args.isRemove(), docFound, &result);
+ appendCommandResponse(exec.get(), request.getRemove().value_or(false), docFound, &result);
return true;
}
@@ -612,8 +655,9 @@ public:
}();
bob->append("find", cmdObj.firstElement().String());
- if (cmdObj.hasField("query")) {
- bob->append("filter", cmdObj["query"].Obj());
+ auto queryField = cmdObj["query"];
+ if (queryField.type() == BSONType::Object) {
+ bob->append("filter", queryField.Obj());
}
cmdObj.filterFieldsUndotted(bob, kMirrorableKeys, true);
diff --git a/src/mongo/db/ops/SConscript b/src/mongo/db/ops/SConscript
index 3b7ad76017e..c86b8efef22 100644
--- a/src/mongo/db/ops/SConscript
+++ b/src/mongo/db/ops/SConscript
@@ -29,6 +29,7 @@ env.Library(
env.Library(
target='write_ops_parsers',
source=[
+ 'find_and_modify_command.idl',
'write_ops_parsers.cpp',
'write_ops.idl',
],
diff --git a/src/mongo/db/ops/find_and_modify_command.idl b/src/mongo/db/ops/find_and_modify_command.idl
new file mode 100644
index 00000000000..fde8bc3c037
--- /dev/null
+++ b/src/mongo/db/ops/find_and_modify_command.idl
@@ -0,0 +1,112 @@
+# Copyright (C) 2020-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
+# <http://www.mongodb.com/licensing/server-side-public-license>.
+#
+# 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.
+#
+
+global:
+ cpp_namespace: "mongo::write_ops"
+ cpp_includes:
+ - "mongo/db/ops/write_ops_parsers.h"
+
+imports:
+ - "mongo/db/ops/write_ops.idl"
+ - "mongo/db/pipeline/legacy_runtime_constants.idl"
+ - "mongo/idl/basic_types.idl"
+ - "mongo/db/query/hint.idl"
+
+commands:
+ findAndModify:
+ description: "Parser for the 'findAndModify' command."
+ command_name: findAndModify
+ cpp_name: FindAndModifyCommand
+ strict: true
+ namespace: concatenate_with_db
+ fields:
+ query:
+ description: "The query that matches documents to update. Uses the same query
+ selectors as used in the 'find' operation."
+ type: object_owned
+ default: mongo::BSONObj()
+ fields:
+ description: "A subset of fields to return."
+ type: object_owned
+ optional: true
+ sort:
+ description: "Determines which document the operation modifies if the query selects
+ multiple documents."
+ type: object_owned
+ optional: true
+ hint:
+ description: "Specifies the hint to use for the operation."
+ type: indexHint
+ default: mongo::BSONObj()
+ collation:
+ description: "Specifies the collation to use for the operation."
+ type: object
+ optional: true
+ arrayFilters:
+ description: "Specifies which array elements an update modifier should apply to."
+ type: array<object>
+ optional: true
+ remove:
+ description: "Removes the document specified in the query field."
+ # We use 'safeBool' here since the field also allows numeric values.
+ type: safeBool
+ optional: true
+ update:
+ description: "Modification to apply."
+ type: update_modification
+ optional: true
+ upsert:
+ description: "If true, perform an insert if no documents match the query. If both
+ upsert and multi are true and no documents match the query, the update
+ operation inserts only a single document."
+ type: safeBool
+ optional: true
+ new:
+ description: "Removes the document specified in the query field."
+ type: safeBool
+ optional: true
+ bypassDocumentValidation:
+ description: "Enables the operation to bypass document validation. This lets you
+ write documents that do not meet the validation requirements."
+ type: safeBool
+ optional: true
+ let:
+ description: "A set of user-specified constants used by pipeline-style update
+ operations and $expr."
+ type: object
+ optional: true
+ runtimeConstants:
+ description: "A collection of values that do not change once computed. These are
+ used by pipeline-style update operations."
+ cpp_name: legacyRuntimeConstants
+ type: LegacyRuntimeConstants
+ optional: true
+ writeConcern:
+ description: "Describes the write concern."
+ type: object
+ optional: true \ No newline at end of file
diff --git a/src/mongo/db/ops/write_ops.idl b/src/mongo/db/ops/write_ops.idl
index 6fce935cf11..06195f1fe50 100644
--- a/src/mongo/db/ops/write_ops.idl
+++ b/src/mongo/db/ops/write_ops.idl
@@ -219,4 +219,4 @@ commands:
'let' is now preferred."
cpp_name: legacyRuntimeConstants
type: LegacyRuntimeConstants
- optional: true
+ optional: true \ No newline at end of file
diff --git a/src/mongo/db/ops/write_ops_retryability.cpp b/src/mongo/db/ops/write_ops_retryability.cpp
index 252b417d92f..ba12fe0c453 100644
--- a/src/mongo/db/ops/write_ops_retryability.cpp
+++ b/src/mongo/db/ops/write_ops_retryability.cpp
@@ -34,7 +34,7 @@
#include "mongo/db/dbdirectclient.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/ops/find_and_modify_result.h"
-#include "mongo/db/query/find_and_modify_request.h"
+#include "mongo/db/ops/write_ops_gen.h"
#include "mongo/logv2/redaction.h"
namespace mongo {
@@ -45,7 +45,7 @@ namespace {
* In the case of nested oplog entry where the correct links are in the top level
* oplog, oplogWithCorrectLinks can be used to specify the outer oplog.
*/
-void validateFindAndModifyRetryability(const FindAndModifyRequest& request,
+void validateFindAndModifyRetryability(const write_ops::FindAndModifyCommand& request,
const repl::OplogEntry& oplogEntry,
const repl::OplogEntry& oplogWithCorrectLinks) {
auto opType = oplogEntry.getOpType();
@@ -58,7 +58,7 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request,
<< " is not compatible with previous write in the transaction of type: "
<< OpType_serializer(oplogEntry.getOpType()) << ", oplogTs: "
<< ts.toString() << ", oplog: " << redact(oplogEntry.toBSON()),
- request.isRemove());
+ request.getRemove().value_or(false));
uassert(40607,
str::stream() << "No pre-image available for findAndModify retry request:"
<< redact(request.toBSON({})),
@@ -70,7 +70,7 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request,
<< " is not compatible with previous write in the transaction of type: "
<< OpType_serializer(oplogEntry.getOpType()) << ", oplogTs: "
<< ts.toString() << ", oplog: " << redact(oplogEntry.toBSON()),
- request.isUpsert());
+ request.getUpsert().value_or(false));
} else {
uassert(
40609,
@@ -80,7 +80,7 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request,
<< ts.toString() << ", oplog: " << redact(oplogEntry.toBSON()),
opType == repl::OpTypeEnum::kUpdate);
- if (request.shouldReturnNew()) {
+ if (request.getNew().value_or(false)) {
uassert(40611,
str::stream() << "findAndModify retry request: " << redact(request.toBSON({}))
<< " wants the document after update returned, but only before "
@@ -127,7 +127,7 @@ BSONObj extractPreOrPostImage(OperationContext* opCtx, const repl::OplogEntry& o
* are in the top level oplog, oplogWithCorrectLinks can be used to specify the outer oplog.
*/
void parseOplogEntryForFindAndModify(OperationContext* opCtx,
- const FindAndModifyRequest& request,
+ const write_ops::FindAndModifyCommand& request,
const repl::OplogEntry& oplogEntry,
const repl::OplogEntry& oplogWithCorrectLinks,
BSONObjBuilder* builder) {
@@ -135,12 +135,13 @@ void parseOplogEntryForFindAndModify(OperationContext* opCtx,
switch (oplogEntry.getOpType()) {
case repl::OpTypeEnum::kInsert:
- return find_and_modify::serializeUpsert(
- 1,
- request.shouldReturnNew() ? oplogEntry.getObject() : boost::optional<BSONObj>(),
- false,
- oplogEntry.getObject()["_id"],
- builder);
+ return find_and_modify::serializeUpsert(1,
+ request.getNew().value_or(false)
+ ? oplogEntry.getObject()
+ : boost::optional<BSONObj>(),
+ false,
+ oplogEntry.getObject()["_id"],
+ builder);
case repl::OpTypeEnum::kUpdate:
return find_and_modify::serializeUpsert(
1, extractPreOrPostImage(opCtx, oplogWithCorrectLinks), true, {}, builder);
@@ -191,7 +192,7 @@ SingleWriteResult parseOplogEntryForUpdate(const repl::OplogEntry& entry) {
}
void parseOplogEntryForFindAndModify(OperationContext* opCtx,
- const FindAndModifyRequest& request,
+ const write_ops::FindAndModifyCommand& request,
const repl::OplogEntry& oplogEntry,
BSONObjBuilder* builder) {
// Migrated op case.
diff --git a/src/mongo/db/ops/write_ops_retryability.h b/src/mongo/db/ops/write_ops_retryability.h
index 1b866dc131c..1f57b19299b 100644
--- a/src/mongo/db/ops/write_ops_retryability.h
+++ b/src/mongo/db/ops/write_ops_retryability.h
@@ -29,14 +29,13 @@
#pragma once
+#include "mongo/db/ops/find_and_modify_command_gen.h"
#include "mongo/db/ops/single_write_result_gen.h"
-#include "mongo/db/ops/write_ops.h"
#include "mongo/db/repl/oplog_entry.h"
namespace mongo {
class BSONObjBuilder;
-class FindAndModifyRequest;
class OperationContext;
/**
@@ -52,7 +51,7 @@ SingleWriteResult parseOplogEntryForUpdate(const repl::OplogEntry& entry);
* generated by the operation.
*/
void parseOplogEntryForFindAndModify(OperationContext* opCtx,
- const FindAndModifyRequest& request,
+ const write_ops::FindAndModifyCommand& request,
const repl::OplogEntry& oplogEntry,
BSONObjBuilder* builder);
diff --git a/src/mongo/db/ops/write_ops_retryability_test.cpp b/src/mongo/db/ops/write_ops_retryability_test.cpp
index fcdef0c6845..61738479b60 100644
--- a/src/mongo/db/ops/write_ops_retryability_test.cpp
+++ b/src/mongo/db/ops/write_ops_retryability_test.cpp
@@ -37,7 +37,6 @@
#include "mongo/db/ops/write_ops_exec.h"
#include "mongo/db/ops/write_ops_gen.h"
#include "mongo/db/ops/write_ops_retryability.h"
-#include "mongo/db/query/find_and_modify_request.h"
#include "mongo/db/repl/mock_repl_coord_server_fixture.h"
#include "mongo/db/repl/oplog_entry.h"
#include "mongo/db/repl/repl_client_info.h"
@@ -99,6 +98,16 @@ void setUpTxnParticipant(OperationContext* opCtx, std::vector<int> executedStmtI
txnPart.setCommittedStmtIdsForTest(std::move(executedStmtIds));
}
+write_ops::FindAndModifyCommand makeFindAndModifyRequest(
+ NamespaceString fullNs, BSONObj query, boost::optional<write_ops::UpdateModification> update) {
+ auto request = write_ops::FindAndModifyCommand(fullNs);
+ request.setQuery(query);
+ if (update) {
+ request.setUpdate(std::move(update));
+ }
+ return request;
+}
+
TEST_F(WriteOpsRetryability, ParseOplogEntryForUpdate) {
const auto entry = assertGet(repl::OplogEntry::parse(
BSON("ts" << Timestamp(50, 10) << "t" << 1LL << "op"
@@ -343,7 +352,7 @@ protected:
* BSONObjBuilder.
*/
static BSONObj constructFindAndModifyRetryResult(OperationContext* opCtx,
- const FindAndModifyRequest& request,
+ const write_ops::FindAndModifyCommand& request,
const repl::OplogEntry& oplogEntry) {
BSONObjBuilder builder;
parseOplogEntryForFindAndModify(opCtx, request, oplogEntry, &builder);
@@ -354,10 +363,10 @@ protected:
const NamespaceString kNs("test.user");
TEST_F(FindAndModifyRetryability, BasicUpsertReturnNew) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
request.setUpsert(true);
- request.setShouldReturnNew(true);
+ request.setNew(true);
auto insertOplog = makeOplogEntry(repl::OpTime(), // optime
repl::OpTypeEnum::kInsert, // op type
@@ -378,10 +387,10 @@ TEST_F(FindAndModifyRetryability, BasicUpsertReturnNew) {
}
TEST_F(FindAndModifyRetryability, BasicUpsertReturnOld) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
request.setUpsert(true);
- request.setShouldReturnNew(false);
+ request.setNew(false);
auto insertOplog = makeOplogEntry(repl::OpTime(), // optime
repl::OpTypeEnum::kInsert, // op type
@@ -399,10 +408,10 @@ TEST_F(FindAndModifyRetryability, BasicUpsertReturnOld) {
}
TEST_F(FindAndModifyRetryability, NestedUpsert) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
request.setUpsert(true);
- request.setShouldReturnNew(true);
+ request.setNew(true);
auto innerOplog = makeOplogEntry(repl::OpTime(), // optime
repl::OpTypeEnum::kInsert, // op type
@@ -422,7 +431,7 @@ TEST_F(FindAndModifyRetryability, NestedUpsert) {
}
TEST_F(FindAndModifyRetryability, AttemptingToRetryUpsertWithUpdateWithoutUpsertErrors) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
request.setUpsert(false);
@@ -436,9 +445,9 @@ TEST_F(FindAndModifyRetryability, AttemptingToRetryUpsertWithUpdateWithoutUpsert
}
TEST_F(FindAndModifyRetryability, ErrorIfRequestIsPostImageButOplogHasPre) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
- request.setShouldReturnNew(true);
+ request.setNew(true);
repl::OpTime imageOpTime(Timestamp(120, 3), 1);
auto noteOplog = makeOplogEntry(imageOpTime, // optime
@@ -461,9 +470,9 @@ TEST_F(FindAndModifyRetryability, ErrorIfRequestIsPostImageButOplogHasPre) {
}
TEST_F(FindAndModifyRetryability, ErrorIfRequestIsUpdateButOplogIsDelete) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
- request.setShouldReturnNew(true);
+ request.setNew(true);
repl::OpTime imageOpTime(Timestamp(120, 3), 1);
auto noteOplog = makeOplogEntry(imageOpTime, // optime
@@ -485,9 +494,9 @@ TEST_F(FindAndModifyRetryability, ErrorIfRequestIsUpdateButOplogIsDelete) {
}
TEST_F(FindAndModifyRetryability, ErrorIfRequestIsPreImageButOplogHasPost) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
- request.setShouldReturnNew(false);
+ request.setNew(false);
repl::OpTime imageOpTime(Timestamp(120, 3), 1);
auto noteOplog = makeOplogEntry(imageOpTime, // optime
@@ -510,9 +519,9 @@ TEST_F(FindAndModifyRetryability, ErrorIfRequestIsPreImageButOplogHasPost) {
}
TEST_F(FindAndModifyRetryability, UpdateWithPreImage) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
- request.setShouldReturnNew(false);
+ request.setNew(false);
repl::OpTime imageOpTime(Timestamp(120, 3), 1);
auto noteOplog = makeOplogEntry(imageOpTime, // optime
@@ -537,9 +546,9 @@ TEST_F(FindAndModifyRetryability, UpdateWithPreImage) {
}
TEST_F(FindAndModifyRetryability, NestedUpdateWithPreImage) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
- request.setShouldReturnNew(false);
+ request.setNew(false);
repl::OpTime imageOpTime(Timestamp(120, 3), 1);
auto noteOplog = makeOplogEntry(imageOpTime, // optime
@@ -570,9 +579,9 @@ TEST_F(FindAndModifyRetryability, NestedUpdateWithPreImage) {
}
TEST_F(FindAndModifyRetryability, UpdateWithPostImage) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
- request.setShouldReturnNew(true);
+ request.setNew(true);
repl::OpTime imageOpTime(Timestamp(120, 3), 1);
auto noteOplog = makeOplogEntry(imageOpTime, // optime
@@ -597,9 +606,9 @@ TEST_F(FindAndModifyRetryability, UpdateWithPostImage) {
}
TEST_F(FindAndModifyRetryability, NestedUpdateWithPostImage) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
- request.setShouldReturnNew(true);
+ request.setNew(true);
repl::OpTime imageOpTime(Timestamp(120, 3), 1);
auto noteOplog = makeOplogEntry(imageOpTime, // optime
@@ -630,9 +639,9 @@ TEST_F(FindAndModifyRetryability, NestedUpdateWithPostImage) {
}
TEST_F(FindAndModifyRetryability, UpdateWithPostImageButOplogDoesNotExistShouldError) {
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
kNs, BSONObj(), write_ops::UpdateModification::parseFromClassicUpdate(BSONObj()));
- request.setShouldReturnNew(true);
+ request.setNew(true);
repl::OpTime imageOpTime(Timestamp(120, 3), 1);
auto updateOplog = makeOplogEntry(repl::OpTime(), // optime
@@ -648,7 +657,8 @@ TEST_F(FindAndModifyRetryability, UpdateWithPostImageButOplogDoesNotExistShouldE
}
TEST_F(FindAndModifyRetryability, BasicRemove) {
- auto request = FindAndModifyRequest::makeRemove(kNs, BSONObj());
+ auto request = makeFindAndModifyRequest(kNs, BSONObj(), boost::none);
+ request.setRemove(true);
repl::OpTime imageOpTime(Timestamp(120, 3), 1);
auto noteOplog = makeOplogEntry(imageOpTime, // optime
@@ -673,7 +683,8 @@ TEST_F(FindAndModifyRetryability, BasicRemove) {
}
TEST_F(FindAndModifyRetryability, NestedRemove) {
- auto request = FindAndModifyRequest::makeRemove(kNs, BSONObj());
+ auto request = makeFindAndModifyRequest(kNs, BSONObj(), boost::none);
+ request.setRemove(true);
repl::OpTime imageOpTime(Timestamp(120, 3), 1);
auto noteOplog = makeOplogEntry(imageOpTime, // optime
@@ -703,7 +714,7 @@ TEST_F(FindAndModifyRetryability, NestedRemove) {
}
TEST_F(FindAndModifyRetryability, AttemptingToRetryUpsertWithRemoveErrors) {
- auto request = FindAndModifyRequest::makeRemove(kNs, BSONObj());
+ auto request = makeFindAndModifyRequest(kNs, BSONObj(), boost::none);
auto insertOplog = makeOplogEntry(repl::OpTime(), // optime
repl::OpTypeEnum::kInsert, // op type
diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript
index 54f0300bf7c..16af82ee571 100644
--- a/src/mongo/db/query/SConscript
+++ b/src/mongo/db/query/SConscript
@@ -162,7 +162,6 @@ env.Library(
"count_request.cpp",
'cursor_request.cpp',
'cursor_response.cpp',
- 'find_and_modify_request.cpp',
'getmore_request.cpp',
'killcursors_response.cpp',
'view_response_formatter.cpp',
@@ -297,7 +296,6 @@ env.CppUnitTest(
"count_command_test.cpp",
"cursor_response_test.cpp",
"explain_options_test.cpp",
- "find_and_modify_request_test.cpp",
"get_executor_test.cpp",
"getmore_request_test.cpp",
"hint_parser_test.cpp",
diff --git a/src/mongo/db/query/find_and_modify_request.cpp b/src/mongo/db/query/find_and_modify_request.cpp
deleted file mode 100644
index 5662b517286..00000000000
--- a/src/mongo/db/query/find_and_modify_request.cpp
+++ /dev/null
@@ -1,441 +0,0 @@
-/**
- * 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
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * 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.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/query/find_and_modify_request.h"
-
-#include "mongo/base/status_with.h"
-#include "mongo/bson/bsonobjbuilder.h"
-#include "mongo/bson/util/bson_extract.h"
-#include "mongo/db/query/hint_parser.h"
-#include "mongo/db/write_concern.h"
-#include "mongo/idl/command_generic_argument.h"
-#include "mongo/idl/idl_parser.h"
-
-namespace mongo {
-
-namespace {
-const char kQueryField[] = "query";
-const char kSortField[] = "sort";
-const char kHintField[] = "hint";
-const char kCollationField[] = "collation";
-const char kArrayFiltersField[] = "arrayFilters";
-const char kLegacyRuntimeConstantsField[] = "runtimeConstants";
-const char kLetField[] = "let";
-const char kRemoveField[] = "remove";
-const char kUpdateField[] = "update";
-const char kNewField[] = "new";
-const char kFieldProjectionField[] = "fields";
-const char kUpsertField[] = "upsert";
-const char kWriteConcernField[] = "writeConcern";
-const char kBypassDocumentValidationField[] = "bypassDocumentValidation";
-
-const std::vector<BSONObj> emptyArrayFilters{};
-
-const std::vector<StringData> _knownFields{kQueryField,
- kSortField,
- kCollationField,
- kArrayFiltersField,
- kRemoveField,
- kUpdateField,
- kNewField,
- kFieldProjectionField,
- kUpsertField,
- kWriteConcernField,
- kBypassDocumentValidationField,
- FindAndModifyRequest::kLegacyCommandName,
- FindAndModifyRequest::kCommandName};
-} // unnamed namespace
-
-FindAndModifyRequest::FindAndModifyRequest(NamespaceString fullNs,
- BSONObj query,
- boost::optional<write_ops::UpdateModification> update)
- : _ns(std::move(fullNs)), _query(query.getOwned()), _update(std::move(update)) {}
-
-FindAndModifyRequest FindAndModifyRequest::makeUpdate(NamespaceString fullNs,
- BSONObj query,
- write_ops::UpdateModification update) {
- return FindAndModifyRequest(fullNs, query, std::move(update));
-}
-
-FindAndModifyRequest FindAndModifyRequest::makeRemove(NamespaceString fullNs, BSONObj query) {
- FindAndModifyRequest request(fullNs, query, {});
- return request;
-}
-
-BSONObj FindAndModifyRequest::toBSON(const BSONObj& commandPassthroughFields) const {
- BSONObjBuilder builder;
-
- builder.append(kCommandName, _ns.coll());
- builder.append(kQueryField, _query);
-
- if (_update) {
- _update->serializeToBSON(kUpdateField, &builder);
-
- if (_isUpsert) {
- builder.append(kUpsertField, _isUpsert);
- }
- } else {
- builder.append(kRemoveField, true);
- }
-
- if (_fieldProjection) {
- builder.append(kFieldProjectionField, _fieldProjection.get());
- }
-
- if (_sort) {
- builder.append(kSortField, _sort.get());
- }
-
- if (_hint) {
- builder.append(kHintField, _hint.get());
- }
-
- if (_collation) {
- builder.append(kCollationField, _collation.get());
- }
-
- if (_arrayFilters) {
- BSONArrayBuilder arrayBuilder(builder.subarrayStart(kArrayFiltersField));
- for (auto arrayFilter : _arrayFilters.get()) {
- arrayBuilder.append(arrayFilter);
- }
- arrayBuilder.doneFast();
- }
-
- if (_legacyRuntimeConstants) {
- BSONObjBuilder rtcBuilder(builder.subobjStart(kLegacyRuntimeConstantsField));
- _legacyRuntimeConstants->serialize(&rtcBuilder);
- rtcBuilder.doneFast();
- }
-
- if (_letParameters) {
- if (auto letParams = _letParameters.get(); !letParams.isEmpty()) {
- builder.append(kLetField, _letParameters.get());
- }
- }
-
- if (_shouldReturnNew) {
- builder.append(kNewField, _shouldReturnNew);
- }
-
- if (_writeConcern) {
- builder.append(kWriteConcernField, _writeConcern->toBSON());
- }
-
- if (_bypassDocumentValidation) {
- builder.append(kBypassDocumentValidationField, _bypassDocumentValidation);
- }
-
- IDLParserErrorContext::appendGenericCommandArguments(
- commandPassthroughFields, _knownFields, &builder);
-
- return builder.obj();
-}
-
-StatusWith<FindAndModifyRequest> FindAndModifyRequest::parseFromBSON(NamespaceString fullNs,
- const BSONObj& cmdObj) {
- BSONObj query;
- BSONObj fields;
- BSONObj updateObj;
- BSONObj sort;
- BSONObj hint;
- boost::optional<write_ops::UpdateModification> update;
-
- BSONObj collation;
- bool shouldReturnNew = false;
- bool isUpsert = false;
- bool isRemove = false;
- bool bypassDocumentValidation = false;
- bool arrayFiltersSet = false;
- std::vector<BSONObj> arrayFilters;
- boost::optional<LegacyRuntimeConstants> runtimeConstants;
- BSONObj letParameters;
- bool writeConcernOptionsSet = false;
- WriteConcernOptions writeConcernOptions;
-
- for (auto&& field : cmdObj.getFieldNames<std::set<std::string>>()) {
- if (field == kQueryField) {
- auto queryElement = cmdObj[kQueryField];
- if (queryElement.type() != Object) {
- return {ErrorCodes::Error(31160),
- str::stream()
- << "'" << kQueryField << "' parameter must be an object, found "
- << queryElement.type()};
- }
- query = queryElement.embeddedObject();
- } else if (field == kSortField) {
- auto sortElement = cmdObj[kSortField];
- if (sortElement.type() != Object) {
- return {ErrorCodes::Error(31174),
- str::stream()
- << "'" << kSortField << "' parameter must be an object, found "
- << sortElement.type()};
- }
- sort = sortElement.embeddedObject();
- } else if (field == kHintField) {
- hint = parseHint(cmdObj[kHintField]);
- } else if (field == kRemoveField) {
- isRemove = cmdObj[kRemoveField].trueValue();
- } else if (field == kUpdateField) {
- update = write_ops::UpdateModification::parseFromBSON(cmdObj[kUpdateField]);
- } else if (field == kNewField) {
- shouldReturnNew = cmdObj[kNewField].trueValue();
- } else if (field == kFieldProjectionField) {
- auto projectionElement = cmdObj[kFieldProjectionField];
- if (projectionElement.type() != Object) {
- return {ErrorCodes::Error(31175),
- str::stream()
- << "'" << kFieldProjectionField
- << "' parameter must be an object, found " << projectionElement.type()};
- }
- fields = projectionElement.embeddedObject();
- } else if (field == kUpsertField) {
- isUpsert = cmdObj[kUpsertField].trueValue();
- } else if (field == kBypassDocumentValidationField) {
- bypassDocumentValidation = cmdObj[kBypassDocumentValidationField].trueValue();
- } else if (field == kCollationField) {
- BSONElement collationElt;
- Status collationEltStatus =
- bsonExtractTypedField(cmdObj, kCollationField, BSONType::Object, &collationElt);
- if (!collationEltStatus.isOK() && (collationEltStatus != ErrorCodes::NoSuchKey)) {
- return collationEltStatus;
- }
- if (collationEltStatus.isOK()) {
- collation = collationElt.embeddedObject();
- }
- } else if (field == kArrayFiltersField) {
- BSONElement arrayFiltersElt;
- Status arrayFiltersEltStatus = bsonExtractTypedField(
- cmdObj, kArrayFiltersField, BSONType::Array, &arrayFiltersElt);
- if (!arrayFiltersEltStatus.isOK() && (arrayFiltersEltStatus != ErrorCodes::NoSuchKey)) {
- return arrayFiltersEltStatus;
- }
- if (arrayFiltersEltStatus.isOK()) {
- arrayFiltersSet = true;
- for (auto arrayFilter : arrayFiltersElt.embeddedObject()) {
- if (arrayFilter.type() != BSONType::Object) {
- return {ErrorCodes::TypeMismatch,
- str::stream() << "Each array filter must be an object, found "
- << arrayFilter.type()};
- }
- arrayFilters.push_back(arrayFilter.embeddedObject());
- }
- }
- } else if (field == kLegacyRuntimeConstantsField) {
- runtimeConstants =
- LegacyRuntimeConstants::parse(IDLParserErrorContext(kLegacyRuntimeConstantsField),
- cmdObj.getObjectField(kLegacyRuntimeConstantsField));
- } else if (field == kLetField) {
- BSONElement letElt;
- if (Status letEltStatus =
- bsonExtractTypedField(cmdObj, kLetField, BSONType::Object, &letElt);
- !letEltStatus.isOK()) {
- return letEltStatus;
- }
- letParameters = letElt.embeddedObject();
- } else if (field == kWriteConcernField) {
- BSONElement writeConcernElt;
- Status writeConcernEltStatus = bsonExtractTypedField(
- cmdObj, kWriteConcernField, BSONType::Object, &writeConcernElt);
- if (!writeConcernEltStatus.isOK()) {
- return writeConcernEltStatus;
- }
- auto sw = WriteConcernOptions::parse(writeConcernElt.embeddedObject());
- if (!sw.isOK()) {
- return sw.getStatus();
- } else {
- writeConcernOptionsSet = true;
- writeConcernOptions = sw.getValue();
- }
- } else if (isMongocryptdArgument(field)) {
- return {ErrorCodes::FailedToParse,
- str::stream() << "unrecognized field '" << field
- << "'. This command may be meant for a mongocryptd process."};
- } else if (!isGenericArgument(field) &&
- !std::count(_knownFields.begin(), _knownFields.end(), field)) {
- return {ErrorCodes::Error(51177),
- str::stream() << "BSON field '" << field << "' is an unknown field."};
- }
- }
-
- if (!isRemove && !update) {
- return {ErrorCodes::FailedToParse, "Either an update or remove=true must be specified"};
- }
-
- if (isRemove) {
- if (update) {
- return {ErrorCodes::FailedToParse, "Cannot specify both an update and remove=true"};
- }
-
- if (isUpsert) {
- return {ErrorCodes::FailedToParse, "Cannot specify both upsert=true and remove=true"};
- }
-
- if (shouldReturnNew) {
- return {ErrorCodes::FailedToParse,
- "Cannot specify both new=true and remove=true;"
- " 'remove' always returns the deleted document"};
- }
-
- if (arrayFiltersSet) {
- return {ErrorCodes::FailedToParse, "Cannot specify arrayFilters and remove=true"};
- }
- }
-
- if (update && update->type() == write_ops::UpdateModification::Type::kPipeline &&
- arrayFiltersSet) {
- return {ErrorCodes::FailedToParse, "Cannot specify arrayFilters and a pipeline update"};
- }
-
- FindAndModifyRequest request(std::move(fullNs), query, std::move(update));
- request.setFieldProjection(fields);
- request.setSort(sort);
- request.setHint(hint);
- request.setCollation(collation);
- request.setBypassDocumentValidation(bypassDocumentValidation);
- request.setLetParameters(letParameters);
- if (arrayFiltersSet) {
- request.setArrayFilters(std::move(arrayFilters));
- }
- if (runtimeConstants) {
- request.setLegacyRuntimeConstants(*runtimeConstants);
- }
- if (writeConcernOptionsSet) {
- request.setWriteConcern(std::move(writeConcernOptions));
- }
-
- if (!isRemove) {
- request.setShouldReturnNew(shouldReturnNew);
- request.setUpsert(isUpsert);
- }
-
- return request;
-}
-
-void FindAndModifyRequest::setFieldProjection(BSONObj fields) {
- _fieldProjection = fields.getOwned();
-}
-
-void FindAndModifyRequest::setSort(BSONObj sort) {
- _sort = sort.getOwned();
-}
-
-void FindAndModifyRequest::setHint(BSONObj hint) {
- _hint = hint.getOwned();
-}
-
-void FindAndModifyRequest::setCollation(BSONObj collation) {
- _collation = collation.getOwned();
-}
-
-void FindAndModifyRequest::setArrayFilters(const std::vector<BSONObj>& arrayFilters) {
- _arrayFilters = std::vector<BSONObj>();
- for (auto arrayFilter : arrayFilters) {
- _arrayFilters->emplace_back(arrayFilter.getOwned());
- }
-}
-
-void FindAndModifyRequest::setQuery(BSONObj query) {
- _query = query.getOwned();
-}
-void FindAndModifyRequest::setUpdateObj(BSONObj updateObj) {
- _update.emplace(write_ops::UpdateModification::parseFromClassicUpdate(updateObj.getOwned()));
-}
-
-void FindAndModifyRequest::setShouldReturnNew(bool shouldReturnNew) {
- dassert(_update);
- _shouldReturnNew = shouldReturnNew;
-}
-
-void FindAndModifyRequest::setUpsert(bool upsert) {
- dassert(_update);
- _isUpsert = upsert;
-}
-
-void FindAndModifyRequest::setWriteConcern(WriteConcernOptions writeConcern) {
- _writeConcern = std::move(writeConcern);
-}
-
-void FindAndModifyRequest::setBypassDocumentValidation(bool bypassDocumentValidation) {
- _bypassDocumentValidation = bypassDocumentValidation;
-}
-
-const NamespaceString& FindAndModifyRequest::getNamespaceString() const {
- return _ns;
-}
-
-BSONObj FindAndModifyRequest::getQuery() const {
- return _query;
-}
-
-BSONObj FindAndModifyRequest::getFields() const {
- return _fieldProjection.value_or(BSONObj());
-}
-
-const boost::optional<write_ops::UpdateModification>& FindAndModifyRequest::getUpdate() const {
- return _update;
-}
-
-BSONObj FindAndModifyRequest::getSort() const {
- return _sort.value_or(BSONObj());
-}
-
-BSONObj FindAndModifyRequest::getHint() const {
- return _hint.value_or(BSONObj());
-}
-
-BSONObj FindAndModifyRequest::getCollation() const {
- return _collation.value_or(BSONObj());
-}
-
-const std::vector<BSONObj>& FindAndModifyRequest::getArrayFilters() const {
- if (_arrayFilters) {
- return _arrayFilters.get();
- }
- return emptyArrayFilters;
-}
-
-bool FindAndModifyRequest::shouldReturnNew() const {
- return _shouldReturnNew;
-}
-
-bool FindAndModifyRequest::isUpsert() const {
- return _isUpsert;
-}
-
-bool FindAndModifyRequest::isRemove() const {
- return !static_cast<bool>(_update);
-}
-
-bool FindAndModifyRequest::getBypassDocumentValidation() const {
- return _bypassDocumentValidation;
-}
-} // namespace mongo
diff --git a/src/mongo/db/query/find_and_modify_request.h b/src/mongo/db/query/find_and_modify_request.h
deleted file mode 100644
index 2f622d0f9bf..00000000000
--- a/src/mongo/db/query/find_and_modify_request.h
+++ /dev/null
@@ -1,236 +0,0 @@
-/**
- * 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
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * 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 <boost/optional.hpp>
-
-#include "mongo/db/jsobj.h"
-#include "mongo/db/namespace_string.h"
-#include "mongo/db/ops/write_ops_parsers.h"
-#include "mongo/db/pipeline/legacy_runtime_constants_gen.h"
-#include "mongo/db/write_concern_options.h"
-
-namespace mongo {
-
-template <typename T>
-class StatusWith;
-
-/**
- * Represents the user-supplied options to the findAndModify command. Note that this
- * does not offer round trip preservation. For example, for the case where
- * output = parseBSON(input).toBSON(), 'output' is not guaranteed to be equal to 'input'.
- * However, the semantic meaning of 'output' will be the same with 'input'.
- *
- * The BSONObj members contained within this struct are owned objects.
- */
-class FindAndModifyRequest {
-public:
- static constexpr auto kLegacyCommandName = "findandmodify"_sd;
- static constexpr auto kCommandName = "findAndModify"_sd;
-
- /**
- * Creates a new instance of a 'update' type findAndModify request.
- */
- static FindAndModifyRequest makeUpdate(NamespaceString fullNs,
- BSONObj query,
- write_ops::UpdateModification update);
-
- /**
- * Creates a new instance of an 'remove' type findAndModify request.
- */
- static FindAndModifyRequest makeRemove(NamespaceString fullNs, BSONObj query);
-
- /**
- * Create a new instance of FindAndModifyRequest from a valid BSONObj.
- * Returns an error if the BSONObj is malformed.
- * Format:
- *
- * {
- * findAndModify: <collection-name>,
- * query: <document>,
- * sort: <document>,
- * collation: <document>,
- * arrayFilters: <array>,
- * remove: <boolean>,
- * update: <document>,
- * new: <boolean>,
- * fields: <document>,
- * upsert: <boolean>
- * }
- *
- * Note: does not parse the writeConcern field or the findAndModify field.
- */
- static StatusWith<FindAndModifyRequest> parseFromBSON(NamespaceString fullNs,
- const BSONObj& cmdObj);
-
- /**
- * Serializes this object into a BSON representation. Fields that are not
- * set will not be part of the the serialized object. Passthrough fields
- * are appended.
- */
- BSONObj toBSON(const BSONObj& commandPassthroughFields) const;
-
- const NamespaceString& getNamespaceString() const;
- BSONObj getQuery() const;
- BSONObj getFields() const;
- const boost::optional<write_ops::UpdateModification>& getUpdate() const;
- BSONObj getSort() const;
- BSONObj getHint() const;
- BSONObj getCollation() const;
- const std::vector<BSONObj>& getArrayFilters() const;
- bool shouldReturnNew() const;
- bool isUpsert() const;
- bool isRemove() const;
- bool getBypassDocumentValidation() const;
-
- // Not implemented. Use extractWriteConcern() to get the setting instead.
- WriteConcernOptions getWriteConcern() const;
-
- //
- // Setters for update type request only.
- //
-
- /**
- * Sets the filter to find a document.
- */
- void setQuery(BSONObj query);
-
- /**
- * Sets the update object that specifies how a document gets updated.
- */
- void setUpdateObj(BSONObj updateObj);
-
- /**
- * If shouldReturnNew is new, the findAndModify response should return the document
- * after the modification was applied if the query matched a document. Otherwise,
- * it will return the matched document before the modification.
- */
- void setShouldReturnNew(bool shouldReturnNew);
-
- /**
- * Sets a flag whether the statement performs an upsert.
- */
- void setUpsert(bool upsert);
-
- //
- // Setters for optional parameters
- //
-
- /**
- * Specifies the field to project on the matched document.
- */
- void setFieldProjection(BSONObj fields);
-
- /**
- * Sets the sort order for the query. In cases where the query yields multiple matches,
- * only the first document based on the sort order will be modified/removed.
- */
- void setSort(BSONObj sort);
-
- /**
- * Sets a hint on the query, which will force the planner to use the specified index.
- */
- void setHint(BSONObj hint);
-
- /**
- * Sets the collation for the query, which is used for all string comparisons.
- */
- void setCollation(BSONObj collation);
-
- /**
- * Sets the array filters for the update, which determine which array elements should be
- * modified.
- */
- void setArrayFilters(const std::vector<BSONObj>& arrayFilters);
-
- /**
- * Sets any constant values which may be required by the query and/or update.
- */
- void setLegacyRuntimeConstants(LegacyRuntimeConstants runtimeConstants) {
- _legacyRuntimeConstants = std::move(runtimeConstants);
- }
-
- /**
- * Returns the runtime constants associated with this findAndModify request, if present.
- */
- const boost::optional<LegacyRuntimeConstants>& getLegacyRuntimeConstants() const {
- return _legacyRuntimeConstants;
- }
-
- /**
- * Sets user-specified parameters that can be used by the findAndModify command's pipeline-style
- * updates and inside $expr.
- */
- void setLetParameters(const boost::optional<BSONObj>& letParameters) {
- _letParameters = letParameters;
- }
-
- /**
- * Returns the runtime constants associated with this findAndModify request, if present.
- */
- const boost::optional<BSONObj>& getLetParameters() const {
- return _letParameters;
- }
-
- /**
- * Sets the write concern for this request.
- */
- void setWriteConcern(WriteConcernOptions writeConcern);
-
- void setBypassDocumentValidation(bool bypassDocumentValidation);
-
-private:
- /**
- * Creates a new FindAndModifyRequest with the required fields.
- */
- FindAndModifyRequest(NamespaceString fullNs,
- BSONObj query,
- boost::optional<write_ops::UpdateModification> update);
-
- // Required fields
- const NamespaceString _ns;
- BSONObj _query;
-
- bool _isUpsert{false};
- boost::optional<BSONObj> _fieldProjection;
- boost::optional<BSONObj> _sort;
- boost::optional<BSONObj> _hint;
- boost::optional<BSONObj> _collation;
- boost::optional<std::vector<BSONObj>> _arrayFilters;
- boost::optional<LegacyRuntimeConstants> _legacyRuntimeConstants;
- boost::optional<BSONObj> _letParameters;
- bool _shouldReturnNew{false};
- boost::optional<WriteConcernOptions> _writeConcern;
- bool _bypassDocumentValidation{false};
-
- // Holds value when performing an update request and none when a remove request.
- boost::optional<write_ops::UpdateModification> _update;
-};
-} // namespace mongo
diff --git a/src/mongo/db/query/find_and_modify_request_test.cpp b/src/mongo/db/query/find_and_modify_request_test.cpp
deleted file mode 100644
index 5f4e4f64145..00000000000
--- a/src/mongo/db/query/find_and_modify_request_test.cpp
+++ /dev/null
@@ -1,786 +0,0 @@
-/**
- * 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
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * 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.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/bson/json.h"
-#include "mongo/db/commands/test_commands_enabled.h"
-#include "mongo/db/pipeline/legacy_runtime_constants_gen.h"
-#include "mongo/db/query/find_and_modify_request.h"
-#include "mongo/unittest/unittest.h"
-
-namespace mongo {
-namespace {
-
-TEST(FindAndModifyRequest, BasicUpdate) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, PipelineUpdate) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj pipelineBSON(
- BSON("pipeline" << BSON_ARRAY(BSON("$addFields" << BSON("y" << 1)))));
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"), query, pipelineBSON["pipeline"]);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: [{$addFields: {y: 1}}]
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, UpdateWithUpsert) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setUpsert(true);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 },
- upsert: true
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, UpdateWithUpsertFalse) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setUpsert(false);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, UpdateWithProjection) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
- const BSONObj field(BSON("z" << 1));
-
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setFieldProjection(field);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 },
- fields: { z: 1 }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, UpdateWithNewTrue) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
-
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setShouldReturnNew(true);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 },
- new: true
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, UpdateWithNewFalse) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
-
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setShouldReturnNew(false);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, UpdateWithSort) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
- const BSONObj sort(BSON("z" << -1));
-
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setSort(sort);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 },
- sort: { z: -1 }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, UpdateWithHint) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
- const BSONObj hint(BSON("z" << -1));
-
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setHint(hint);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 },
- hint: { z: -1 }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, UpdateWithCollation) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
- const BSONObj collation(BSON("locale"
- << "en_US"));
-
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setCollation(collation);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 },
- collation: { locale: 'en_US' }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, UpdateWithArrayFilters) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
- const std::vector<BSONObj> arrayFilters{BSON("i" << 0)};
-
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setArrayFilters(arrayFilters);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 },
- arrayFilters: [ { i: 0 } ]
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, UpdateWithWriteConcern) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
- const WriteConcernOptions writeConcern(2, WriteConcernOptions::SyncMode::FSYNC, 150);
-
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setWriteConcern(writeConcern);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 },
- writeConcern: { w: 2, fsync: true, wtimeout: 150 }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, UpdateWithLegacyRuntimeConstants) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj update(BSON("y" << 1));
-
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setLegacyRuntimeConstants({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));
- const BSONObj sort(BSON("z" << -1));
- const BSONObj hint(BSON("z" << -1));
- const BSONObj collation(BSON("locale"
- << "en_US"));
- 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 = LegacyRuntimeConstants{Date_t(), Timestamp(1, 0)};
-
- auto request = FindAndModifyRequest::makeUpdate(
- NamespaceString("test.user"),
- query,
- write_ops::UpdateModification::parseFromClassicUpdate(update));
- request.setFieldProjection(field);
- request.setShouldReturnNew(true);
- request.setSort(sort);
- request.setHint(hint);
- request.setCollation(collation);
- request.setArrayFilters(arrayFilters);
- request.setLegacyRuntimeConstants(rtc);
- request.setWriteConcern(writeConcern);
- request.setBypassDocumentValidation(true);
- request.setUpsert(true);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 },
- upsert: true,
- fields: { x: 1, y: 1 },
- sort: { z: -1 },
- hint: { 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
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, BasicRemove) {
- const BSONObj query(BSON("x" << 1));
- auto request = FindAndModifyRequest::makeRemove(NamespaceString("test.user"), query);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- remove: true
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, RemoveWithProjection) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj field(BSON("z" << 1));
-
- auto request = FindAndModifyRequest::makeRemove(NamespaceString("test.user"), query);
- request.setFieldProjection(field);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- remove: true,
- fields: { z: 1 }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, RemoveWithSort) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj sort(BSON("z" << -1));
-
- auto request = FindAndModifyRequest::makeRemove(NamespaceString("test.user"), query);
- request.setSort(sort);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- remove: true,
- sort: { z: -1 }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, RemoveWithCollation) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj collation(BSON("locale"
- << "en_US"));
-
- auto request = FindAndModifyRequest::makeRemove(NamespaceString("test.user"), query);
- request.setCollation(collation);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- remove: true,
- collation: { locale: 'en_US' }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, RemoveWithWriteConcern) {
- const BSONObj query(BSON("x" << 1));
- const WriteConcernOptions writeConcern(2, WriteConcernOptions::SyncMode::FSYNC, 150);
-
- auto request = FindAndModifyRequest::makeRemove(NamespaceString("test.user"), query);
- request.setWriteConcern(writeConcern);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- remove: true,
- writeConcern: { w: 2, fsync: true, wtimeout: 150 }
- })json"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, RemoveWithFullSpec) {
- const BSONObj query(BSON("x" << 1));
- const BSONObj sort(BSON("z" << -1));
- const BSONObj collation(BSON("locale"
- << "en_US"));
- const BSONObj field(BSON("x" << 1 << "y" << 1));
- const WriteConcernOptions writeConcern(2, WriteConcernOptions::SyncMode::FSYNC, 150);
- auto rtc = LegacyRuntimeConstants{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.setLegacyRuntimeConstants(rtc);
-
- BSONObj expectedObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- remove: true,
- 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"));
-
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
-}
-
-TEST(FindAndModifyRequest, ParseWithUpdateOnlyRequiredFields) {
- BSONObj cmdObj(fromjson(R"json({
- query: { x: 1 },
- update: { y: 1 }
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_OK(parseStatus.getStatus());
-
- auto request = parseStatus.getValue();
- ASSERT_EQ(NamespaceString("a.b"), request.getNamespaceString());
- ASSERT_BSONOBJ_EQ(BSON("x" << 1), request.getQuery());
- ASSERT(request.getUpdate());
- ASSERT(request.getUpdate()->type() == write_ops::UpdateModification::Type::kClassic);
- ASSERT_BSONOBJ_EQ(BSON("y" << 1), request.getUpdate()->getUpdateClassic());
- ASSERT_EQ(false, request.isUpsert());
- ASSERT_EQ(false, request.isRemove());
- ASSERT_BSONOBJ_EQ(BSONObj(), request.getFields());
- ASSERT_BSONOBJ_EQ(BSONObj(), request.getSort());
- ASSERT_BSONOBJ_EQ(BSONObj(), request.getHint());
- ASSERT_BSONOBJ_EQ(BSONObj(), request.getCollation());
- ASSERT_EQ(0u, request.getArrayFilters().size());
- ASSERT_EQ(false, request.shouldReturnNew());
-}
-
-TEST(FindAndModifyRequest, ParseWithUpdateFullSpec) {
- BSONObj cmdObj(fromjson(R"json({
- query: { x: 1 },
- update: { y: 1 },
- upsert: true,
- fields: { x: 1, y: 1 },
- sort: { z: -1 },
- hint: { z: -1 },
- collation: {locale: 'en_US' },
- arrayFilters: [ { i: 0 } ],
- new: true
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_OK(parseStatus.getStatus());
-
- auto request = parseStatus.getValue();
- ASSERT_EQ(NamespaceString("a.b"), request.getNamespaceString());
- ASSERT_BSONOBJ_EQ(BSON("x" << 1), request.getQuery());
- ASSERT(request.getUpdate());
- ASSERT(request.getUpdate()->type() == write_ops::UpdateModification::Type::kClassic);
- ASSERT_BSONOBJ_EQ(BSON("y" << 1), request.getUpdate()->getUpdateClassic());
- ASSERT_EQ(true, request.isUpsert());
- ASSERT_EQ(false, request.isRemove());
- ASSERT_BSONOBJ_EQ(BSON("x" << 1 << "y" << 1), request.getFields());
- ASSERT_BSONOBJ_EQ(BSON("z" << -1), request.getSort());
- ASSERT_BSONOBJ_EQ(BSON("z" << -1), request.getHint());
- ASSERT_BSONOBJ_EQ(BSON("locale"
- << "en_US"),
- request.getCollation());
- ASSERT_EQ(1u, request.getArrayFilters().size());
- ASSERT_BSONOBJ_EQ(BSON("i" << 0), request.getArrayFilters()[0]);
- ASSERT_EQ(true, request.shouldReturnNew());
-}
-
-TEST(FindAndModifyRequest, ParseWithUpdateAndStringHint) {
- BSONObj cmdObj(fromjson(R"json({
- query: { x: 1 },
- update: { y: 1 },
- hint: 'z_1'
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_OK(parseStatus.getStatus());
-
- auto request = parseStatus.getValue();
- ASSERT_EQ(NamespaceString("a.b"), request.getNamespaceString());
- ASSERT_BSONOBJ_EQ(BSON("x" << 1), request.getQuery());
- ASSERT(request.getUpdate());
- ASSERT(request.getUpdate()->type() == write_ops::UpdateModification::Type::kClassic);
- ASSERT_BSONOBJ_EQ(BSON("y" << 1), request.getUpdate()->getUpdateClassic());
- ASSERT_BSONOBJ_EQ(BSON("$hint"
- << "z_1"),
- request.getHint());
- ASSERT_EQ(false, request.isUpsert());
- ASSERT_EQ(false, request.isRemove());
- ASSERT_BSONOBJ_EQ(BSONObj(), request.getFields());
- ASSERT_BSONOBJ_EQ(BSONObj(), request.getSort());
- ASSERT_BSONOBJ_EQ(BSONObj(), request.getCollation());
- ASSERT_EQ(0u, request.getArrayFilters().size());
- ASSERT_EQ(false, request.shouldReturnNew());
-}
-
-TEST(FindAndModifyRequest, ParseWithRemoveOnlyRequiredFields) {
- BSONObj cmdObj(fromjson(R"json({
- query: { x: 1 },
- remove: true
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_OK(parseStatus.getStatus());
-
- auto request = parseStatus.getValue();
- ASSERT_EQ(NamespaceString("a.b"), request.getNamespaceString());
- ASSERT_BSONOBJ_EQ(BSON("x" << 1), request.getQuery());
- ASSERT_FALSE(request.getUpdate());
- ASSERT_EQ(false, request.isUpsert());
- ASSERT_EQ(true, request.isRemove());
- ASSERT_BSONOBJ_EQ(BSONObj(), request.getFields());
- ASSERT_BSONOBJ_EQ(BSONObj(), request.getSort());
- ASSERT_BSONOBJ_EQ(BSONObj(), request.getCollation());
- ASSERT_EQ(false, request.shouldReturnNew());
-}
-
-TEST(FindAndModifyRequest, ParseWithRemoveFullSpec) {
- BSONObj cmdObj(fromjson(R"json({
- query: { x: 1 },
- remove: true,
- fields: { x: 1, y: 1 },
- sort: { z: -1 },
- collation: { locale: 'en_US' },
- new: false
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_OK(parseStatus.getStatus());
-
- auto request = parseStatus.getValue();
- ASSERT_EQ(NamespaceString("a.b"), request.getNamespaceString());
- ASSERT_BSONOBJ_EQ(BSON("x" << 1), request.getQuery());
- ASSERT_FALSE(request.getUpdate());
- ASSERT_EQ(false, request.isUpsert());
- ASSERT_EQ(true, request.isRemove());
- ASSERT_BSONOBJ_EQ(BSON("x" << 1 << "y" << 1), request.getFields());
- ASSERT_BSONOBJ_EQ(BSON("z" << -1), request.getSort());
- ASSERT_BSONOBJ_EQ(BSON("locale"
- << "en_US"),
- request.getCollation());
- ASSERT_EQ(false, request.shouldReturnNew());
-}
-
-TEST(FindAndModifyRequest, ParseWithIncompleteSpec) {
- BSONObj cmdObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 }
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_NOT_OK(parseStatus.getStatus());
-}
-
-TEST(FindAndModifyRequest, ParseWithAmbiguousUpdateRemove) {
- BSONObj cmdObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- update: { y: 1 },
- remove: true
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_NOT_OK(parseStatus.getStatus());
-}
-
-TEST(FindAndModifyRequest, ParseWithRemovePlusUpsert) {
- BSONObj cmdObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- remove: true,
- upsert: true
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_NOT_OK(parseStatus.getStatus());
-}
-
-TEST(FindAndModifyRequest, ParseWithRemoveAndReturnNew) {
- BSONObj cmdObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- remove: true,
- new: true
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_NOT_OK(parseStatus.getStatus());
-}
-
-TEST(FindAndModifyRequest, ParseWithRemoveAndArrayFilters) {
- BSONObj cmdObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- remove: true,
- arrayFilters: [ { i: 0 } ]
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_NOT_OK(parseStatus.getStatus());
-}
-
-TEST(FindAndModifyRequest, ParseWithCollationTypeMismatch) {
- BSONObj cmdObj(fromjson(R"json({
- query: { x: 1 },
- update: { y: 1 },
- collation: 'en_US'
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_EQ(parseStatus.getStatus(), ErrorCodes::TypeMismatch);
-}
-
-TEST(FindAndModifyRequest, ParseWithBypassDocumentValidation) {
- BSONObj cmdObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- remove: true,
- bypassDocumentValidation: true
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_OK(parseStatus.getStatus());
-
- auto request = parseStatus.getValue();
- ASSERT_EQ(NamespaceString("a.b"), request.getNamespaceString());
- ASSERT_BSONOBJ_EQ(BSON("x" << 1), request.getQuery());
- ASSERT_FALSE(request.getUpdate());
- ASSERT_EQ(true, request.isRemove());
- ASSERT_EQ(true, request.getBypassDocumentValidation());
-}
-
-TEST(FindAndModifyRequest, ParseWithWriteConcernAsArray) {
- BSONObj cmdObj(fromjson(R"json({
- findAndModify: 'user',
- query: { x: 1 },
- remove: true,
- writeConcern: []
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_EQ(ErrorCodes::TypeMismatch, parseStatus.getStatus());
-}
-
-TEST(FindAndModifyRequest, ParsesAndSerializesPipelineUpdate) {
- BSONObj cmdObj(fromjson(R"json({
- query: { x: 1 },
- update: [{$replaceWith: {y: 1}}]
- })json"));
-
- auto request =
- unittest::assertGet(FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj));
- ASSERT(request.getUpdate());
- ASSERT(request.getUpdate()->type() == write_ops::UpdateModification::Type::kPipeline);
- auto serialized = request.toBSON({});
- ASSERT_BSONOBJ_EQ(serialized, fromjson(R"json({
- findAndModify: "b",
- query: {x: 1},
- update: [{$replaceWith: {y: 1}}],
- fields: {},
- sort: {},
- hint: {},
- collation: {}
- })json"));
- ASSERT_OK(FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), serialized).getStatus());
-}
-
-TEST(FindAndModifyRequest, RejectsBothArrayFiltersAndPipelineUpdate) {
- BSONObj cmdObj(fromjson(R"json({
- query: { x: 1 },
- update: [{$replaceWith: {y: 1}}],
- arrayFilters: []
- })json"));
-
- auto swRequestNoFilters = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_EQ(swRequestNoFilters.getStatus(), ErrorCodes::FailedToParse);
-
- cmdObj = fromjson(R"json({
- query: { x: 1 },
- update: [{$replaceWith: {y: 1}}],
- arrayFilters: [{"i.x": 1}]
- })json");
- auto swRequestOneFilter = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_EQ(swRequestOneFilter.getStatus(), ErrorCodes::FailedToParse);
-}
-
-TEST(FindAndModifyRequest, InvalidQueryParameter) {
- BSONObj cmdObj(fromjson(R"json({
- findAndModify: 'user',
- query: '{ x: 1 }',
- remove: true
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_EQ(31160, parseStatus.getStatus().code());
-}
-
-TEST(FindAndModifyRequest, InvalidSortParameter) {
- BSONObj cmdObj(fromjson(R"json({
- findAndModify: 'user',
- sort: 1,
- remove: true
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_EQ(31174, parseStatus.getStatus().code());
-}
-
-TEST(FindAndModifyRequest, InvalidHintParameter) {
- BSONObj cmdObj(fromjson(R"json({
- findAndModify: 'user',
- hint: 1
- })json"));
-
- ASSERT_THROWS_CODE(FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj),
- AssertionException,
- ErrorCodes::FailedToParse);
-}
-
-TEST(FindAndModifyRequest, InvalidFieldParameter) {
- BSONObj cmdObj(fromjson(R"json({
- findAndModify: 'user',
- fields: null,
- remove: true
- })json"));
-
- auto parseStatus = FindAndModifyRequest::parseFromBSON(NamespaceString("a.b"), cmdObj);
- ASSERT_EQ(31175, parseStatus.getStatus().code());
-}
-} // unnamed namespace
-} // namespace mongo
diff --git a/src/mongo/db/s/shard_local_test.cpp b/src/mongo/db/s/shard_local_test.cpp
index 200043c20df..48d3b61533d 100644
--- a/src/mongo/db/s/shard_local_test.cpp
+++ b/src/mongo/db/s/shard_local_test.cpp
@@ -33,8 +33,8 @@
#include "mongo/db/catalog_raii.h"
#include "mongo/db/client.h"
#include "mongo/db/dbdirectclient.h"
+#include "mongo/db/ops/find_and_modify_command_gen.h"
#include "mongo/db/query/cursor_response.h"
-#include "mongo/db/query/find_and_modify_request.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/repl/replication_coordinator_mock.h"
#include "mongo/db/s/shard_local.h"
@@ -101,12 +101,15 @@ void ShardLocalTest::tearDown() {
StatusWith<Shard::CommandResponse> ShardLocalTest::runFindAndModifyRunCommand(NamespaceString nss,
BSONObj find,
BSONObj set) {
- FindAndModifyRequest findAndModifyRequest = FindAndModifyRequest::makeUpdate(
- nss, find, write_ops::UpdateModification::parseFromClassicUpdate(set));
+ auto findAndModifyRequest = write_ops::FindAndModifyCommand(nss);
+ findAndModifyRequest.setQuery(find);
+ findAndModifyRequest.setUpdate(write_ops::UpdateModification::parseFromClassicUpdate(set));
findAndModifyRequest.setUpsert(true);
- findAndModifyRequest.setShouldReturnNew(true);
- findAndModifyRequest.setWriteConcern(WriteConcernOptions(
- WriteConcernOptions::kMajority, WriteConcernOptions::SyncMode::UNSET, Seconds(15)));
+ findAndModifyRequest.setNew(true);
+ findAndModifyRequest.setWriteConcern(WriteConcernOptions(WriteConcernOptions::kMajority,
+ WriteConcernOptions::SyncMode::UNSET,
+ Seconds(15))
+ .toBSON());
return _shardLocal->runCommandWithFixedRetryAttempts(
_opCtx.get(),
diff --git a/src/mongo/s/catalog/dist_lock_catalog_impl.cpp b/src/mongo/s/catalog/dist_lock_catalog_impl.cpp
index 56b925be417..9dc62b9d360 100644
--- a/src/mongo/s/catalog/dist_lock_catalog_impl.cpp
+++ b/src/mongo/s/catalog/dist_lock_catalog_impl.cpp
@@ -38,7 +38,6 @@
#include "mongo/bson/util/bson_extract.h"
#include "mongo/client/read_preference.h"
#include "mongo/db/lasterror.h"
-#include "mongo/db/query/find_and_modify_request.h"
#include "mongo/db/repl/read_concern_args.h"
#include "mongo/rpc/get_status_from_command_result.h"
#include "mongo/rpc/metadata.h"
@@ -156,6 +155,18 @@ StatusWith<OID> extractElectionId(const BSONObj& responseObj) {
return electionId;
}
+write_ops::FindAndModifyCommand makeFindAndModifyRequest(
+ NamespaceString fullNs, BSONObj query, boost::optional<write_ops::UpdateModification> update) {
+ auto request = write_ops::FindAndModifyCommand(fullNs);
+ request.setQuery(query);
+ if (update) {
+ request.setUpdate(std::move(update));
+ } else {
+ request.setRemove(true);
+ }
+ return request;
+}
+
} // unnamed namespace
DistLockCatalogImpl::DistLockCatalogImpl()
@@ -191,13 +202,12 @@ StatusWith<LockpingsType> DistLockCatalogImpl::getPing(OperationContext* opCtx,
}
Status DistLockCatalogImpl::ping(OperationContext* opCtx, StringData processID, Date_t ping) {
- auto request =
- FindAndModifyRequest::makeUpdate(_lockPingNS,
- BSON(LockpingsType::process() << processID),
- write_ops::UpdateModification::parseFromClassicUpdate(
- BSON("$set" << BSON(LockpingsType::ping(ping)))));
+ auto request = write_ops::FindAndModifyCommand(_lockPingNS);
+ request.setQuery(BSON(LockpingsType::process() << processID));
+ request.setUpdate(write_ops::UpdateModification::parseFromClassicUpdate(
+ BSON("$set" << BSON(LockpingsType::ping(ping)))));
request.setUpsert(true);
- request.setWriteConcern(kMajorityWriteConcern);
+ request.setWriteConcern(kMajorityWriteConcern.toBSON());
auto const shardRegistry = Grid::get(opCtx)->shardRegistry();
auto resultStatus = shardRegistry->getConfigShard()->runCommandWithFixedRetryAttempts(
@@ -225,13 +235,13 @@ StatusWith<LocksType> DistLockCatalogImpl::grabLock(OperationContext* opCtx,
<< LocksType::process() << processId << LocksType::when(time)
<< LocksType::why() << why));
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
_locksNS,
BSON(LocksType::name() << lockID << LocksType::state(LocksType::UNLOCKED)),
write_ops::UpdateModification::parseFromClassicUpdate(BSON("$set" << newLockDetails)));
request.setUpsert(true);
- request.setShouldReturnNew(true);
- request.setWriteConcern(writeConcern);
+ request.setNew(true);
+ request.setWriteConcern(writeConcern.toBSON());
auto const shardRegistry = Grid::get(opCtx)->shardRegistry();
auto resultStatus = shardRegistry->getConfigShard()->runCommandWithFixedRetryAttempts(
@@ -282,12 +292,12 @@ StatusWith<LocksType> DistLockCatalogImpl::overtakeLock(OperationContext* opCtx,
<< LocksType::process() << processId << LocksType::when(time)
<< LocksType::why() << why));
- auto request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
_locksNS,
BSON("$or" << orQueryBuilder.arr()),
write_ops::UpdateModification::parseFromClassicUpdate(BSON("$set" << newLockDetails)));
- request.setShouldReturnNew(true);
- request.setWriteConcern(kMajorityWriteConcern);
+ request.setNew(true);
+ request.setWriteConcern(kMajorityWriteConcern.toBSON());
auto const shardRegistry = Grid::get(opCtx)->shardRegistry();
auto resultStatus = shardRegistry->getConfigShard()->runCommandWithFixedRetryAttempts(
@@ -315,28 +325,29 @@ StatusWith<LocksType> DistLockCatalogImpl::overtakeLock(OperationContext* opCtx,
}
Status DistLockCatalogImpl::unlock(OperationContext* opCtx, const OID& lockSessionID) {
- FindAndModifyRequest request = FindAndModifyRequest::makeUpdate(
- _locksNS,
- BSON(LocksType::lockID(lockSessionID)),
- write_ops::UpdateModification::parseFromClassicUpdate(
- BSON("$set" << BSON(LocksType::state(LocksType::UNLOCKED)))));
- request.setWriteConcern(kMajorityWriteConcern);
+ auto request =
+ makeFindAndModifyRequest(_locksNS,
+ BSON(LocksType::lockID(lockSessionID)),
+ write_ops::UpdateModification::parseFromClassicUpdate(
+ BSON("$set" << BSON(LocksType::state(LocksType::UNLOCKED)))));
+ request.setWriteConcern(kMajorityWriteConcern.toBSON());
return _unlock(opCtx, request);
}
Status DistLockCatalogImpl::unlock(OperationContext* opCtx,
const OID& lockSessionID,
StringData name) {
- FindAndModifyRequest request = FindAndModifyRequest::makeUpdate(
+ auto request = makeFindAndModifyRequest(
_locksNS,
BSON(LocksType::lockID(lockSessionID) << LocksType::name(name.toString())),
write_ops::UpdateModification::parseFromClassicUpdate(
BSON("$set" << BSON(LocksType::state(LocksType::UNLOCKED)))));
- request.setWriteConcern(kMajorityWriteConcern);
+ request.setWriteConcern(kMajorityWriteConcern.toBSON());
return _unlock(opCtx, request);
}
-Status DistLockCatalogImpl::_unlock(OperationContext* opCtx, const FindAndModifyRequest& request) {
+Status DistLockCatalogImpl::_unlock(OperationContext* opCtx,
+ const write_ops::FindAndModifyCommand& request) {
auto const shardRegistry = Grid::get(opCtx)->shardRegistry();
auto resultStatus = shardRegistry->getConfigShard()->runCommandWithFixedRetryAttempts(
opCtx,
@@ -498,8 +509,8 @@ StatusWith<LocksType> DistLockCatalogImpl::getLockByName(OperationContext* opCtx
Status DistLockCatalogImpl::stopPing(OperationContext* opCtx, StringData processId) {
auto request =
- FindAndModifyRequest::makeRemove(_lockPingNS, BSON(LockpingsType::process() << processId));
- request.setWriteConcern(kMajorityWriteConcern);
+ makeFindAndModifyRequest(_lockPingNS, BSON(LockpingsType::process() << processId), {});
+ request.setWriteConcern(kMajorityWriteConcern.toBSON());
auto const shardRegistry = Grid::get(opCtx)->shardRegistry();
auto resultStatus = shardRegistry->getConfigShard()->runCommandWithFixedRetryAttempts(
diff --git a/src/mongo/s/catalog/dist_lock_catalog_impl.h b/src/mongo/s/catalog/dist_lock_catalog_impl.h
index 6735576002e..cdbb4bff12c 100644
--- a/src/mongo/s/catalog/dist_lock_catalog_impl.h
+++ b/src/mongo/s/catalog/dist_lock_catalog_impl.h
@@ -35,13 +35,13 @@
#include "mongo/bson/oid.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/db/ops/find_and_modify_command_gen.h"
#include "mongo/db/write_concern_options.h"
#include "mongo/s/catalog/dist_lock_catalog.h"
#include "mongo/util/time_support.h"
namespace mongo {
-class FindAndModifyRequest;
struct ReadPreferenceSetting;
class DistLockCatalogImpl final : public DistLockCatalog {
@@ -86,7 +86,7 @@ public:
Status stopPing(OperationContext* opCtx, StringData processId) override;
private:
- Status _unlock(OperationContext* opCtx, const FindAndModifyRequest& request);
+ Status _unlock(OperationContext* opCtx, const write_ops::FindAndModifyCommand& request);
StatusWith<std::vector<BSONObj>> _findOnConfig(OperationContext* opCtx,
const ReadPreferenceSetting& readPref,
diff --git a/src/mongo/s/catalog/dist_lock_catalog_impl_test.cpp b/src/mongo/s/catalog/dist_lock_catalog_impl_test.cpp
index 619217a5259..a491d5710c9 100644
--- a/src/mongo/s/catalog/dist_lock_catalog_impl_test.cpp
+++ b/src/mongo/s/catalog/dist_lock_catalog_impl_test.cpp
@@ -35,7 +35,6 @@
#include "mongo/bson/json.h"
#include "mongo/client/remote_command_targeter_mock.h"
#include "mongo/db/commands.h"
-#include "mongo/db/query/find_and_modify_request.h"
#include "mongo/db/repl/read_concern_args.h"
#include "mongo/db/s/shard_server_test_fixture.h"
#include "mongo/db/storage/duplicate_key_error_info.h"