diff options
author | Gregory Wlodarek <gregory.wlodarek@mongodb.com> | 2022-03-09 21:04:22 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-03-09 21:52:37 +0000 |
commit | f07124df698a81e7e67023da45e8fad4d44e7628 (patch) | |
tree | 20282169c4af900eb48089b67d24d124c3418f7b | |
parent | f27d9b694591e3267dbca65771f7c02ce7018559 (diff) | |
download | mongo-f07124df698a81e7e67023da45e8fad4d44e7628.tar.gz |
SERVER-64369 Must not allow deletes from capped collections in FCV 4.4
-rw-r--r-- | jstests/multiVersion/capped_deletes.js | 23 | ||||
-rw-r--r-- | jstests/replsets/apply_ops_capped_collection.js | 1 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_impl.cpp | 20 | ||||
-rw-r--r-- | src/mongo/db/query/get_executor.cpp | 24 |
4 files changed, 59 insertions, 9 deletions
diff --git a/jstests/multiVersion/capped_deletes.js b/jstests/multiVersion/capped_deletes.js new file mode 100644 index 00000000000..8aa9b7a0a49 --- /dev/null +++ b/jstests/multiVersion/capped_deletes.js @@ -0,0 +1,23 @@ +/** + * Test that user deletes on capped collections are only allowed in FCV 5.0. + */ +(function() { +"use strict"; + +const conn = MongoRunner.runMongod(); +const db = conn.getDB("test"); + +assert.commandWorked(db.createCollection("a", {capped: true, size: 1024})); +assert.commandWorked(db.a.insert({_id: 1})); +assert.commandWorked(db.a.insert({_id: 2})); + +// FCV 5.0. +assert.commandWorked(db.adminCommand({setFeatureCompatibilityVersion: latestFCV})); +assert.commandWorked(db.a.remove({_id: 1})); + +// FCV 4.4. +assert.commandWorked(db.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV})); +assert.writeErrorWithCode(db.a.remove({_id: 2}), ErrorCodes.IllegalOperation); + +MongoRunner.stopMongod(conn); +})(); diff --git a/jstests/replsets/apply_ops_capped_collection.js b/jstests/replsets/apply_ops_capped_collection.js index cfdd7ec6299..aedbcbf5840 100644 --- a/jstests/replsets/apply_ops_capped_collection.js +++ b/jstests/replsets/apply_ops_capped_collection.js @@ -6,6 +6,7 @@ * multiversion_incompatible, * requires_capped, * requires_replication, + * requires_fcv_50, * ] */ (function() { diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp index faad2b4ee34..b669a982a5e 100644 --- a/src/mongo/db/catalog/collection_impl.cpp +++ b/src/mongo/db/catalog/collection_impl.cpp @@ -1138,9 +1138,23 @@ void CollectionImpl::deleteDocument(OperationContext* opCtx, bool fromMigrate, bool noWarn, Collection::StoreDeletedDoc storeDeletedDoc) const { - if (isCapped() && opCtx->inMultiDocumentTransaction()) { - uasserted(ErrorCodes::IllegalOperation, - "Cannot remove from a capped collection in a multi-document transaction"); + if (isCapped()) { + const auto isFCV50 = serverGlobalParams.featureCompatibility.isVersionInitialized() && + serverGlobalParams.featureCompatibility.getVersion() == + ServerGlobalParams::FeatureCompatibility::Version::kVersion50; + + if (isFCV50) { + if (opCtx->inMultiDocumentTransaction()) { + // User deletes outside of multi-document transacations can only happen in FCV 5.0. + uasserted(ErrorCodes::IllegalOperation, + "Cannot remove from a capped collection in a multi-document transaction"); + } + } else if (opCtx->isEnforcingConstraints()) { + // System operations such as tenant migration or secondary batch application can delete + // from capped collections. + LOGV2(20291, "failing remove on a capped ns", "namespace"_attr = _ns); + uasserted(10089, "cannot remove from a capped collection"); + } } std::vector<OplogSlot> oplogSlots; diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp index 500722734d7..7acf1ee351d 100644 --- a/src/mongo/db/query/get_executor.cpp +++ b/src/mongo/db/query/get_executor.cpp @@ -1327,16 +1327,28 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorDele } } - if (collection && collection->isCapped() && opCtx->inMultiDocumentTransaction()) { + if (collection && collection->isCapped()) { // This check is duplicated from CollectionImpl::deleteDocument() for two reasons: // - Performing a remove on an empty capped collection would not call // CollectionImpl::deleteDocument(). // - We can avoid doing lookups on documents and erroring later when trying to delete them. - return Status( - ErrorCodes::IllegalOperation, - str::stream() - << "Cannot remove from a capped collection in a multi-document transaction: " - << nss.ns()); + const auto isFCV50 = serverGlobalParams.featureCompatibility.isVersionInitialized() && + serverGlobalParams.featureCompatibility.getVersion() == + ServerGlobalParams::FeatureCompatibility::Version::kVersion50; + + if (isFCV50) { + if (opCtx->inMultiDocumentTransaction()) { + return Status(ErrorCodes::IllegalOperation, + str::stream() << "Cannot remove from a capped collection in a " + "multi-document transaction: " + << nss.ns()); + } + } else if (opCtx->isEnforcingConstraints()) { + // System operations such as tenant migration or secondary batch application can delete + // from capped collections. + return Status(ErrorCodes::IllegalOperation, + str::stream() << "cannot remove from a capped collection: " << nss.ns()); + } } bool userInitiatedWritesAndNotPrimary = opCtx->writesAreReplicated() && |