summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/repl/storage_interface.h9
-rw-r--r--src/mongo/db/repl/storage_interface_impl.cpp38
-rw-r--r--src/mongo/db/repl/storage_interface_impl.h4
-rw-r--r--src/mongo/db/repl/storage_interface_impl_test.cpp42
-rw-r--r--src/mongo/db/repl/storage_interface_mock.h6
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,