summaryrefslogtreecommitdiff
path: root/src/mongo/db/fle_crud.cpp
diff options
context:
space:
mode:
authorErwin Pe <erwin.pe@mongodb.com>2023-03-06 18:02:29 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-03-06 19:55:09 +0000
commit4889d67ff011baa34ede7e9b20f79506c6e07fbe (patch)
tree6e23b662a3e66a323aab7b21bd92cb1db2850c8d /src/mongo/db/fle_crud.cpp
parent10b9d6d1712ad49815c3c99aabf5c68b4186d494 (diff)
downloadmongo-4889d67ff011baa34ede7e9b20f79506c6e07fbe.tar.gz
SERVER-72933 Implement v2 changes to Queryable Encryption findAndModify
Diffstat (limited to 'src/mongo/db/fle_crud.cpp')
-rw-r--r--src/mongo/db/fle_crud.cpp108
1 files changed, 86 insertions, 22 deletions
diff --git a/src/mongo/db/fle_crud.cpp b/src/mongo/db/fle_crud.cpp
index 2c000e845e8..5d9a3f4b2f4 100644
--- a/src/mongo/db/fle_crud.cpp
+++ b/src/mongo/db/fle_crud.cpp
@@ -1399,7 +1399,15 @@ write_ops::FindAndModifyCommandReply processFindAndModify(
auto ei = findAndModifyRequest.getEncryptionInformation().value();
auto efc = EncryptionInformationHelpers::getAndValidateSchema(edcNss, ei);
- auto tokenMap = EncryptionInformationHelpers::getDeleteTokens(edcNss, ei);
+
+ // TODO: SERVER-73303 delete when v2 is enabled by default
+ StringMap<FLEDeleteToken> tokenMap;
+ if (!gFeatureFlagFLE2ProtocolVersion2.isEnabled(serverGlobalParams.featureCompatibility)) {
+ tokenMap = EncryptionInformationHelpers::getDeleteTokens(edcNss, ei);
+ } else if (ei.getDeleteTokens().has_value()) {
+ uasserted(7293301, "Illegal delete tokens encountered in EncryptionInformation");
+ }
+
int32_t stmtId = findAndModifyRequest.getStmtId().value_or(0);
auto newFindAndModifyRequest = findAndModifyRequest;
@@ -1485,39 +1493,95 @@ write_ops::FindAndModifyCommandReply processFindAndModify(
"Missing _id field in pre-image document, the fields document must contain _id",
idElement.fieldNameStringData() == "_id"_sd);
- BSONObj newDocument;
- std::vector<EDCIndexedFields> newFields;
+ // TODO: SERVER-73303 remove once v2 is enabled by default
+ if (!gFeatureFlagFLE2ProtocolVersion2.isEnabled(serverGlobalParams.featureCompatibility)) {
+ BSONObj newDocument;
+ std::vector<EDCIndexedFields> newFields;
+
+ // Is this a delete
+ bool isDelete = findAndModifyRequest.getRemove().value_or(false);
+
+ // Unlike update, there will not always be a new document since users can delete the
+ // document
+ if (!isDelete) {
+ newDocument = queryImpl->getById(edcNss, idElement);
+
+ // Fail if we could not find the new document
+ uassert(6371404, "Could not find pre-image document by _id", !newDocument.isEmpty());
- // Is this a delete
- bool isDelete = findAndModifyRequest.getRemove().value_or(false);
+ if (hasIndexedFieldsInSchema(efc.getFields())) {
+ // Check the user did not remove/destroy the __safeContent__ array. If there are no
+ // indexed fields, then there will not be a safeContent array in the document.
+ FLEClientCrypto::validateTagsArray(newDocument);
+ }
+
+ newFields = EDCServerCollection::getEncryptedIndexedFields(newDocument);
+ }
- // Unlike update, there will not always be a new document since users can delete the document
- if (!isDelete) {
- newDocument = queryImpl->getById(edcNss, idElement);
+ // Step 5 ----
+ auto originalFields = EDCServerCollection::getEncryptedIndexedFields(originalDocument);
+ auto deletedFields = EDCServerCollection::getRemovedFields(originalFields, newFields);
- // Fail if we could not find the new document
- uassert(6371404, "Could not find pre-image document by _id", !newDocument.isEmpty());
+ processRemovedFields(queryImpl, edcNss, efc, tokenMap, deletedFields, &stmtId);
- if (hasIndexedFieldsInSchema(efc.getFields())) {
- // Check the user did not remove/destroy the __safeContent__ array. If there are no
- // indexed fields, then there will not be a safeContent array in the document.
- FLEClientCrypto::validateTagsArray(newDocument);
+ // Step 6 ----
+ // We don't need to make a second update in the case of a delete
+ if (!isDelete) {
+ BSONObj pullUpdate =
+ EDCServerCollection::generateUpdateToRemoveTags(deletedFields, tokenMap);
+ auto newUpdateRequest =
+ write_ops::UpdateCommandRequest(findAndModifyRequest.getNamespace());
+ auto pullUpdateOpEntry = write_ops::UpdateOpEntry();
+ pullUpdateOpEntry.setUpsert(false);
+ pullUpdateOpEntry.setMulti(false);
+ pullUpdateOpEntry.setQ(BSON("_id"_sd << idElement));
+ pullUpdateOpEntry.setU(mongo::write_ops::UpdateModification(
+ pullUpdate, write_ops::UpdateModification::ModifierUpdateTag{}));
+ newUpdateRequest.setUpdates({pullUpdateOpEntry});
+ newUpdateRequest.setLegacyRuntimeConstants(boost::none);
+ newUpdateRequest.getWriteCommandRequestBase().setStmtId(boost::none);
+ newUpdateRequest.getWriteCommandRequestBase().setEncryptionInformation(boost::none);
+
+ auto finalUpdateReply = queryImpl->update(edcNss, stmtId, newUpdateRequest);
+ checkWriteErrors(finalUpdateReply);
}
- newFields = EDCServerCollection::getEncryptedIndexedFields(newDocument);
+ return reply;
+ }
+
+ // Is this a delete? If so, there's no need to GarbageCollect.
+ if (findAndModifyRequest.getRemove().value_or(false)) {
+ return reply;
+ }
+
+ // Validate that the original document does not contain values with on-disk version
+ // incompatible with the current protocol version.
+ EDCServerCollection::validateModifiedDocumentCompatibility(originalDocument);
+
+ auto newDocument = queryImpl->getById(edcNss, idElement);
+
+ // Fail if we could not find the new document
+ uassert(7293302, "Could not find pre-image document by _id", !newDocument.isEmpty());
+
+ if (hasIndexedFieldsInSchema(efc.getFields())) {
+ // Check the user did not remove/destroy the __safeContent__ array. If there are no
+ // indexed fields, then there will not be a safeContent array in the document.
+ FLEClientCrypto::validateTagsArray(newDocument);
}
// Step 5 ----
auto originalFields = EDCServerCollection::getEncryptedIndexedFields(originalDocument);
- auto deletedFields = EDCServerCollection::getRemovedFields(originalFields, newFields);
-
- processRemovedFields(queryImpl, edcNss, efc, tokenMap, deletedFields, &stmtId);
+ auto newFields = EDCServerCollection::getEncryptedIndexedFields(newDocument);
// Step 6 ----
- // We don't need to make a second update in the case of a delete
- if (!isDelete) {
- BSONObj pullUpdate =
- EDCServerCollection::generateUpdateToRemoveTags(deletedFields, tokenMap);
+ // GarbageCollect steps:
+ // 1. Gather the tags from the metadata block(s) of each removed field. These are stale tags.
+ // 2. Generate the update command that pulls the stale tags from __safeContent__
+ // 3. Perform the update
+ auto staleTags = EDCServerCollection::getRemovedTags(originalFields, newFields);
+
+ if (!staleTags.empty()) {
+ BSONObj pullUpdate = EDCServerCollection::generateUpdateToRemoveTags(staleTags);
auto newUpdateRequest =
write_ops::UpdateCommandRequest(findAndModifyRequest.getNamespace());
auto pullUpdateOpEntry = write_ops::UpdateOpEntry();