diff options
-rw-r--r-- | src/mongo/db/repl/storage_interface.h | 9 | ||||
-rw-r--r-- | src/mongo/db/repl/storage_interface_impl.cpp | 38 | ||||
-rw-r--r-- | src/mongo/db/repl/storage_interface_impl.h | 4 | ||||
-rw-r--r-- | src/mongo/db/repl/storage_interface_impl_test.cpp | 42 | ||||
-rw-r--r-- | src/mongo/db/repl/storage_interface_mock.h | 6 |
5 files changed, 97 insertions, 2 deletions
diff --git a/src/mongo/db/repl/storage_interface.h b/src/mongo/db/repl/storage_interface.h index 020135634d9..2cc34104b83 100644 --- a/src/mongo/db/repl/storage_interface.h +++ b/src/mongo/db/repl/storage_interface.h @@ -263,6 +263,15 @@ public: std::size_t limit) = 0; /** + * Finds a single document in the collection referenced by the specified _id. + * + * Not supported on collections with a default collation. + */ + virtual StatusWith<BSONObj> findById(OperationContext* opCtx, + const NamespaceString& nss, + const BSONElement& idKey) = 0; + + /** * Updates a single document in the collection referenced by the specified _id. * The document is located by looking up "idKey" in the id index. * "update" represents the replacement document or list of requested modifications to be applied diff --git a/src/mongo/db/repl/storage_interface_impl.cpp b/src/mongo/db/repl/storage_interface_impl.cpp index 5700f0a4fcd..a51143aaedc 100644 --- a/src/mongo/db/repl/storage_interface_impl.cpp +++ b/src/mongo/db/repl/storage_interface_impl.cpp @@ -86,6 +86,9 @@ namespace { using UniqueLock = stdx::unique_lock<stdx::mutex>; const BSONObj kInitialSyncFlag(BSON(StorageInterfaceImpl::kInitialSyncFlagFieldName << true)); + +const auto kIdIndexName = "_id_"_sd; + } // namespace StorageInterfaceImpl::StorageInterfaceImpl() @@ -467,7 +470,8 @@ DeleteStageParams makeDeleteStageParamsForDeleteDocuments() { } /** - * Shared implementation between findDocuments and deleteDocuments. + * Shared implementation between findDocuments, deleteDocuments, and _findOrDeleteById. + * _findOrDeleteById is used by findById, and deleteById. */ enum class FindDeleteMode { kFind, kDelete }; StatusWith<std::vector<BSONObj>> _findOrDeleteDocuments( @@ -476,11 +480,12 @@ StatusWith<std::vector<BSONObj>> _findOrDeleteDocuments( boost::optional<StringData> indexName, StorageInterface::ScanDirection scanDirection, const BSONObj& startKey, + const BSONObj& endKey, BoundInclusion boundInclusion, std::size_t limit, FindDeleteMode mode) { auto isFind = mode == FindDeleteMode::kFind; - auto opStr = isFind ? "StorageInterfaceImpl::findOne" : "StorageInterfaceImpl::deleteOne"; + auto opStr = isFind ? "StorageInterfaceImpl::find" : "StorageInterfaceImpl::delete"; MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { auto collectionAccessMode = isFind ? MODE_IS : MODE_IX; @@ -544,6 +549,9 @@ StatusWith<std::vector<BSONObj>> _findOrDeleteDocuments( if (!startKey.isEmpty()) { bounds.first = startKey; } + if (!endKey.isEmpty()) { + bounds.second = endKey; + } planExecutor = isFind ? InternalPlanner::indexScan(opCtx, collection, @@ -597,6 +605,7 @@ StatusWith<std::vector<BSONObj>> StorageInterfaceImpl::findDocuments( indexName, scanDirection, startKey, + {}, boundInclusion, limit, FindDeleteMode::kFind); @@ -615,11 +624,36 @@ StatusWith<std::vector<BSONObj>> StorageInterfaceImpl::deleteDocuments( indexName, scanDirection, startKey, + {}, boundInclusion, limit, FindDeleteMode::kDelete); } +StatusWith<BSONObj> StorageInterfaceImpl::findById(OperationContext* opCtx, + const NamespaceString& nss, + const BSONElement& idKey) { + auto wrappedIdKey = idKey.wrap(""); + auto result = _findOrDeleteDocuments(opCtx, + nss, + kIdIndexName, + ScanDirection::kForward, + wrappedIdKey, + wrappedIdKey, + BoundInclusion::kIncludeBothStartAndEndKeys, + 1U, + FindDeleteMode::kFind); + if (!result.isOK()) { + return result.getStatus(); + } + const auto& docs = result.getValue(); + if (docs.empty()) { + return {ErrorCodes::NoSuchKey, str::stream() << "No document found with _id: " << idKey}; + } + + return docs.front(); +} + namespace { /** diff --git a/src/mongo/db/repl/storage_interface_impl.h b/src/mongo/db/repl/storage_interface_impl.h index 6a6fdbecb82..b5769c69cc1 100644 --- a/src/mongo/db/repl/storage_interface_impl.h +++ b/src/mongo/db/repl/storage_interface_impl.h @@ -117,6 +117,10 @@ public: BoundInclusion boundInclusion, std::size_t limit) override; + StatusWith<BSONObj> findById(OperationContext* opCtx, + const NamespaceString& nss, + const BSONElement& idKey) override; + Status upsertById(OperationContext* opCtx, const NamespaceString& nss, const BSONElement& idKey, diff --git a/src/mongo/db/repl/storage_interface_impl_test.cpp b/src/mongo/db/repl/storage_interface_impl_test.cpp index b23b9841bda..68fde3bf366 100644 --- a/src/mongo/db/repl/storage_interface_impl_test.cpp +++ b/src/mongo/db/repl/storage_interface_impl_test.cpp @@ -1488,6 +1488,48 @@ TEST_F(StorageInterfaceImplTest, .getStatus()); } +TEST_F(StorageInterfaceImplTest, FindByIdReturnsNamespaceNotFoundWhenDatabaseDoesNotExist) { + auto opCtx = getOperationContext(); + StorageInterfaceImpl storage; + NamespaceString nss("nosuchdb.coll"); + auto doc = BSON("_id" << 0 << "x" << 0); + ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, + storage.findById(opCtx, nss, doc["_id"]).getStatus()); +} + +TEST_F(StorageInterfaceImplTest, FindByIdReturnsNoSuchKeyWhenCollectionIsEmpty) { + auto opCtx = getOperationContext(); + StorageInterfaceImpl storage; + auto nss = makeNamespace(_agent); + ASSERT_OK(storage.createCollection(opCtx, nss, CollectionOptions())); + auto doc = BSON("_id" << 0 << "x" << 0); + ASSERT_EQUALS(ErrorCodes::NoSuchKey, storage.findById(opCtx, nss, doc["_id"]).getStatus()); +} + +TEST_F(StorageInterfaceImplTest, FindByIdReturnsNoSuchKeyWhenDocumentIsNotFound) { + auto opCtx = getOperationContext(); + StorageInterfaceImpl storage; + auto nss = makeNamespace(_agent); + ASSERT_OK(storage.createCollection(opCtx, nss, CollectionOptions())); + auto doc1 = BSON("_id" << 0 << "x" << 0); + auto doc2 = BSON("_id" << 1 << "x" << 1); + auto doc3 = BSON("_id" << 2 << "x" << 2); + ASSERT_OK(storage.insertDocuments(opCtx, nss, {doc1, doc3})); + ASSERT_EQUALS(ErrorCodes::NoSuchKey, storage.findById(opCtx, nss, doc2["_id"]).getStatus()); +} + +TEST_F(StorageInterfaceImplTest, FindByIdReturnsDocumentWhenDocumentExists) { + auto opCtx = getOperationContext(); + StorageInterfaceImpl storage; + auto nss = makeNamespace(_agent); + ASSERT_OK(storage.createCollection(opCtx, nss, CollectionOptions())); + auto doc1 = BSON("_id" << 0 << "x" << 0); + auto doc2 = BSON("_id" << 1 << "x" << 1); + auto doc3 = BSON("_id" << 2 << "x" << 2); + ASSERT_OK(storage.insertDocuments(opCtx, nss, {doc1, doc2, doc3})); + ASSERT_BSONOBJ_EQ(doc2, unittest::assertGet(storage.findById(opCtx, nss, doc2["_id"]))); +} + TEST_F(StorageInterfaceImplTest, UpsertSingleDocumentReturnsNamespaceNotFoundWhenDatabaseDoesNotExist) { auto opCtx = getOperationContext(); diff --git a/src/mongo/db/repl/storage_interface_mock.h b/src/mongo/db/repl/storage_interface_mock.h index 6f13b68abe2..f6df26cd2df 100644 --- a/src/mongo/db/repl/storage_interface_mock.h +++ b/src/mongo/db/repl/storage_interface_mock.h @@ -199,6 +199,12 @@ public: opCtx, nss, indexName, scanDirection, startKey, boundInclusion, limit); } + StatusWith<BSONObj> findById(OperationContext* opCtx, + const NamespaceString& nss, + const BSONElement& idKey) override { + return Status{ErrorCodes::IllegalOperation, "findById not implemented."}; + } + Status upsertById(OperationContext* opCtx, const NamespaceString& nss, const BSONElement& idKey, |