diff options
author | Pawel Terlecki <pawel.terlecki@mongodb.com> | 2019-04-20 10:25:26 -0400 |
---|---|---|
committer | Pawel Terlecki <pawel.terlecki@mongodb.com> | 2019-04-25 21:04:43 -0400 |
commit | 9318790212dead660f8f057f18ed48fba9232b83 (patch) | |
tree | 9bd7ddd4dc14a606650e089194b07f5d1da3f6e6 /src/mongo/db | |
parent | bf40065c01769683000310fedcd4b8c729c4fdb2 (diff) | |
download | mongo-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.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/query/find_and_modify_request.cpp | 109 | ||||
-rw-r--r-- | src/mongo/db/query/find_and_modify_request.h | 9 | ||||
-rw-r--r-- | src/mongo/db/query/find_and_modify_request_test.cpp | 34 |
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) { |