summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorPawel Terlecki <pawel.terlecki@mongodb.com>2019-04-20 10:25:26 -0400
committerPawel Terlecki <pawel.terlecki@mongodb.com>2019-04-25 21:04:43 -0400
commit9318790212dead660f8f057f18ed48fba9232b83 (patch)
tree9bd7ddd4dc14a606650e089194b07f5d1da3f6e6 /src/mongo/db
parentbf40065c01769683000310fedcd4b8c729c4fdb2 (diff)
downloadmongo-9318790212dead660f8f057f18ed48fba9232b83.tar.gz
SERVER-40005 Add translation for FindAndModify for FLE
Extended FindAndModifyRequest to validate all fields in parsing and serialize to BSON with passthru fields.
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/ops/write_ops_retryability.cpp12
-rw-r--r--src/mongo/db/query/find_and_modify_request.cpp109
-rw-r--r--src/mongo/db/query/find_and_modify_request.h9
-rw-r--r--src/mongo/db/query/find_and_modify_request_test.cpp34
4 files changed, 99 insertions, 65 deletions
diff --git a/src/mongo/db/ops/write_ops_retryability.cpp b/src/mongo/db/ops/write_ops_retryability.cpp
index 6e8efaa83b9..32a160d433c 100644
--- a/src/mongo/db/ops/write_ops_retryability.cpp
+++ b/src/mongo/db/ops/write_ops_retryability.cpp
@@ -54,7 +54,7 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request,
if (opType == repl::OpTypeEnum::kDelete) {
uassert(
40606,
- str::stream() << "findAndModify retry request: " << redact(request.toBSON())
+ str::stream() << "findAndModify retry request: " << redact(request.toBSON({}))
<< " is not compatible with previous write in the transaction of type: "
<< OpType_serializer(oplogEntry.getOpType())
<< ", oplogTs: "
@@ -64,12 +64,12 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request,
request.isRemove());
uassert(40607,
str::stream() << "No pre-image available for findAndModify retry request:"
- << redact(request.toBSON()),
+ << redact(request.toBSON({})),
oplogWithCorrectLinks.getPreImageOpTime());
} else if (opType == repl::OpTypeEnum::kInsert) {
uassert(
40608,
- str::stream() << "findAndModify retry request: " << redact(request.toBSON())
+ str::stream() << "findAndModify retry request: " << redact(request.toBSON({}))
<< " is not compatible with previous write in the transaction of type: "
<< OpType_serializer(oplogEntry.getOpType())
<< ", oplogTs: "
@@ -80,7 +80,7 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request,
} else {
uassert(
40609,
- str::stream() << "findAndModify retry request: " << redact(request.toBSON())
+ str::stream() << "findAndModify retry request: " << redact(request.toBSON({}))
<< " is not compatible with previous write in the transaction of type: "
<< OpType_serializer(oplogEntry.getOpType())
<< ", oplogTs: "
@@ -91,7 +91,7 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request,
if (request.shouldReturnNew()) {
uassert(40611,
- str::stream() << "findAndModify retry request: " << redact(request.toBSON())
+ str::stream() << "findAndModify retry request: " << redact(request.toBSON({}))
<< " wants the document after update returned, but only before "
"update document is stored, oplogTs: "
<< ts.toString()
@@ -100,7 +100,7 @@ void validateFindAndModifyRetryability(const FindAndModifyRequest& request,
oplogWithCorrectLinks.getPostImageOpTime());
} else {
uassert(40612,
- str::stream() << "findAndModify retry request: " << redact(request.toBSON())
+ str::stream() << "findAndModify retry request: " << redact(request.toBSON({}))
<< " wants the document before update returned, but only after "
"update document is stored, oplogTs: "
<< ts.toString()
diff --git a/src/mongo/db/query/find_and_modify_request.cpp b/src/mongo/db/query/find_and_modify_request.cpp
index 1ef84b03762..6c46b639177 100644
--- a/src/mongo/db/query/find_and_modify_request.cpp
+++ b/src/mongo/db/query/find_and_modify_request.cpp
@@ -34,7 +34,9 @@
#include "mongo/base/status_with.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/bson/util/bson_extract.h"
+#include "mongo/db/command_generic_argument.h"
#include "mongo/db/write_concern.h"
+#include "mongo/idl/idl_parser.h"
namespace mongo {
@@ -52,6 +54,12 @@ const char kUpsertField[] = "upsert";
const char kWriteConcernField[] = "writeConcern";
const std::vector<BSONObj> emptyArrayFilters{};
+
+const std::vector<StringData> _knownFields{
+ FindAndModifyRequest::kBypassDocumentValidationFieldName,
+ FindAndModifyRequest::kLegacyCommandName,
+ FindAndModifyRequest::kCommandName,
+};
} // unnamed namespace
FindAndModifyRequest::FindAndModifyRequest(NamespaceString fullNs, BSONObj query, BSONObj updateObj)
@@ -67,12 +75,12 @@ FindAndModifyRequest FindAndModifyRequest::makeUpdate(NamespaceString fullNs,
}
FindAndModifyRequest FindAndModifyRequest::makeRemove(NamespaceString fullNs, BSONObj query) {
- FindAndModifyRequest request(fullNs, query, BSONObj());
+ FindAndModifyRequest request(fullNs, query, {});
request._isRemove = true;
return request;
}
-BSONObj FindAndModifyRequest::toBSON() const {
+BSONObj FindAndModifyRequest::toBSON(const BSONObj& commandPassthroughFields) const {
BSONObjBuilder builder;
builder.append(kCmdName, _ns.coll());
@@ -116,56 +124,77 @@ BSONObj FindAndModifyRequest::toBSON() const {
builder.append(kWriteConcernField, _writeConcern->toBSON());
}
+ IDLParserErrorContext::appendGenericCommandArguments(
+ commandPassthroughFields, _knownFields, &builder);
+
return builder.obj();
}
StatusWith<FindAndModifyRequest> FindAndModifyRequest::parseFromBSON(NamespaceString fullNs,
const BSONObj& cmdObj) {
- BSONObj query = cmdObj.getObjectField(kQueryField);
- BSONObj fields = cmdObj.getObjectField(kFieldProjectionField);
- BSONObj updateObj = cmdObj.getObjectField(kUpdateField);
- BSONObj sort = cmdObj.getObjectField(kSortField);
-
+ BSONObj query;
+ BSONObj fields;
+ BSONObj updateObj;
+ BSONObj sort;
BSONObj collation;
- {
- BSONElement collationElt;
- Status collationEltStatus =
- bsonExtractTypedField(cmdObj, kCollationField, BSONType::Object, &collationElt);
- if (!collationEltStatus.isOK() && (collationEltStatus != ErrorCodes::NoSuchKey)) {
- return collationEltStatus;
- }
- if (collationEltStatus.isOK()) {
- collation = collationElt.Obj();
- }
- }
-
- std::vector<BSONObj> arrayFilters;
+ bool shouldReturnNew = false;
+ bool isUpsert = false;
+ bool isRemove = false;
+ bool isUpdate = false;
bool arrayFiltersSet = false;
- {
- 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.Obj()) {
- if (arrayFilter.type() != BSONType::Object) {
- return {ErrorCodes::TypeMismatch,
- str::stream() << "Each array filter must be an object, found "
- << arrayFilter.type()};
+ std::vector<BSONObj> arrayFilters;
+
+ for (auto&& field : cmdObj.getFieldNames<std::set<std::string>>()) {
+ if (field == kQueryField) {
+ query = cmdObj.getObjectField(kQueryField);
+ } else if (field == kSortField) {
+ sort = cmdObj.getObjectField(kSortField);
+ } else if (field == kRemoveField) {
+ isRemove = cmdObj[kRemoveField].trueValue();
+ } else if (field == kUpdateField) {
+ updateObj = cmdObj.getObjectField(kUpdateField);
+ isUpdate = true;
+ } else if (field == kNewField) {
+ shouldReturnNew = cmdObj[kNewField].trueValue();
+ } else if (field == kFieldProjectionField) {
+ fields = cmdObj.getObjectField(kFieldProjectionField);
+ } else if (field == kUpsertField) {
+ isUpsert = cmdObj[kUpsertField].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.Obj();
+ }
+ } 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.Obj()) {
+ if (arrayFilter.type() != BSONType::Object) {
+ return {ErrorCodes::TypeMismatch,
+ str::stream() << "Each array filter must be an object, found "
+ << arrayFilter.type()};
+ }
+ arrayFilters.push_back(arrayFilter.Obj());
}
- arrayFilters.push_back(arrayFilter.Obj());
}
+ } else if (!isGenericArgument(field) &&
+ !std::count(_knownFields.begin(), _knownFields.end(), field)) {
+ return {ErrorCodes::Error(51177),
+ str::stream() << "BSON field '" << field << "' is an unknown field."};
}
}
- bool shouldReturnNew = cmdObj[kNewField].trueValue();
- bool isUpsert = cmdObj[kUpsertField].trueValue();
- bool isRemove = cmdObj[kRemoveField].trueValue();
- bool isUpdate = cmdObj.hasField(kUpdateField);
-
if (!isRemove && !isUpdate) {
return {ErrorCodes::FailedToParse, "Either an update or remove=true must be specified"};
}
diff --git a/src/mongo/db/query/find_and_modify_request.h b/src/mongo/db/query/find_and_modify_request.h
index 71e1ac983d1..f0882a63941 100644
--- a/src/mongo/db/query/find_and_modify_request.h
+++ b/src/mongo/db/query/find_and_modify_request.h
@@ -50,6 +50,10 @@ class StatusWith;
*/
class FindAndModifyRequest {
public:
+ static constexpr auto kBypassDocumentValidationFieldName = "bypassDocumentValidation"_sd;
+ static constexpr auto kLegacyCommandName = "findandmodify"_sd;
+ static constexpr auto kCommandName = "findAndModify"_sd;
+
/**
* Creates a new instance of an 'update' type findAndModify request.
*/
@@ -87,9 +91,10 @@ public:
/**
* Serializes this object into a BSON representation. Fields that are not
- * set will not be part of the the serialized object.
+ * set will not be part of the the serialized object. Passthrough fields
+ * are appended.
*/
- BSONObj toBSON() const;
+ BSONObj toBSON(const BSONObj& commandPassthroughFields) const;
const NamespaceString& getNamespaceString() const;
BSONObj getQuery() const;
diff --git a/src/mongo/db/query/find_and_modify_request_test.cpp b/src/mongo/db/query/find_and_modify_request_test.cpp
index 7266cb04a18..a30bdd2e538 100644
--- a/src/mongo/db/query/find_and_modify_request_test.cpp
+++ b/src/mongo/db/query/find_and_modify_request_test.cpp
@@ -47,7 +47,7 @@ TEST(FindAndModifyRequest, BasicUpdate) {
update: { y: 1 }
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, UpdateWithUpsert) {
@@ -63,7 +63,7 @@ TEST(FindAndModifyRequest, UpdateWithUpsert) {
upsert: true
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, UpdateWithUpsertFalse) {
@@ -79,7 +79,7 @@ TEST(FindAndModifyRequest, UpdateWithUpsertFalse) {
upsert: false
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, UpdateWithProjection) {
@@ -97,7 +97,7 @@ TEST(FindAndModifyRequest, UpdateWithProjection) {
fields: { z: 1 }
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, UpdateWithNewTrue) {
@@ -114,7 +114,7 @@ TEST(FindAndModifyRequest, UpdateWithNewTrue) {
new: true
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, UpdateWithNewFalse) {
@@ -131,7 +131,7 @@ TEST(FindAndModifyRequest, UpdateWithNewFalse) {
new: false
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, UpdateWithSort) {
@@ -149,7 +149,7 @@ TEST(FindAndModifyRequest, UpdateWithSort) {
sort: { z: -1 }
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, UpdateWithCollation) {
@@ -168,7 +168,7 @@ TEST(FindAndModifyRequest, UpdateWithCollation) {
collation: { locale: 'en_US' }
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, UpdateWithArrayFilters) {
@@ -186,7 +186,7 @@ TEST(FindAndModifyRequest, UpdateWithArrayFilters) {
arrayFilters: [ { i: 0 } ]
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, UpdateWithWriteConcern) {
@@ -204,7 +204,7 @@ TEST(FindAndModifyRequest, UpdateWithWriteConcern) {
writeConcern: { w: 2, fsync: true, wtimeout: 150 }
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, UpdateWithFullSpec) {
@@ -239,7 +239,7 @@ TEST(FindAndModifyRequest, UpdateWithFullSpec) {
writeConcern: { w: 2, fsync: true, wtimeout: 150 }
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, BasicRemove) {
@@ -252,7 +252,7 @@ TEST(FindAndModifyRequest, BasicRemove) {
remove: true
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, RemoveWithProjection) {
@@ -269,7 +269,7 @@ TEST(FindAndModifyRequest, RemoveWithProjection) {
fields: { z: 1 }
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, RemoveWithSort) {
@@ -286,7 +286,7 @@ TEST(FindAndModifyRequest, RemoveWithSort) {
sort: { z: -1 }
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, RemoveWithCollation) {
@@ -304,7 +304,7 @@ TEST(FindAndModifyRequest, RemoveWithCollation) {
collation: { locale: 'en_US' }
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, RemoveWithWriteConcern) {
@@ -321,7 +321,7 @@ TEST(FindAndModifyRequest, RemoveWithWriteConcern) {
writeConcern: { w: 2, fsync: true, wtimeout: 150 }
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, RemoveWithFullSpec) {
@@ -348,7 +348,7 @@ TEST(FindAndModifyRequest, RemoveWithFullSpec) {
writeConcern: { w: 2, fsync: true, wtimeout: 150 }
})json"));
- ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON());
+ ASSERT_BSONOBJ_EQ(expectedObj, request.toBSON({}));
}
TEST(FindAndModifyRequest, ParseWithUpdateOnlyRequiredFields) {