summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/SConscript4
-rw-r--r--src/mongo/db/catalog/catalog_raii.cpp118
-rw-r--r--src/mongo/db/catalog/catalog_raii.h47
-rw-r--r--src/mongo/db/catalog/catalog_raii_test.cpp2
-rw-r--r--src/mongo/db/catalog/database_test.cpp33
-rw-r--r--src/mongo/db/commands.cpp18
-rw-r--r--src/mongo/db/commands.h4
-rw-r--r--src/mongo/db/commands/count_cmd.cpp60
-rw-r--r--src/mongo/db/commands/distinct.cpp63
-rw-r--r--src/mongo/db/commands/find_and_modify.cpp105
-rw-r--r--src/mongo/db/commands/find_cmd.cpp48
-rw-r--r--src/mongo/db/commands/list_indexes.cpp60
-rw-r--r--src/mongo/db/commands/parallel_collection_scan.cpp44
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp12
-rw-r--r--src/mongo/db/commands_test.cpp34
-rw-r--r--src/mongo/db/cursor_manager.cpp9
-rw-r--r--src/mongo/db/db_raii.cpp214
-rw-r--r--src/mongo/db/db_raii.h193
-rw-r--r--src/mongo/db/namespace_string.cpp8
-rw-r--r--src/mongo/db/namespace_string.h15
-rw-r--r--src/mongo/db/ops/SConscript6
-rw-r--r--src/mongo/db/ops/write_ops_exec.cpp2
-rw-r--r--src/mongo/db/ops/write_ops_retryability.cpp1
-rw-r--r--src/mongo/db/pipeline/pipeline_d.cpp7
-rw-r--r--src/mongo/db/query/find.cpp13
-rw-r--r--src/mongo/db/repl/sync_tail.cpp6
26 files changed, 460 insertions, 666 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 80e0345c93a..83ee5a45eca 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -525,11 +525,10 @@ env.Library(
'$BUILD_DIR/mongo/rpc/rpc',
'audit',
'auth/authcore',
+ 'command_can_run_here',
'commands/server_status_core',
'commands/test_commands_enabled',
- 'command_can_run_here',
'service_context',
- '$BUILD_DIR/mongo/db/catalog/uuid_catalog',
],
)
@@ -638,7 +637,6 @@ env.Library(
'rw_concern_d',
],
LIBDEPS_PRIVATE=[
- '$BUILD_DIR/mongo/db/ops/write_ops_exec',
'$BUILD_DIR/mongo/db/commands/fsync_locked',
'$BUILD_DIR/mongo/db/service_entry_point_common',
],
diff --git a/src/mongo/db/catalog/catalog_raii.cpp b/src/mongo/db/catalog/catalog_raii.cpp
index 2888c300845..379f40c982a 100644
--- a/src/mongo/db/catalog/catalog_raii.cpp
+++ b/src/mongo/db/catalog/catalog_raii.cpp
@@ -55,16 +55,11 @@ void uassertLockTimeout(std::string resourceName,
} // namespace
AutoGetDb::AutoGetDb(OperationContext* opCtx, StringData dbName, LockMode mode, Date_t deadline)
- : _dbLock(opCtx, dbName, mode, deadline), _db(dbHolder().get(opCtx, dbName)) {
- uassertLockTimeout("database " + dbName, mode, deadline, _dbLock.isLocked());
-}
-
-AutoGetDb::AutoGetDb(OperationContext* opCtx, StringData dbName, Lock::DBLock dbLock)
- : _dbLock(std::move(dbLock)), _db(dbHolder().get(opCtx, dbName)) {
- uassert(ErrorCodes::LockTimeout,
- str::stream() << "Failed to acquire lock for '" << dbName << "'.",
- _dbLock.isLocked());
-}
+ : _dbLock(opCtx, dbName, mode, deadline), _db([&] {
+ uassertLockTimeout(
+ str::stream() << "database " << dbName, mode, deadline, _dbLock.isLocked());
+ return dbHolder().get(opCtx, dbName);
+ }()) {}
AutoGetCollection::AutoGetCollection(OperationContext* opCtx,
const NamespaceStringOrUUID& nsOrUUID,
@@ -72,87 +67,88 @@ AutoGetCollection::AutoGetCollection(OperationContext* opCtx,
LockMode modeColl,
ViewMode viewMode,
Date_t deadline)
- : AutoGetCollection(opCtx,
- nsOrUUID,
- Lock::DBLock(opCtx, nsOrUUID.db(), modeDB, deadline),
- modeColl,
- viewMode,
- deadline) {}
+ : // The UUID to NamespaceString resolution is performed outside of any locks
+ _resolvedNss(resolveNamespaceStringOrUUID(opCtx, nsOrUUID)),
+ // The database locking is performed based on the resolved NamespaceString
+ _autoDb(opCtx, _resolvedNss.db(), modeDB, deadline) {
+ // In order to account for possible collection rename happening because the resolution from UUID
+ // to NamespaceString was done outside of database lock, if UUID was specified we need to
+ // re-resolve the _resolvedNss after acquiring the database lock so it has the correct value.
+ //
+ // Holding a database lock prevents collection renames, so this guarantees a stable UUID to
+ // NamespaceString mapping.
+ if (nsOrUUID.uuid())
+ _resolvedNss = resolveNamespaceStringOrUUID(opCtx, nsOrUUID);
+
+ _collLock.emplace(opCtx->lockState(), _resolvedNss.ns(), modeColl, deadline);
+ uassertLockTimeout(str::stream() << "collection " << nsOrUUID.toString(),
+ modeColl,
+ deadline,
+ _collLock->isLocked());
-AutoGetCollection::AutoGetCollection(OperationContext* opCtx,
- const NamespaceStringOrUUID& nsOrUUID,
- Lock::DBLock dbLock,
- LockMode modeColl,
- ViewMode viewMode,
- Date_t deadline)
- : _autoDb(opCtx, nsOrUUID.db(), std::move(dbLock)),
- _nsAndLock([&]() -> NamespaceAndCollectionLock {
- if (nsOrUUID.nss()) {
- return {Lock::CollectionLock(
- opCtx->lockState(), nsOrUUID.nss()->ns(), modeColl, deadline),
- *nsOrUUID.nss()};
- } else {
- UUIDCatalog& catalog = UUIDCatalog::get(opCtx);
- auto resolvedNss = catalog.lookupNSSByUUID(nsOrUUID.dbAndUUID()->uuid);
-
- // If the collection UUID cannot be resolved, we can't obtain a collection or check
- // for vews
- uassert(ErrorCodes::NamespaceNotFound,
- str::stream() << "Unable to resolve " << nsOrUUID.toString(),
- resolvedNss.isValid());
-
- return {
- Lock::CollectionLock(opCtx->lockState(), resolvedNss.ns(), modeColl, deadline),
- std::move(resolvedNss)};
- }
- }()) {
// Wait for a configured amount of time after acquiring locks if the failpoint is enabled
MONGO_FAIL_POINT_BLOCK(setAutoGetCollectionWait, customWait) {
const BSONObj& data = customWait.getData();
sleepFor(Milliseconds(data["waitForMillis"].numberInt()));
}
- uassertLockTimeout(
- "collection " + nsOrUUID.toString(), modeColl, deadline, _nsAndLock.lock.isLocked());
-
Database* const db = _autoDb.getDb();
+ invariant(!nsOrUUID.uuid() || db,
+ str::stream() << "Database for " << _resolvedNss.ns()
+ << " disappeared after successufully resolving "
+ << nsOrUUID.toString());
// If the database doesn't exists, we can't obtain a collection or check for views
if (!db)
return;
- _coll = db->getCollection(opCtx, _nsAndLock.nss);
+ _coll = db->getCollection(opCtx, _resolvedNss);
+ invariant(!nsOrUUID.uuid() || _coll,
+ str::stream() << "Collection for " << _resolvedNss.ns()
+ << " disappeared after successufully resolving "
+ << nsOrUUID.toString());
- // If the collection exists, there is no need to check for views and we must keep the collection
- // lock
- if (_coll) {
+ // If the collection exists, there is no need to check for views
+ if (_coll)
return;
- }
- _view = db->getViewCatalog()->lookup(opCtx, _nsAndLock.nss.ns());
+ _view = db->getViewCatalog()->lookup(opCtx, _resolvedNss.ns());
uassert(ErrorCodes::CommandNotSupportedOnView,
- str::stream() << "Namespace " << _nsAndLock.nss.ns() << " is a view, not a collection",
+ str::stream() << "Namespace " << _resolvedNss.ns() << " is a view, not a collection",
!_view || viewMode == kViewsPermitted);
}
+NamespaceString AutoGetCollection::resolveNamespaceStringOrUUID(OperationContext* opCtx,
+ NamespaceStringOrUUID nsOrUUID) {
+ if (nsOrUUID.nss())
+ return *nsOrUUID.nss();
+
+ UUIDCatalog& uuidCatalog = UUIDCatalog::get(opCtx);
+ auto resolvedNss = uuidCatalog.lookupNSSByUUID(*nsOrUUID.uuid());
+
+ uassert(ErrorCodes::NamespaceNotFound,
+ str::stream() << "Unable to resolve " << nsOrUUID.toString(),
+ resolvedNss.isValid());
+
+ return resolvedNss;
+}
+
AutoGetOrCreateDb::AutoGetOrCreateDb(OperationContext* opCtx,
StringData dbName,
LockMode mode,
- Date_t deadline)
- : _dbLock(opCtx, dbName, mode, deadline), _db(dbHolder().get(opCtx, dbName)) {
+ Date_t deadline) {
invariant(mode == MODE_IX || mode == MODE_X);
- _justCreated = false;
- uassertLockTimeout("database " + dbName, mode, deadline, _dbLock.isLocked());
+ _autoDb.emplace(opCtx, dbName, mode, deadline);
+ _db = _autoDb->getDb();
// If the database didn't exist, relock in MODE_X
- if (_db == NULL) {
+ if (!_db) {
if (mode != MODE_X) {
- _dbLock.relockWithMode(MODE_X);
+ _autoDb.emplace(opCtx, dbName, MODE_X, deadline);
}
- _db = dbHolder().openDb(opCtx, dbName);
- _justCreated = true;
+ _db = dbHolder().openDb(opCtx, dbName, &_justCreated);
}
}
diff --git a/src/mongo/db/catalog/catalog_raii.h b/src/mongo/db/catalog/catalog_raii.h
index bd1356e1a83..70d71382b35 100644
--- a/src/mongo/db/catalog/catalog_raii.h
+++ b/src/mongo/db/catalog/catalog_raii.h
@@ -57,9 +57,6 @@ public:
LockMode mode,
Date_t deadline = Date_t::max());
- // TODO (SERVER-32367): Do not use this constructor, it is for internal purposes only
- AutoGetDb(OperationContext* opCtx, StringData dbName, Lock::DBLock dbLock);
-
/**
* Returns nullptr if the database didn't exist.
*/
@@ -89,27 +86,26 @@ public:
AutoGetCollection(OperationContext* opCtx,
const NamespaceStringOrUUID& nsOrUUID,
- LockMode modeDB,
- LockMode modeColl,
- ViewMode viewMode = kViewsForbidden,
- Date_t deadline = Date_t::max());
-
- AutoGetCollection(OperationContext* opCtx,
- const NamespaceStringOrUUID& nsOrUUID,
LockMode modeAll,
ViewMode viewMode = kViewsForbidden,
Date_t deadline = Date_t::max())
: AutoGetCollection(opCtx, nsOrUUID, modeAll, modeAll, viewMode, deadline) {}
- // TODO (SERVER-32367): Do not use this constructor, it is for internal purposes only
AutoGetCollection(OperationContext* opCtx,
const NamespaceStringOrUUID& nsOrUUID,
- Lock::DBLock dbLock,
+ LockMode modeDB,
LockMode modeColl,
- ViewMode viewMode,
+ ViewMode viewMode = kViewsForbidden,
Date_t deadline = Date_t::max());
/**
+ * Without acquiring any locks resolves the given NamespaceStringOrUUID to an actual namespace.
+ * Throws NamespaceNotFound if the collection UUID cannot be resolved to a name.
+ */
+ static NamespaceString resolveNamespaceStringOrUUID(OperationContext* opCtx,
+ NamespaceStringOrUUID nsOrUUID);
+
+ /**
* Returns nullptr if the database didn't exist.
*/
Database* getDb() const {
@@ -134,17 +130,19 @@ public:
* Returns the resolved namespace of the collection or view.
*/
const NamespaceString& getNss() const {
- return _nsAndLock.nss;
+ return _resolvedNss;
}
private:
- struct NamespaceAndCollectionLock {
- Lock::CollectionLock lock;
- NamespaceString nss;
- };
+ // If the object was instantiated with a UUID, contains the resolved namespace, otherwise it is
+ // the same as the input namespace string
+ NamespaceString _resolvedNss;
AutoGetDb _autoDb;
- const NamespaceAndCollectionLock _nsAndLock;
+
+ // This field is boost::optional, because in the case of lookup by UUID, the collection lock
+ // might need to be relocked for the correct namespace
+ boost::optional<Lock::CollectionLock> _collLock;
Collection* _coll = nullptr;
std::shared_ptr<ViewDefinition> _view;
@@ -168,7 +166,7 @@ class AutoGetOrCreateDb {
public:
AutoGetOrCreateDb(OperationContext* opCtx,
- StringData ns,
+ StringData dbName,
LockMode mode,
Date_t deadline = Date_t::max());
@@ -180,14 +178,11 @@ public:
return _justCreated;
}
- Lock::DBLock& lock() {
- return _dbLock;
- }
-
private:
- Lock::DBLock _dbLock; // not const, as we may need to relock for implicit create
+ boost::optional<AutoGetDb> _autoDb;
+
Database* _db;
- bool _justCreated;
+ bool _justCreated{false};
};
} // namespace mongo
diff --git a/src/mongo/db/catalog/catalog_raii_test.cpp b/src/mongo/db/catalog/catalog_raii_test.cpp
index a8ba8ae8de7..3066d2aa1e6 100644
--- a/src/mongo/db/catalog/catalog_raii_test.cpp
+++ b/src/mongo/db/catalog/catalog_raii_test.cpp
@@ -57,7 +57,7 @@ public:
}
const NamespaceString nss = NamespaceString("test", "coll");
- const Milliseconds timeoutMs = Milliseconds(1500);
+ const Milliseconds timeoutMs = Seconds(1);
const ClientAndCtx client1 = makeClientWithLocker("client1");
const ClientAndCtx client2 = makeClientWithLocker("client2");
};
diff --git a/src/mongo/db/catalog/database_test.cpp b/src/mongo/db/catalog/database_test.cpp
index 4b22f4f4c5f..d81392f1972 100644
--- a/src/mongo/db/catalog/database_test.cpp
+++ b/src/mongo/db/catalog/database_test.cpp
@@ -542,28 +542,6 @@ TEST_F(
});
}
-TEST_F(DatabaseTest, DBLockCanBePassedToAutoGetDb) {
- NamespaceString nss("test", "coll");
- Lock::DBLock lock(_opCtx.get(), nss.db(), MODE_X);
- {
- AutoGetDb db(_opCtx.get(), nss.db(), std::move(lock));
- ASSERT(_opCtx.get()->lockState()->isDbLockedForMode(nss.db(), MODE_X));
- }
- // The moved lock should go out of scope here, so the database should no longer be locked.
- ASSERT_FALSE(_opCtx.get()->lockState()->isDbLockedForMode(nss.db(), MODE_X));
-}
-
-TEST_F(DatabaseTest, DBLockCanBePassedToAutoGetCollectionOrViewForReadCommand) {
- NamespaceString nss("test", "coll");
- Lock::DBLock lock(_opCtx.get(), nss.db(), MODE_X);
- {
- AutoGetCollectionOrViewForReadCommand coll(_opCtx.get(), nss, std::move(lock));
- ASSERT(_opCtx.get()->lockState()->isDbLockedForMode(nss.db(), MODE_X));
- }
- // The moved lock should go out of scope here, so the database should no longer be locked.
- ASSERT_FALSE(_opCtx.get()->lockState()->isDbLockedForMode(nss.db(), MODE_X));
-}
-
TEST_F(DatabaseTest, AutoGetDBSucceedsWithDeadlineNow) {
NamespaceString nss("test", "coll");
Lock::DBLock lock(_opCtx.get(), nss.db(), MODE_X);
@@ -588,29 +566,32 @@ TEST_F(DatabaseTest, AutoGetDBSucceedsWithDeadlineMin) {
}
}
-TEST_F(DatabaseTest, AutoGetCollectionOrViewForReadCommandSucceedsWithDeadlineNow) {
+TEST_F(DatabaseTest, AutoGetCollectionForReadCommandSucceedsWithDeadlineNow) {
NamespaceString nss("test", "coll");
Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_X);
ASSERT(_opCtx.get()->lockState()->isDbLockedForMode(nss.db(), MODE_X));
Lock::CollectionLock collLock(_opCtx.get()->lockState(), nss.toString(), MODE_X);
ASSERT(_opCtx.get()->lockState()->isCollectionLockedForMode(nss.toString(), MODE_X));
try {
- AutoGetCollectionOrViewForReadCommand db(_opCtx.get(), nss, Date_t::now());
+ AutoGetCollectionForReadCommand db(
+ _opCtx.get(), nss, AutoGetCollection::kViewsForbidden, Date_t::now());
} catch (const ExceptionFor<ErrorCodes::LockTimeout>&) {
FAIL("Should get the db within the timeout");
}
}
-TEST_F(DatabaseTest, AutoGetCollectionOrViewForReadCommandSucceedsWithDeadlineMin) {
+TEST_F(DatabaseTest, AutoGetCollectionForReadCommandSucceedsWithDeadlineMin) {
NamespaceString nss("test", "coll");
Lock::DBLock dbLock(_opCtx.get(), nss.db(), MODE_X);
ASSERT(_opCtx.get()->lockState()->isDbLockedForMode(nss.db(), MODE_X));
Lock::CollectionLock collLock(_opCtx.get()->lockState(), nss.toString(), MODE_X);
ASSERT(_opCtx.get()->lockState()->isCollectionLockedForMode(nss.toString(), MODE_X));
try {
- AutoGetCollectionOrViewForReadCommand db(_opCtx.get(), nss, Date_t::min());
+ AutoGetCollectionForReadCommand db(
+ _opCtx.get(), nss, AutoGetCollection::kViewsForbidden, Date_t::min());
} catch (const ExceptionFor<ErrorCodes::LockTimeout>&) {
FAIL("Should get the db within the timeout");
}
}
+
} // namespace
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index e42c0ffda58..94f93bddc4e 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -43,7 +43,6 @@
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/privilege.h"
-#include "mongo/db/catalog/uuid_catalog.h"
#include "mongo/db/client.h"
#include "mongo/db/curop.h"
#include "mongo/db/jsobj.h"
@@ -122,23 +121,10 @@ NamespaceString CommandHelpers::parseNsCollectionRequired(StringData dbname,
return nss;
}
-NamespaceString CommandHelpers::parseNsOrUUID(OperationContext* opCtx,
- StringData dbname,
- const BSONObj& cmdObj) {
+NamespaceStringOrUUID CommandHelpers::parseNsOrUUID(StringData dbname, const BSONObj& cmdObj) {
BSONElement first = cmdObj.firstElement();
if (first.type() == BinData && first.binDataType() == BinDataType::newUUID) {
- UUIDCatalog& catalog = UUIDCatalog::get(opCtx);
- UUID uuid = uassertStatusOK(UUID::parse(first));
- NamespaceString nss = catalog.lookupNSSByUUID(uuid);
- uassert(ErrorCodes::NamespaceNotFound,
- str::stream() << "UUID " << uuid << " specified in "
- << cmdObj.firstElement().fieldNameStringData()
- << " command not found in "
- << dbname
- << " database",
- nss.isValid() && nss.db() == dbname);
-
- return nss;
+ return uassertStatusOK(UUID::parse(first));
} else {
// Ensure collection identifier is not a Command
const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h
index 8b2f40b05e9..9ed9e27ccca 100644
--- a/src/mongo/db/commands.h
+++ b/src/mongo/db/commands.h
@@ -69,9 +69,7 @@ struct CommandHelpers {
// The first field is interpreted as a collection name.
static NamespaceString parseNsCollectionRequired(StringData dbname, const BSONObj& cmdObj);
- static NamespaceString parseNsOrUUID(OperationContext* opCtx,
- StringData dbname,
- const BSONObj& cmdObj);
+ static NamespaceStringOrUUID parseNsOrUUID(StringData dbname, const BSONObj& cmdObj);
static Command* findCommand(StringData name);
diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp
index 655fdd1a0af..306c339f41a 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -102,12 +102,11 @@ public:
return Status(ErrorCodes::Unauthorized, "Unauthorized");
}
- const NamespaceString nss(CommandHelpers::parseNsOrUUID(opCtx, dbname, cmdObj));
- if (!authSession->isAuthorizedForActionsOnNamespace(nss, ActionType::find)) {
- return Status(ErrorCodes::Unauthorized, "Unauthorized");
- }
-
- return Status::OK();
+ const auto hasTerm = false;
+ return authSession->checkAuthForFind(
+ AutoGetCollection::resolveNamespaceStringOrUUID(
+ opCtx, CommandHelpers::parseNsOrUUID(dbname, cmdObj)),
+ hasTerm);
}
Status explain(OperationContext* opCtx,
@@ -115,21 +114,23 @@ public:
const BSONObj& cmdObj,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
+ // Acquire locks and resolve possible UUID. The RAII object is optional, because in the case
+ // of a view, the locks need to be released.
+ boost::optional<AutoGetCollectionForReadCommand> ctx;
+ ctx.emplace(opCtx,
+ CommandHelpers::parseNsOrUUID(dbname, cmdObj),
+ AutoGetCollection::ViewMode::kViewsPermitted);
+ const auto nss = ctx->getNss();
+
const bool isExplain = true;
- Lock::DBLock dbLock(opCtx, dbname, MODE_IS);
- auto nss = CommandHelpers::parseNsOrUUID(opCtx, dbname, cmdObj);
auto request = CountRequest::parseFromBSON(nss, cmdObj, isExplain);
if (!request.isOK()) {
return request.getStatus();
}
- // Acquire the db read lock.
- AutoGetCollectionOrViewForReadCommand ctx(
- opCtx, request.getValue().getNs(), std::move(dbLock));
- Collection* collection = ctx.getCollection();
-
- if (ctx.getView()) {
- ctx.releaseLocksForView();
+ if (ctx->getView()) {
+ // Relinquish locks. The aggregation command will re-acquire them.
+ ctx.reset();
auto viewAggregation = request.getValue().asAggregationCommand();
if (!viewAggregation.isOK()) {
@@ -149,10 +150,11 @@ public:
*out);
}
+ Collection* const collection = ctx->getCollection();
+
// Prevent chunks from being cleaned up during yields - this allows us to only check the
// version on initial entry into count.
- auto rangePreserver =
- CollectionShardingState::get(opCtx, request.getValue().getNs())->getMetadata();
+ auto rangePreserver = CollectionShardingState::get(opCtx, nss)->getMetadata();
auto statusWithPlanExecutor = getExecutorCount(opCtx,
collection,
@@ -173,20 +175,23 @@ public:
const string& dbname,
const BSONObj& cmdObj,
BSONObjBuilder& result) override {
+ // Acquire locks and resolve possible UUID. The RAII object is optional, because in the case
+ // of a view, the locks need to be released.
+ boost::optional<AutoGetCollectionForReadCommand> ctx;
+ ctx.emplace(opCtx,
+ CommandHelpers::parseNsOrUUID(dbname, cmdObj),
+ AutoGetCollection::ViewMode::kViewsPermitted);
+ const auto nss = ctx->getNss();
+
const bool isExplain = false;
- Lock::DBLock dbLock(opCtx, dbname, MODE_IS);
- auto nss = CommandHelpers::parseNsOrUUID(opCtx, dbname, cmdObj);
auto request = CountRequest::parseFromBSON(nss, cmdObj, isExplain);
if (!request.isOK()) {
return CommandHelpers::appendCommandStatus(result, request.getStatus());
}
- AutoGetCollectionOrViewForReadCommand ctx(
- opCtx, request.getValue().getNs(), std::move(dbLock));
- Collection* collection = ctx.getCollection();
-
- if (ctx.getView()) {
- ctx.releaseLocksForView();
+ if (ctx->getView()) {
+ // Relinquish locks. The aggregation command will re-acquire them.
+ ctx.reset();
auto viewAggregation = request.getValue().asAggregationCommand();
if (!viewAggregation.isOK()) {
@@ -200,10 +205,11 @@ public:
return true;
}
+ Collection* const collection = ctx->getCollection();
+
// Prevent chunks from being cleaned up during yields - this allows us to only check the
// version on initial entry into count.
- auto rangePreserver =
- CollectionShardingState::get(opCtx, request.getValue().getNs())->getMetadata();
+ auto rangePreserver = CollectionShardingState::get(opCtx, nss)->getMetadata();
auto statusWithPlanExecutor = getExecutorCount(opCtx,
collection,
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index 584f9b7c979..eba4fdd51bc 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -35,13 +35,8 @@
#include <string>
#include <vector>
-#include "mongo/bson/util/bson_extract.h"
-#include "mongo/db/auth/action_set.h"
-#include "mongo/db/auth/action_type.h"
-#include "mongo/db/auth/privilege.h"
+#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/bson/dotted_path_support.h"
-#include "mongo/db/catalog/collection.h"
-#include "mongo/db/catalog/database.h"
#include "mongo/db/client.h"
#include "mongo/db/clientcursor.h"
#include "mongo/db/commands.h"
@@ -98,12 +93,20 @@ public:
return FindCommon::kInitReplyBufferSize;
}
- void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) const override {
- ActionSet actions;
- actions.addAction(ActionType::find);
- out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
+ Status checkAuthForOperation(OperationContext* opCtx,
+ const std::string& dbname,
+ const BSONObj& cmdObj) const override {
+ AuthorizationSession* authSession = AuthorizationSession::get(opCtx->getClient());
+
+ if (!authSession->isAuthorizedToParseNamespaceElement(cmdObj.firstElement())) {
+ return Status(ErrorCodes::Unauthorized, "Unauthorized");
+ }
+
+ const auto hasTerm = false;
+ return authSession->checkAuthForFind(
+ AutoGetCollection::resolveNamespaceStringOrUUID(
+ opCtx, CommandHelpers::parseNsOrUUID(dbname, cmdObj)),
+ hasTerm);
}
Status explain(OperationContext* opCtx,
@@ -111,17 +114,21 @@ public:
const BSONObj& cmdObj,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
- const NamespaceString nss(CommandHelpers::parseNsCollectionRequired(dbname, cmdObj));
+ // Acquire locks and resolve possible UUID. The RAII object is optional, because in the case
+ // of a view, the locks need to be released.
+ boost::optional<AutoGetCollectionForReadCommand> ctx;
+ ctx.emplace(opCtx,
+ CommandHelpers::parseNsOrUUID(dbname, cmdObj),
+ AutoGetCollection::ViewMode::kViewsPermitted);
+ const auto nss = ctx->getNss();
const ExtensionsCallbackReal extensionsCallback(opCtx, &nss);
auto parsedDistinct =
uassertStatusOK(ParsedDistinct::parse(opCtx, nss, cmdObj, extensionsCallback, true));
- AutoGetCollectionOrViewForReadCommand ctx(opCtx, nss);
- Collection* collection = ctx.getCollection();
-
- if (ctx.getView()) {
- ctx.releaseLocksForView();
+ if (ctx->getView()) {
+ // Relinquish locks. The aggregation command will re-acquire them.
+ ctx.reset();
auto viewAggregation = parsedDistinct.asAggregationCommand();
if (!viewAggregation.isOK()) {
@@ -138,6 +145,8 @@ public:
opCtx, nss, viewAggRequest.getValue(), viewAggregation.getValue(), *out);
}
+ Collection* const collection = ctx->getCollection();
+
auto executor = uassertStatusOK(getExecutorDistinct(
opCtx, collection, nss.ns(), &parsedDistinct, PlanExecutor::YIELD_AUTO));
@@ -149,17 +158,21 @@ public:
const std::string& dbname,
const BSONObj& cmdObj,
BSONObjBuilder& result) override {
- const NamespaceString nss(CommandHelpers::parseNsCollectionRequired(dbname, cmdObj));
+ // Acquire locks and resolve possible UUID. The RAII object is optional, because in the case
+ // of a view, the locks need to be released.
+ boost::optional<AutoGetCollectionForReadCommand> ctx;
+ ctx.emplace(opCtx,
+ CommandHelpers::parseNsOrUUID(dbname, cmdObj),
+ AutoGetCollection::ViewMode::kViewsPermitted);
+ const auto& nss = ctx->getNss();
const ExtensionsCallbackReal extensionsCallback(opCtx, &nss);
auto parsedDistinct =
uassertStatusOK(ParsedDistinct::parse(opCtx, nss, cmdObj, extensionsCallback, false));
- AutoGetCollectionOrViewForReadCommand ctx(opCtx, nss);
- Collection* collection = ctx.getCollection();
-
- if (ctx.getView()) {
- ctx.releaseLocksForView();
+ if (ctx->getView()) {
+ // Relinquish locks. The aggregation command will re-acquire them.
+ ctx.reset();
auto viewAggregation = parsedDistinct.asAggregationCommand();
if (!viewAggregation.isOK()) {
@@ -172,6 +185,8 @@ public:
return true;
}
+ Collection* const collection = ctx->getCollection();
+
auto executor = getExecutorDistinct(
opCtx, collection, nss.ns(), &parsedDistinct, PlanExecutor::YIELD_AUTO);
if (!executor.isOK()) {
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp
index 29b23b34b4a..8fdbb86a019 100644
--- a/src/mongo/db/commands/find_and_modify.cpp
+++ b/src/mongo/db/commands/find_and_modify.cpp
@@ -189,6 +189,10 @@ void assertCanWrite(OperationContext* opCtx, const NamespaceString& nsString) {
str::stream() << "Not primary while running findAndModify command on collection "
<< nsString.ns(),
repl::ReplicationCoordinator::get(opCtx)->canAcceptWritesFor(opCtx, nsString));
+
+ // Check for shard version match
+ auto css = CollectionShardingState::get(opCtx, nsString);
+ css->checkShardVersionOrThrow(opCtx);
}
void recordStatsForTopCommand(OperationContext* opCtx) {
@@ -246,11 +250,10 @@ public:
const BSONObj& cmdObj,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
- const NamespaceString fullNs = CommandHelpers::parseNsCollectionRequired(dbName, cmdObj);
- uassertStatusOK(userAllowedWriteNS(fullNs.ns()));
-
- const auto args(uassertStatusOK(FindAndModifyRequest::parseFromBSON(fullNs, cmdObj)));
+ const auto args(uassertStatusOK(FindAndModifyRequest::parseFromBSON(
+ CommandHelpers::parseNsCollectionRequired(dbName, cmdObj), cmdObj)));
const NamespaceString& nsString = args.getNamespaceString();
+ uassertStatusOK(userAllowedWriteNS(nsString));
auto const curOp = CurOp::get(opCtx);
OpDebug* const opDebug = &curOp->debug();
@@ -310,14 +313,10 @@ public:
const std::string& dbName,
const BSONObj& cmdObj,
BSONObjBuilder& result) override {
- // findAndModify command is not replicated directly.
- invariant(opCtx->writesAreReplicated());
-
- const NamespaceString fullNs = CommandHelpers::parseNsCollectionRequired(dbName, cmdObj);
- uassertStatusOK(userAllowedWriteNS(fullNs.ns()));
-
- const auto args(uassertStatusOK(FindAndModifyRequest::parseFromBSON(fullNs, cmdObj)));
+ const auto args(uassertStatusOK(FindAndModifyRequest::parseFromBSON(
+ CommandHelpers::parseNsCollectionRequired(dbName, cmdObj), cmdObj)));
const NamespaceString& nsString = args.getNamespaceString();
+ uassertStatusOK(userAllowedWriteNS(nsString));
auto const curOp = CurOp::get(opCtx);
OpDebug* const opDebug = &curOp->debug();
@@ -353,27 +352,20 @@ public:
ParsedDelete parsedDelete(opCtx, &request);
uassertStatusOK(parsedDelete.parseRequest());
- AutoGetOrCreateDb autoDb(opCtx, dbName, MODE_IX);
- Lock::CollectionLock collLock(opCtx->lockState(), nsString.ns(), MODE_IX);
+ AutoGetCollection autoColl(opCtx, nsString, MODE_IX);
{
+ boost::optional<int> dbProfilingLevel;
+ if (autoColl.getDb())
+ dbProfilingLevel = autoColl.getDb()->getProfilingLevel();
+
stdx::lock_guard<Client> lk(*opCtx->getClient());
- CurOp::get(opCtx)->enter_inlock(nsString.ns().c_str(),
- autoDb.getDb()->getProfilingLevel());
+ CurOp::get(opCtx)->enter_inlock(nsString.ns().c_str(), dbProfilingLevel);
}
- auto css = CollectionShardingState::get(opCtx, nsString);
- css->checkShardVersionOrThrow(opCtx);
-
assertCanWrite(opCtx, nsString);
- Collection* const collection = autoDb.getDb()->getCollection(opCtx, nsString);
- if (!collection && autoDb.getDb()->getViewCatalog()->lookup(opCtx, nsString.ns())) {
- CommandHelpers::appendCommandStatus(result,
- {ErrorCodes::CommandNotSupportedOnView,
- "findAndModify not supported on a view"});
- return false;
- }
+ Collection* const collection = autoColl.getCollection();
const auto exec =
uassertStatusOK(getExecutorDelete(opCtx, opDebug, collection, &parsedDelete));
@@ -418,54 +410,57 @@ public:
ParsedUpdate parsedUpdate(opCtx, &request);
uassertStatusOK(parsedUpdate.parseRequest());
- AutoGetOrCreateDb autoDb(opCtx, dbName, MODE_IX);
- Lock::CollectionLock collLock(opCtx->lockState(), nsString.ns(), MODE_IX);
+ // These are boost::optionap, because if the database or collection does not exist,
+ // they will have to be reacquired in MODE_X
+ boost::optional<AutoGetOrCreateDb> autoDb;
+ boost::optional<Lock::CollectionLock> collLock;
+
+ autoDb.emplace(opCtx, dbName, MODE_IX);
+ collLock.emplace(opCtx->lockState(), nsString.ns(), MODE_IX);
{
stdx::lock_guard<Client> lk(*opCtx->getClient());
CurOp::get(opCtx)->enter_inlock(nsString.ns().c_str(),
- autoDb.getDb()->getProfilingLevel());
+ autoDb->getDb()->getProfilingLevel());
}
- auto css = CollectionShardingState::get(opCtx, nsString);
- css->checkShardVersionOrThrow(opCtx);
-
assertCanWrite(opCtx, nsString);
- Collection* collection = autoDb.getDb()->getCollection(opCtx, nsString.ns());
- if (!collection && autoDb.getDb()->getViewCatalog()->lookup(opCtx, nsString.ns())) {
- CommandHelpers::appendCommandStatus(result,
- {ErrorCodes::CommandNotSupportedOnView,
- "findAndModify not supported on a view"});
- return false;
- }
+ Collection* collection = autoDb->getDb()->getCollection(opCtx, nsString);
- // Create the collection if it does not exist when performing an upsert
- // because the update stage does not create its own collection.
+ // Create the collection if it does not exist when performing an upsert because the
+ // update stage does not create its own collection
if (!collection && args.isUpsert()) {
- // Release the collection lock and reacquire a lock on the database
- // in exclusive mode in order to create the collection.
- collLock.relockAsDatabaseExclusive(autoDb.lock());
- collection = autoDb.getDb()->getCollection(opCtx, nsString);
+ // Release the collection lock and reacquire a lock on the database in exclusive
+ // mode in order to create the collection
+ collLock.reset();
+ autoDb.reset();
+ autoDb.emplace(opCtx, dbName, MODE_X);
+
assertCanWrite(opCtx, nsString);
- if (collection) {
- // Someone else beat us to creating the collection, do nothing.
- } else {
+ collection = autoDb->getDb()->getCollection(opCtx, nsString);
+
+ // If someone else beat us to creating the collection, do nothing
+ if (!collection) {
WriteUnitOfWork wuow(opCtx);
- Status createCollStatus =
- userCreateNS(opCtx, autoDb.getDb(), nsString.ns(), BSONObj());
- if (!createCollStatus.isOK()) {
- CommandHelpers::appendCommandStatus(result, createCollStatus);
- return false;
- }
+ uassertStatusOK(
+ userCreateNS(opCtx, autoDb->getDb(), nsString.ns(), BSONObj()));
wuow.commit();
- collection = autoDb.getDb()->getCollection(opCtx, nsString);
- invariant(collection);
+ collection = autoDb->getDb()->getCollection(opCtx, nsString);
}
+
+ invariant(collection);
}
+ // Perform an explicit check for "not a view" because the update path doesn't use
+ // AutoGetCollection
+ uassert(ErrorCodes::CommandNotSupportedOnView,
+ "findAndModify not supported on a view",
+ collection ||
+ !autoDb->getDb()->getViewCatalog()->lookup(opCtx, nsString.ns()));
+
const auto exec =
uassertStatusOK(getExecutorUpdate(opCtx, opDebug, collection, &parsedUpdate));
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index dcff67f141f..34f7b733411 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -31,12 +31,10 @@
#include "mongo/platform/basic.h"
#include "mongo/db/auth/authorization_session.h"
-#include "mongo/db/catalog/collection.h"
#include "mongo/db/client.h"
#include "mongo/db/clientcursor.h"
#include "mongo/db/commands.h"
#include "mongo/db/commands/run_aggregate.h"
-#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/exec/working_set_common.h"
#include "mongo/db/matcher/extensions_callback_real.h"
@@ -119,9 +117,12 @@ public:
if (!authSession->isAuthorizedToParseNamespaceElement(cmdObj.firstElement())) {
return Status(ErrorCodes::Unauthorized, "Unauthorized");
}
- const NamespaceString nss(CommandHelpers::parseNsOrUUID(opCtx, dbname, cmdObj));
- auto hasTerm = cmdObj.hasField(kTermField);
- return authSession->checkAuthForFind(nss, hasTerm);
+
+ const auto hasTerm = cmdObj.hasField(kTermField);
+ return authSession->checkAuthForFind(
+ AutoGetCollection::resolveNamespaceStringOrUUID(
+ opCtx, CommandHelpers::parseNsOrUUID(dbname, cmdObj)),
+ hasTerm);
}
Status explain(OperationContext* opCtx,
@@ -129,11 +130,13 @@ public:
const BSONObj& cmdObj,
ExplainOptions::Verbosity verbosity,
BSONObjBuilder* out) const override {
- const NamespaceString nss(parseNs(dbname, cmdObj));
- if (!nss.isValid()) {
- return {ErrorCodes::InvalidNamespace,
- str::stream() << "Invalid collection name: " << nss.ns()};
- }
+ // Acquire locks and resolve possible UUID. The RAII object is optional, because in the case
+ // of a view, the locks need to be released.
+ boost::optional<AutoGetCollectionForReadCommand> ctx;
+ ctx.emplace(opCtx,
+ CommandHelpers::parseNsOrUUID(dbname, cmdObj),
+ AutoGetCollection::ViewMode::kViewsPermitted);
+ const auto nss = ctx->getNss();
// Parse the command BSON to a QueryRequest.
const bool isExplain = true;
@@ -157,12 +160,9 @@ public:
}
std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());
- // Acquire locks. If the namespace is a view, we release our locks and convert the query
- // request into an aggregation command.
- AutoGetCollectionOrViewForReadCommand ctx(opCtx, nss);
- if (ctx.getView()) {
+ if (ctx->getView()) {
// Relinquish locks. The aggregation command will re-acquire them.
- ctx.releaseLocksForView();
+ ctx.reset();
// Convert the find command into an aggregation using $match (and other stages, as
// necessary), if possible.
@@ -193,7 +193,7 @@ public:
// The collection may be NULL. If so, getExecutor() should handle it by returning an
// execution tree with an EOFStage.
- Collection* collection = ctx.getCollection();
+ Collection* const collection = ctx->getCollection();
// We have a parsed query. Time to get the execution plan for it.
auto readConcernArgs = repl::ReadConcernArgs::get(opCtx);
@@ -252,8 +252,12 @@ public:
// Acquire locks. If the query is on a view, we release our locks and convert the query
// request into an aggregation command.
- Lock::DBLock dbLock(opCtx, dbname, getLockModeForQuery(opCtx));
- const NamespaceString nss(CommandHelpers::parseNsOrUUID(opCtx, dbname, cmdObj));
+ boost::optional<AutoGetCollectionForReadCommand> ctx;
+ ctx.emplace(opCtx,
+ CommandHelpers::parseNsOrUUID(dbname, cmdObj),
+ AutoGetCollection::ViewMode::kViewsPermitted);
+ const auto& nss = ctx->getNss();
+
qr->refreshNSS(opCtx);
// Fill out curop information.
@@ -280,11 +284,9 @@ public:
}
std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());
- AutoGetCollectionOrViewForReadCommand ctx(opCtx, nss, std::move(dbLock));
- Collection* collection = ctx.getCollection();
- if (ctx.getView()) {
+ if (ctx->getView()) {
// Relinquish locks. The aggregation command will re-acquire them.
- ctx.releaseLocksForView();
+ ctx.reset();
// Convert the find command into an aggregation using $match (and other stages, as
// necessary), if possible.
@@ -309,6 +311,8 @@ public:
return status.isOK();
}
+ Collection* const collection = ctx->getCollection();
+
// Get the execution plan for the query.
auto readConcernArgs = repl::ReadConcernArgs::get(opCtx);
auto yieldPolicy =
diff --git a/src/mongo/db/commands/list_indexes.cpp b/src/mongo/db/commands/list_indexes.cpp
index d0e2d40461c..7f5205b5cbc 100644
--- a/src/mongo/db/commands/list_indexes.cpp
+++ b/src/mongo/db/commands/list_indexes.cpp
@@ -29,9 +29,7 @@
#include "mongo/platform/basic.h"
#include "mongo/db/auth/authorization_session.h"
-#include "mongo/db/catalog/collection.h"
#include "mongo/db/catalog/collection_catalog_entry.h"
-#include "mongo/db/catalog/database.h"
#include "mongo/db/clientcursor.h"
#include "mongo/db/commands.h"
#include "mongo/db/commands/feature_compatibility_version.h"
@@ -76,9 +74,12 @@ namespace {
*/
class CmdListIndexes : public BasicCommand {
public:
+ CmdListIndexes() : BasicCommand("listIndexes") {}
+
AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
return AllowedOnSecondary::kOptIn;
}
+
virtual bool adminOnly() const {
return false;
}
@@ -90,10 +91,10 @@ public:
return "list indexes for a collection";
}
- virtual Status checkAuthForCommand(Client* client,
- const std::string& dbname,
- const BSONObj& cmdObj) const {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
+ Status checkAuthForOperation(OperationContext* opCtx,
+ const std::string& dbname,
+ const BSONObj& cmdObj) const override {
+ AuthorizationSession* authzSession = AuthorizationSession::get(opCtx->getClient());
if (!authzSession->isAuthorizedToParseNamespaceElement(cmdObj.firstElement())) {
return Status(ErrorCodes::Unauthorized, "Unauthorized");
@@ -101,9 +102,9 @@ public:
// Check for the listIndexes ActionType on the database, or find on system.indexes for pre
// 3.0 systems.
- const NamespaceString ns(
- CommandHelpers::parseNsOrUUID(client->getOperationContext(), dbname, cmdObj));
- if (authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns),
+ const auto nss = AutoGetCollection::resolveNamespaceStringOrUUID(
+ opCtx, CommandHelpers::parseNsOrUUID(dbname, cmdObj));
+ if (authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(nss),
ActionType::listIndexes) ||
authzSession->isAuthorizedForActionsOnResource(
ResourcePattern::forExactNamespace(NamespaceString(dbname, "system.indexes")),
@@ -113,44 +114,31 @@ public:
return Status(ErrorCodes::Unauthorized,
str::stream() << "Not authorized to list indexes on collection: "
- << ns.coll());
+ << nss.ns());
}
- CmdListIndexes() : BasicCommand("listIndexes") {}
-
bool run(OperationContext* opCtx,
const string& dbname,
const BSONObj& cmdObj,
BSONObjBuilder& result) {
- Lock::DBLock dbSLock(opCtx, dbname, MODE_IS);
- const NamespaceString ns(CommandHelpers::parseNsOrUUID(opCtx, dbname, cmdObj));
const long long defaultBatchSize = std::numeric_limits<long long>::max();
long long batchSize;
- Status parseCursorStatus =
- CursorRequest::parseCommandCursorOptions(cmdObj, defaultBatchSize, &batchSize);
- if (!parseCursorStatus.isOK()) {
- return CommandHelpers::appendCommandStatus(result, parseCursorStatus);
- }
+ uassertStatusOK(
+ CursorRequest::parseCommandCursorOptions(cmdObj, defaultBatchSize, &batchSize));
- AutoGetCollectionForReadCommand autoColl(opCtx, ns, std::move(dbSLock));
- if (!autoColl.getDb()) {
- return CommandHelpers::appendCommandStatus(
- result,
- Status(ErrorCodes::NamespaceNotFound, "Database " + ns.db() + " doesn't exist"));
- }
-
- const Collection* collection = autoColl.getCollection();
- if (!collection) {
- return CommandHelpers::appendCommandStatus(
- result,
- Status(ErrorCodes::NamespaceNotFound, "Collection " + ns.ns() + " doesn't exist"));
- }
+ AutoGetCollectionForReadCommand ctx(opCtx, CommandHelpers::parseNsOrUUID(dbname, cmdObj));
+ Collection* collection = ctx.getCollection();
+ uassert(ErrorCodes::NamespaceNotFound,
+ str::stream() << "ns does not exist: " << ctx.getNss().ns(),
+ collection);
const CollectionCatalogEntry* cce = collection->getCatalogEntry();
invariant(cce);
+ const auto nss = ctx.getNss();
+
vector<string> indexNames;
- writeConflictRetry(opCtx, "listIndexes", ns.ns(), [&indexNames, &cce, &opCtx] {
+ writeConflictRetry(opCtx, "listIndexes", nss.ns(), [&indexNames, &cce, &opCtx] {
indexNames.clear();
cce->getReadyIndexes(opCtx, &indexNames);
});
@@ -160,7 +148,7 @@ public:
for (size_t i = 0; i < indexNames.size(); i++) {
BSONObj indexSpec =
- writeConflictRetry(opCtx, "listIndexes", ns.ns(), [&cce, &opCtx, &indexNames, i] {
+ writeConflictRetry(opCtx, "listIndexes", nss.ns(), [&cce, &opCtx, &indexNames, i] {
return cce->getIndexSpec(opCtx, indexNames[i]);
});
@@ -173,8 +161,8 @@ public:
root->pushBack(id);
}
- const NamespaceString cursorNss = NamespaceString::makeListIndexesNSS(dbname, ns.coll());
- dassert(ns == cursorNss.getTargetNSForListIndexes());
+ const NamespaceString cursorNss = NamespaceString::makeListIndexesNSS(dbname, nss.coll());
+ invariant(nss == cursorNss.getTargetNSForListIndexes());
auto statusWithPlanExecutor = PlanExecutor::make(
opCtx, std::move(ws), std::move(root), cursorNss, PlanExecutor::NO_YIELD);
diff --git a/src/mongo/db/commands/parallel_collection_scan.cpp b/src/mongo/db/commands/parallel_collection_scan.cpp
index 157171627d9..05b8dbff677 100644
--- a/src/mongo/db/commands/parallel_collection_scan.cpp
+++ b/src/mongo/db/commands/parallel_collection_scan.cpp
@@ -80,39 +80,31 @@ public:
return Status(ErrorCodes::Unauthorized, "Unauthorized");
}
- const NamespaceString ns(CommandHelpers::parseNsOrUUID(opCtx, dbname, cmdObj));
- if (!authSession->isAuthorizedForActionsOnNamespace(ns, ActionType::find)) {
- return Status(ErrorCodes::Unauthorized, "Unauthorized");
- }
-
- return Status::OK();
+ const auto hasTerm = false;
+ return authSession->checkAuthForFind(
+ AutoGetCollection::resolveNamespaceStringOrUUID(
+ opCtx, CommandHelpers::parseNsOrUUID(dbname, cmdObj)),
+ hasTerm);
}
bool run(OperationContext* opCtx,
const string& dbname,
const BSONObj& cmdObj,
BSONObjBuilder& result) override {
- Lock::DBLock dbSLock(opCtx, dbname, MODE_IS);
- const NamespaceString ns(CommandHelpers::parseNsOrUUID(opCtx, dbname, cmdObj));
+ AutoGetCollectionForReadCommand ctx(opCtx, CommandHelpers::parseNsOrUUID(dbname, cmdObj));
+ const auto nss = ctx.getNss();
- AutoGetCollectionForReadCommand ctx(opCtx, ns, std::move(dbSLock));
-
- Collection* collection = ctx.getCollection();
- if (!collection)
- return CommandHelpers::appendCommandStatus(
- result,
- Status(ErrorCodes::NamespaceNotFound,
- str::stream() << "ns does not exist: " << ns.ns()));
+ Collection* const collection = ctx.getCollection();
+ uassert(ErrorCodes::NamespaceNotFound,
+ str::stream() << "ns does not exist: " << nss.ns(),
+ collection);
size_t numCursors = static_cast<size_t>(cmdObj["numCursors"].numberInt());
-
- if (numCursors == 0 || numCursors > 10000)
- return CommandHelpers::appendCommandStatus(
- result,
- Status(ErrorCodes::BadValue,
- str::stream() << "numCursors has to be between 1 and 10000"
- << " was: "
- << numCursors));
+ uassert(ErrorCodes::BadValue,
+ str::stream() << "numCursors has to be between 1 and 10000"
+ << " was: "
+ << numCursors,
+ numCursors >= 1 && numCursors <= 10000);
std::vector<std::unique_ptr<RecordCursor>> iterators;
// Opening multiple cursors on a capped collection and reading them in parallel can produce
@@ -161,7 +153,7 @@ public:
auto pinnedCursor = collection->getCursorManager()->registerCursor(
opCtx,
{std::move(exec),
- ns,
+ nss,
AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
opCtx->recoveryUnit()->isReadingFromMajorityCommittedSnapshot(),
cmdObj});
@@ -169,7 +161,7 @@ public:
BSONObjBuilder threadResult;
appendCursorResponseObject(
- pinnedCursor.getCursor()->cursorid(), ns.ns(), BSONArray(), &threadResult);
+ pinnedCursor.getCursor()->cursorid(), nss.ns(), BSONArray(), &threadResult);
threadResult.appendBool("ok", 1);
bucketsBuilder.append(threadResult.obj());
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index 6ccec360753..066ad027e4f 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -376,10 +376,10 @@ Status runAggregate(OperationContext* opCtx,
const auto& pipelineInvolvedNamespaces = liteParsedPipeline.getInvolvedNamespaces();
- // If emplaced, AutoGetCollectionOrViewForReadCommand will throw if the sharding version for
- // this connection is out of date. If the namespace is a view, the lock will be released
- // before re-running the expanded aggregation.
- boost::optional<AutoGetCollectionOrViewForReadCommand> ctx;
+ // If emplaced, AutoGetCollectionForReadCommand will throw if the sharding version for this
+ // connection is out of date. If the namespace is a view, the lock will be released before
+ // re-running the expanded aggregation.
+ boost::optional<AutoGetCollectionForReadCommand> ctx;
// If this is a collectionless aggregation, we won't create 'ctx' but will still need an
// AutoStatsTracker to record CurOp and Top entries.
@@ -390,7 +390,7 @@ Status runAggregate(OperationContext* opCtx,
if (nss.isCollectionlessAggregateNS() && pipelineInvolvedNamespaces.empty()) {
statsTracker.emplace(opCtx, nss, Top::LockType::NotLocked, 0);
} else {
- ctx.emplace(opCtx, nss);
+ ctx.emplace(opCtx, nss, AutoGetCollection::ViewMode::kViewsPermitted);
}
Collection* collection = ctx ? ctx->getCollection() : nullptr;
@@ -428,7 +428,7 @@ Status runAggregate(OperationContext* opCtx,
}
// With the view & collation resolved, we can relinquish locks.
- ctx->releaseLocksForView();
+ ctx.reset();
// Parse the resolved view into a new aggregation request.
auto newRequest = resolvedView.getValue().asExpandedViewAggregation(request);
diff --git a/src/mongo/db/commands_test.cpp b/src/mongo/db/commands_test.cpp
index b4ebf32a464..cd74dff528e 100644
--- a/src/mongo/db/commands_test.cpp
+++ b/src/mongo/db/commands_test.cpp
@@ -28,13 +28,13 @@
#include "mongo/db/commands.h"
#include "mongo/db/catalog/collection_mock.h"
-#include "mongo/db/catalog/uuid_catalog.h"
#include "mongo/db/dbmessage.h"
#include "mongo/db/service_context_noop.h"
#include "mongo/platform/basic.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
+namespace {
TEST(Commands, appendCommandStatusOK) {
BSONObjBuilder actualResult;
@@ -109,48 +109,36 @@ public:
TEST_F(ParseNsOrUUID, FailWrongType) {
auto cmd = BSON("query" << BSON("a" << BSON("$gte" << 11)));
ASSERT_THROWS_CODE(
- CommandHelpers::parseNsOrUUID(opCtx, "db", cmd), DBException, ErrorCodes::InvalidNamespace);
+ CommandHelpers::parseNsOrUUID("db", cmd), DBException, ErrorCodes::InvalidNamespace);
}
TEST_F(ParseNsOrUUID, FailEmptyDbName) {
auto cmd = BSON("query"
<< "coll");
ASSERT_THROWS_CODE(
- CommandHelpers::parseNsOrUUID(opCtx, "", cmd), DBException, ErrorCodes::InvalidNamespace);
+ CommandHelpers::parseNsOrUUID("", cmd), DBException, ErrorCodes::InvalidNamespace);
}
TEST_F(ParseNsOrUUID, FailInvalidDbName) {
auto cmd = BSON("query"
<< "coll");
- ASSERT_THROWS_CODE(CommandHelpers::parseNsOrUUID(opCtx, "test.coll", cmd),
- DBException,
- ErrorCodes::InvalidNamespace);
-}
-
-TEST_F(ParseNsOrUUID, ParseUnknownUUID) {
- auto cmd = BSON("query" << UUID::gen());
- ASSERT_THROWS_CODE(CommandHelpers::parseNsOrUUID(opCtx, "test.coll", cmd),
- DBException,
- ErrorCodes::NamespaceNotFound);
+ ASSERT_THROWS_CODE(
+ CommandHelpers::parseNsOrUUID("test.coll", cmd), DBException, ErrorCodes::InvalidNamespace);
}
TEST_F(ParseNsOrUUID, ParseValidColl) {
auto cmd = BSON("query"
<< "coll");
- auto parsedNss = CommandHelpers::parseNsOrUUID(opCtx, "test", cmd);
- ASSERT_EQ(parsedNss, NamespaceString("test.coll"));
+ auto parsedNss = CommandHelpers::parseNsOrUUID("test", cmd);
+ ASSERT_EQ(*parsedNss.nss(), NamespaceString("test.coll"));
}
TEST_F(ParseNsOrUUID, ParseValidUUID) {
- // Register a UUID/Collection pair in the UUIDCatalog.
const CollectionUUID uuid = UUID::gen();
- const NamespaceString nss("test.coll");
- Collection coll(stdx::make_unique<CollectionMock>(nss));
- UUIDCatalog& catalog = UUIDCatalog::get(opCtx);
- catalog.onCreateCollection(opCtx, &coll, uuid);
-
auto cmd = BSON("query" << uuid);
- auto parsedNss = CommandHelpers::parseNsOrUUID(opCtx, "test", cmd);
- ASSERT_EQUALS(nss, parsedNss);
+ auto parsedNsOrUUID = CommandHelpers::parseNsOrUUID("test", cmd);
+ ASSERT_EQUALS(uuid, *parsedNsOrUUID.uuid());
}
+
+} // namespace
} // namespace mongo
diff --git a/src/mongo/db/cursor_manager.cpp b/src/mongo/db/cursor_manager.cpp
index 061a5a59cc5..418a1265b95 100644
--- a/src/mongo/db/cursor_manager.cpp
+++ b/src/mongo/db/cursor_manager.cpp
@@ -261,14 +261,14 @@ std::size_t GlobalCursorIdCache::timeoutCursors(OperationContext* opCtx, Date_t
// For each collection, time out its cursors under the collection lock (to prevent the
// collection from going away during the erase).
- for (unsigned i = 0; i < todo.size(); i++) {
- AutoGetCollectionOrViewForReadCommand ctx(opCtx, NamespaceString(todo[i]));
+ for (const auto& nsTodo : todo) {
+ AutoGetCollectionForReadCommand ctx(opCtx, nsTodo);
if (!ctx.getDb()) {
continue;
}
- Collection* collection = ctx.getCollection();
- if (collection == NULL) {
+ Collection* const collection = ctx.getCollection();
+ if (!collection) {
continue;
}
@@ -277,6 +277,7 @@ std::size_t GlobalCursorIdCache::timeoutCursors(OperationContext* opCtx, Date_t
return totalTimedOut;
}
+
} // namespace
template <typename Visitor>
diff --git a/src/mongo/db/db_raii.cpp b/src/mongo/db/db_raii.cpp
index fb61a0a8adf..52efb4948f8 100644
--- a/src/mongo/db/db_raii.cpp
+++ b/src/mongo/db/db_raii.cpp
@@ -36,6 +36,11 @@
#include "mongo/db/s/collection_sharding_state.h"
namespace mongo {
+namespace {
+
+const boost::optional<int> kDoNotChangeProfilingLevel = boost::none;
+
+} // namespace
AutoStatsTracker::AutoStatsTracker(OperationContext* opCtx,
const NamespaceString& nss,
@@ -69,31 +74,11 @@ AutoStatsTracker::~AutoStatsTracker() {
}
AutoGetCollectionForRead::AutoGetCollectionForRead(OperationContext* opCtx,
- const NamespaceString& nss,
- Date_t deadline)
- : AutoGetCollectionForRead(opCtx,
- nss,
- AutoGetCollection::ViewMode::kViewsForbidden,
- Lock::DBLock(opCtx, nss.db(), getLockModeForQuery(opCtx), deadline),
- deadline) {}
-
-AutoGetCollectionForRead::AutoGetCollectionForRead(OperationContext* opCtx,
- const StringData dbName,
- const UUID& uuid,
- Date_t deadline)
- : AutoGetCollectionForRead(opCtx,
- NamespaceStringOrUUID({dbName.toString(), uuid}),
- AutoGetCollection::ViewMode::kViewsForbidden,
- Lock::DBLock(opCtx, dbName, getLockModeForQuery(opCtx), deadline),
- deadline) {}
-
-AutoGetCollectionForRead::AutoGetCollectionForRead(OperationContext* opCtx,
const NamespaceStringOrUUID& nsOrUUID,
AutoGetCollection::ViewMode viewMode,
- Lock::DBLock lock,
Date_t deadline) {
const auto collectionLockMode = getLockModeForQuery(opCtx);
- _autoColl.emplace(opCtx, nsOrUUID, std::move(lock), collectionLockMode, viewMode, deadline);
+ _autoColl.emplace(opCtx, nsOrUUID, collectionLockMode, viewMode, deadline);
while (true) {
auto coll = _autoColl->getCollection();
@@ -131,124 +116,54 @@ AutoGetCollectionForRead::AutoGetCollectionForRead(OperationContext* opCtx,
AutoGetCollectionForReadCommand::AutoGetCollectionForReadCommand(
OperationContext* opCtx,
- const NamespaceString& nss,
- AutoGetCollection::ViewMode viewMode,
- Lock::DBLock lock,
- Date_t deadline) {
- _autoCollForRead.emplace(opCtx, nss, viewMode, std::move(lock), deadline);
- const int doNotChangeProfilingLevel = 0;
- _statsTracker.emplace(opCtx,
- nss,
- Top::LockType::ReadLocked,
- _autoCollForRead->getDb() ? _autoCollForRead->getDb()->getProfilingLevel()
- : doNotChangeProfilingLevel,
- deadline);
-
- // We have both the DB and collection locked, which is the prerequisite to do a stable shard
- // version check, but we'd like to do the check after we have a satisfactory snapshot.
- auto css = CollectionShardingState::get(opCtx, nss);
- css->checkShardVersionOrThrow(opCtx);
-}
-
-AutoGetCollectionForReadCommand::AutoGetCollectionForReadCommand(
- OperationContext* opCtx,
- const NamespaceString& nss,
+ const NamespaceStringOrUUID& nsOrUUID,
AutoGetCollection::ViewMode viewMode,
Date_t deadline)
- : AutoGetCollectionForReadCommand(
- opCtx,
- nss,
- viewMode,
- Lock::DBLock(opCtx, nss.db(), getLockModeForQuery(opCtx), deadline)) {}
-
-AutoGetCollectionOrViewForReadCommand::AutoGetCollectionOrViewForReadCommand(
- OperationContext* opCtx, const NamespaceString& nss, Date_t deadline)
- : AutoGetCollectionForReadCommand(
- opCtx, nss, AutoGetCollection::ViewMode::kViewsPermitted, deadline),
- _view(_autoCollForRead->getDb() && !getCollection()
- ? _autoCollForRead->getDb()->getViewCatalog()->lookup(opCtx, nss.ns())
- : nullptr) {}
-
-AutoGetCollectionOrViewForReadCommand::AutoGetCollectionOrViewForReadCommand(
- OperationContext* opCtx, const NamespaceString& nss, Lock::DBLock lock, Date_t deadline)
- : AutoGetCollectionForReadCommand(
- opCtx, nss, AutoGetCollection::ViewMode::kViewsPermitted, std::move(lock), deadline),
- _view(_autoCollForRead->getDb() && !getCollection()
- ? _autoCollForRead->getDb()->getViewCatalog()->lookup(opCtx, nss.ns())
- : nullptr) {}
-
-AutoGetCollectionForReadCommand::AutoGetCollectionForReadCommand(OperationContext* opCtx,
- const StringData dbName,
- const UUID& uuid,
- Date_t deadline) {
- _autoCollForRead.emplace(opCtx, dbName, uuid, deadline);
- if (_autoCollForRead->getCollection()) {
- _statsTracker.emplace(opCtx,
- _autoCollForRead->getCollection()->ns(),
- Top::LockType::ReadLocked,
- _autoCollForRead->getDb()->getProfilingLevel(),
- deadline);
-
+ : _autoCollForRead(opCtx, nsOrUUID, viewMode, deadline),
+ _statsTracker(opCtx,
+ _autoCollForRead.getNss(),
+ Top::LockType::ReadLocked,
+ _autoCollForRead.getDb() ? _autoCollForRead.getDb()->getProfilingLevel()
+ : kDoNotChangeProfilingLevel,
+ deadline) {
+ if (!_autoCollForRead.getView()) {
// We have both the DB and collection locked, which is the prerequisite to do a stable shard
// version check, but we'd like to do the check after we have a satisfactory snapshot.
- auto css = CollectionShardingState::get(opCtx, _autoCollForRead->getCollection()->ns());
+ auto css = CollectionShardingState::get(opCtx, _autoCollForRead.getNss());
css->checkShardVersionOrThrow(opCtx);
}
}
-void AutoGetCollectionOrViewForReadCommand::releaseLocksForView() noexcept {
- invariant(_view);
- _view = nullptr;
- _autoCollForRead = boost::none;
-}
-
-OldClientContext::OldClientContext(OperationContext* opCtx,
- const std::string& ns,
- Database* db,
- bool justCreated)
- : _justCreated(justCreated), _doVersion(true), _ns(ns), _db(db), _opCtx(opCtx) {
- _finishInit();
-}
-
-OldClientContext::OldClientContext(OperationContext* opCtx,
- const std::string& ns,
- bool doVersion)
- : _justCreated(false), // set for real in finishInit
- _doVersion(doVersion),
- _ns(ns),
- _db(NULL),
- _opCtx(opCtx) {
- _finishInit();
-}
+OldClientContext::OldClientContext(OperationContext* opCtx, const std::string& ns, bool doVersion)
+ : OldClientContext(opCtx, ns, doVersion, dbHolder().get(opCtx, ns), false) {}
-void OldClientContext::_finishInit() {
- _db = dbHolder().get(_opCtx, _ns);
- if (_db) {
- _justCreated = false;
- } else {
- invariant(_opCtx->lockState()->isDbLockedForMode(nsToDatabaseSubstring(_ns), MODE_X));
- _db = dbHolder().openDb(_opCtx, _ns, &_justCreated);
+OldClientContext::OldClientContext(
+ OperationContext* opCtx, const std::string& ns, bool doVersion, Database* db, bool justCreated)
+ : _opCtx(opCtx), _db(db), _justCreated(justCreated) {
+ if (!_db) {
+ const auto dbName = nsToDatabaseSubstring(ns);
+ invariant(_opCtx->lockState()->isDbLockedForMode(dbName, MODE_X));
+ _db = dbHolder().openDb(_opCtx, dbName, &_justCreated);
invariant(_db);
}
- if (_doVersion) {
- _checkNotStale();
+ auto const currentOp = CurOp::get(_opCtx);
+
+ if (doVersion) {
+ switch (currentOp->getNetworkOp()) {
+ case dbGetMore: // getMore is special and should be handled elsewhere
+ case dbUpdate: // update & delete check shard version as part of the write executor
+ case dbDelete: // path, so no need to check them here as well
+ break;
+ default:
+ auto css = CollectionShardingState::get(_opCtx, ns);
+ css->checkShardVersionOrThrow(_opCtx);
+ break;
+ }
}
stdx::lock_guard<Client> lk(*_opCtx->getClient());
- CurOp::get(_opCtx)->enter_inlock(_ns.c_str(), _db->getProfilingLevel());
-}
-
-void OldClientContext::_checkNotStale() const {
- switch (CurOp::get(_opCtx)->getNetworkOp()) {
- case dbGetMore: // getMore is special and should be handled elsewhere.
- case dbUpdate: // update & delete check shard version in instance.cpp, so don't check
- case dbDelete: // here as well.
- break;
- default:
- auto css = CollectionShardingState::get(_opCtx, _ns);
- css->checkShardVersionOrThrow(_opCtx);
- }
+ currentOp->enter_inlock(ns.c_str(), _db->getProfilingLevel());
}
OldClientContext::~OldClientContext() {
@@ -268,19 +183,46 @@ OldClientContext::~OldClientContext() {
}
-OldClientWriteContext::OldClientWriteContext(OperationContext* opCtx, const std::string& ns)
- : _opCtx(opCtx),
- _nss(ns),
- _autodb(opCtx, _nss.db(), MODE_IX),
- _collk(opCtx->lockState(), ns, MODE_IX),
- _c(opCtx, ns, _autodb.getDb(), _autodb.justCreated()) {
- _collection = _c.db()->getCollection(opCtx, ns);
- if (!_collection && !_autodb.justCreated()) {
- // relock database in MODE_X to allow collection creation
- _collk.relockAsDatabaseExclusive(_autodb.lock());
- Database* db = dbHolder().get(_opCtx, ns);
- invariant(db == _c.db());
+OldClientWriteContext::OldClientWriteContext(OperationContext* opCtx, StringData ns)
+ : _opCtx(opCtx), _nss(ns) {
+ // Lock the database and collection
+ _autoCreateDb.emplace(opCtx, _nss.db(), MODE_IX);
+ _collLock.emplace(opCtx->lockState(), _nss.ns(), MODE_IX);
+
+ // TODO (Kal): None of the places which use OldClientWriteContext seem to require versioning, so
+ // we should consider defaulting this to false
+ const bool doShardVersionCheck = true;
+ _clientContext.emplace(opCtx,
+ _nss.ns(),
+ doShardVersionCheck,
+ _autoCreateDb->getDb(),
+ _autoCreateDb->justCreated());
+ invariant(_autoCreateDb->getDb() == _clientContext->db());
+
+ // If the collection exists, there is no need to lock into stronger mode
+ if (getCollection())
+ return;
+
+ // If the database was just created, it is already locked in MODE_X so we can skip the relocking
+ // code below
+ if (_autoCreateDb->justCreated()) {
+ dassert(opCtx->lockState()->isDbLockedForMode(_nss.db(), MODE_X));
+ return;
}
+
+ // If the collection doesn't exists, put the context in a state where the database is locked in
+ // MODE_X so that the collection can be created
+ _clientContext.reset();
+ _collLock.reset();
+ _autoCreateDb.reset();
+ _autoCreateDb.emplace(opCtx, _nss.db(), MODE_X);
+
+ _clientContext.emplace(opCtx,
+ _nss.ns(),
+ doShardVersionCheck,
+ _autoCreateDb->getDb(),
+ _autoCreateDb->justCreated());
+ invariant(_autoCreateDb->getDb() == _clientContext->db());
}
LockMode getLockModeForQuery(OperationContext* opCtx) {
diff --git a/src/mongo/db/db_raii.h b/src/mongo/db/db_raii.h
index 07d69f95ccd..0639ce03704 100644
--- a/src/mongo/db/db_raii.h
+++ b/src/mongo/db/db_raii.h
@@ -67,37 +67,26 @@ private:
};
/**
- * RAII-style class, which would acquire the appropriate hierarchy of locks for obtaining
- * a particular collection and would retrieve a reference to the collection. In addition, this
- * utility will ensure that the read will be performed against an appropriately committed snapshot
- * if the operation is using a readConcern of 'majority'.
+ * Same as calling AutoGetCollection with MODE_IS, but in addition ensures that the read will be
+ * performed against an appropriately committed snapshot if the operation is using a readConcern of
+ * 'majority'.
*
* Use this when you want to read the contents of a collection, but you are not at the top-level of
* some command. This will ensure your reads obey any requested readConcern, but will not update the
* status of CurrentOp, or add a Top entry.
*
- * It is guaranteed that locks will be released when this object goes out of scope, therefore
- * database and collection references returned by this class should not be retained.
+ * NOTE: Must not be used with any locks held, because it needs to block waiting on the committed
+ * snapshot to become available.
*/
class AutoGetCollectionForRead {
MONGO_DISALLOW_COPYING(AutoGetCollectionForRead);
public:
- AutoGetCollectionForRead(OperationContext* opCtx,
- const NamespaceString& nss,
- Date_t deadline = Date_t::max());
-
- AutoGetCollectionForRead(OperationContext* opCtx,
- const StringData dbName,
- const UUID& uuid,
- Date_t deadline = Date_t::max());
-
- // TODO (SERVER-32367): Do not use this constructor, it is for internal purposes only
- AutoGetCollectionForRead(OperationContext* opCtx,
- const NamespaceStringOrUUID& nsOrUUID,
- AutoGetCollection::ViewMode viewMode,
- Lock::DBLock lock,
- Date_t deadline = Date_t::max());
+ AutoGetCollectionForRead(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollection::ViewMode viewMode = AutoGetCollection::ViewMode::kViewsForbidden,
+ Date_t deadline = Date_t::max());
Database* getDb() const {
return _autoColl->getDb();
@@ -107,6 +96,14 @@ public:
return _autoColl->getCollection();
}
+ ViewDefinition* getView() const {
+ return _autoColl->getView();
+ }
+
+ const NamespaceString& getNss() const {
+ return _autoColl->getNss();
+ }
+
private:
// This field is optional, because the code to wait for majority committed snapshot needs to
// release locks in order to block waiting
@@ -114,105 +111,38 @@ private:
};
/**
- * RAII-style class, which would acquire the appropriate hierarchy of locks for obtaining
- * a particular collection and would retrieve a reference to the collection. In addition, this
- * utility validates the shard version for the specified namespace and sets the current operation's
- * namespace for the duration while this object is alive.
- *
- * Use this when you are a read-only command and you know that your target namespace is a collection
- * (not a view). In addition to ensuring your read obeys any requested readConcern, this will add a
- * Top entry upon destruction and ensure the CurrentOp object has the right namespace and has
- * started its timer.
- *
- * It is guaranteed that locks will be released when this object goes out of scope, therefore
- * database and collection references returned by this class should not be retained.
+ * Same as AutoGetCollectionForRead, but in addition will add a Top entry upon destruction and
+ * ensure the CurrentOp object has the right namespace and has started its timer.
*/
class AutoGetCollectionForReadCommand {
MONGO_DISALLOW_COPYING(AutoGetCollectionForReadCommand);
public:
- AutoGetCollectionForReadCommand(OperationContext* opCtx,
- const NamespaceString& nss,
- Date_t deadline = Date_t::max())
- : AutoGetCollectionForReadCommand(
- opCtx, nss, AutoGetCollection::ViewMode::kViewsForbidden, deadline) {}
-
- AutoGetCollectionForReadCommand(OperationContext* opCtx,
- const NamespaceString& nss,
- Lock::DBLock lock,
- Date_t deadline = Date_t::max())
- : AutoGetCollectionForReadCommand(
- opCtx, nss, AutoGetCollection::ViewMode::kViewsForbidden, std::move(lock), deadline) {
- }
-
- AutoGetCollectionForReadCommand(OperationContext* opCtx,
- const StringData dbName,
- const UUID& uuid,
- Date_t deadline = Date_t::max());
+ AutoGetCollectionForReadCommand(
+ OperationContext* opCtx,
+ const NamespaceStringOrUUID& nsOrUUID,
+ AutoGetCollection::ViewMode viewMode = AutoGetCollection::ViewMode::kViewsForbidden,
+ Date_t deadline = Date_t::max());
Database* getDb() const {
- return _autoCollForRead->getDb();
+ return _autoCollForRead.getDb();
}
Collection* getCollection() const {
- return _autoCollForRead->getCollection();
+ return _autoCollForRead.getCollection();
}
-protected:
- AutoGetCollectionForReadCommand(OperationContext* opCtx,
- const NamespaceString& nss,
- AutoGetCollection::ViewMode viewMode,
- Date_t deadline = Date_t::max());
-
- AutoGetCollectionForReadCommand(OperationContext* opCtx,
- const NamespaceString& nss,
- AutoGetCollection::ViewMode viewMode,
- Lock::DBLock lock,
- Date_t deadline = Date_t::max());
-
- // '_autoCollForRead' may need to be reset by AutoGetCollectionOrViewForReadCommand, so needs to
- // be a boost::optional.
- boost::optional<AutoGetCollectionForRead> _autoCollForRead;
-
- // This needs to be initialized after 'autoCollForRead', since we need to consult the Database
- // object to get the profiling level. Thus, it needs to be a boost::optional.
- boost::optional<AutoStatsTracker> _statsTracker;
-};
-
-/**
- * RAII-style class for obtaining a collection or view for reading. The pointer to a view definition
- * is nullptr if it does not exist.
- *
- * Use this when you are a read-only command, but have not yet determined if the namespace is a view
- * or a collection.
- */
-class AutoGetCollectionOrViewForReadCommand final : public AutoGetCollectionForReadCommand {
- MONGO_DISALLOW_COPYING(AutoGetCollectionOrViewForReadCommand);
-
-public:
- AutoGetCollectionOrViewForReadCommand(OperationContext* opCtx,
- const NamespaceString& nss,
- Date_t deadline = Date_t::max());
- AutoGetCollectionOrViewForReadCommand(OperationContext* opCtx,
- const NamespaceString& nss,
- Lock::DBLock lock,
- Date_t deadline = Date_t::max());
-
ViewDefinition* getView() const {
- return _view.get();
+ return _autoCollForRead.getView();
}
- /**
- * Unlock this view or collection and release all resources. After calling this function, it is
- * illegal to access this object's database, collection and view pointers.
- *
- * TODO(SERVER-24909): Consider having the constructor release locks instead, or otherwise
- * remove the need for this method.
- */
- void releaseLocksForView() noexcept;
+ const NamespaceString& getNss() const {
+ return _autoCollForRead.getNss();
+ }
private:
- std::shared_ptr<ViewDefinition> _view;
+ AutoGetCollectionForRead _autoCollForRead;
+ AutoStatsTracker _statsTracker;
};
/**
@@ -223,18 +153,7 @@ class OldClientContext {
MONGO_DISALLOW_COPYING(OldClientContext);
public:
- /** this is probably what you want */
OldClientContext(OperationContext* opCtx, const std::string& ns, bool doVersion = true);
-
- /**
- * Below still calls _finishInit, but assumes database has already been acquired
- * or just created.
- */
- OldClientContext(OperationContext* opCtx,
- const std::string& ns,
- Database* db,
- bool justCreated);
-
~OldClientContext();
Database* db() const {
@@ -246,42 +165,58 @@ public:
return _justCreated;
}
+ /**
+ * Only used by the OldClientWriteContext class below and internally, do not use in any new
+ * code.
+ */
+ OldClientContext(OperationContext* opCtx,
+ const std::string& ns,
+ bool doVersion,
+ Database* db,
+ bool justCreated);
+
private:
friend class CurOp;
- void _finishInit();
- void _checkNotStale() const;
- bool _justCreated;
- bool _doVersion;
- const std::string _ns;
- Database* _db;
- OperationContext* _opCtx;
+ const Timer _timer;
+
+ OperationContext* const _opCtx;
- Timer _timer;
+ Database* _db;
+ bool _justCreated;
};
+/**
+ * Combines AutoGetOrCreateDb and OldClientContext. If the requested 'ns' exists, the constructed
+ * object will have both the database and the collection locked in MODE_IX. Otherwise, the database
+ * will be locked in MODE_X and will be created (note, only the database will be created, but not
+ * the collection).
+ *
+ * TODO: Based on its usages, this class should become AutoGetOrCreateCollection whereby the
+ * requested collection should be automatically created instead of relying on the callers to perform
+ * a check and create it afterwards.
+ */
class OldClientWriteContext {
MONGO_DISALLOW_COPYING(OldClientWriteContext);
public:
- OldClientWriteContext(OperationContext* opCtx, const std::string& ns);
+ OldClientWriteContext(OperationContext* opCtx, StringData ns);
Database* db() const {
- return _c.db();
+ return _clientContext->db();
}
Collection* getCollection() const {
- return _c.db()->getCollection(_opCtx, _nss);
+ return db()->getCollection(_opCtx, _nss);
}
private:
OperationContext* const _opCtx;
const NamespaceString _nss;
- AutoGetOrCreateDb _autodb;
- Lock::CollectionLock _collk;
- OldClientContext _c;
- Collection* _collection;
+ boost::optional<AutoGetOrCreateDb> _autoCreateDb;
+ boost::optional<Lock::CollectionLock> _collLock;
+ boost::optional<OldClientContext> _clientContext;
};
/**
diff --git a/src/mongo/db/namespace_string.cpp b/src/mongo/db/namespace_string.cpp
index ba17dc66cc4..cdd87d63fff 100644
--- a/src/mongo/db/namespace_string.cpp
+++ b/src/mongo/db/namespace_string.cpp
@@ -245,17 +245,11 @@ bool NamespaceString::isReplicated() const {
return true;
}
-StringData NamespaceStringOrUUID::db() const {
- if (_nss)
- return _nss->db();
- return _dbAndUUID->dbName;
-}
-
std::string NamespaceStringOrUUID::toString() const {
if (_nss)
return _nss->toString();
else
- return str::stream() << _dbAndUUID->dbName << ':' << _dbAndUUID->uuid.toString();
+ return _uuid->toString();
}
std::ostream& operator<<(std::ostream& stream, const NamespaceString& nss) {
diff --git a/src/mongo/db/namespace_string.h b/src/mongo/db/namespace_string.h
index eb19a5dfb04..6a1ab881700 100644
--- a/src/mongo/db/namespace_string.h
+++ b/src/mongo/db/namespace_string.h
@@ -449,22 +449,15 @@ private:
*/
class NamespaceStringOrUUID {
public:
- struct DBNameAndUUID {
- std::string dbName;
- UUID uuid;
- };
-
NamespaceStringOrUUID(NamespaceString nss) : _nss(std::move(nss)) {}
- NamespaceStringOrUUID(StringData dbName, UUID uuid) : _dbAndUUID({dbName.toString(), uuid}) {}
-
- StringData db() const;
+ NamespaceStringOrUUID(UUID uuid) : _uuid(std::move(uuid)) {}
const boost::optional<NamespaceString>& nss() const {
return _nss;
}
- const boost::optional<DBNameAndUUID>& dbAndUUID() const {
- return _dbAndUUID;
+ const boost::optional<UUID>& uuid() const {
+ return _uuid;
}
std::string toString() const;
@@ -472,7 +465,7 @@ public:
private:
// At any given time exactly one of these optionals will be initialized
boost::optional<NamespaceString> _nss;
- boost::optional<DBNameAndUUID> _dbAndUUID;
+ boost::optional<UUID> _uuid;
};
std::ostream& operator<<(std::ostream& stream, const NamespaceString& nss);
diff --git a/src/mongo/db/ops/SConscript b/src/mongo/db/ops/SConscript
index 7755be679ee..e830718ce40 100644
--- a/src/mongo/db/ops/SConscript
+++ b/src/mongo/db/ops/SConscript
@@ -11,12 +11,12 @@ env.Library(
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/db/catalog/catalog_raii',
+ '$BUILD_DIR/mongo/db/curop',
'$BUILD_DIR/mongo/db/repl/oplog',
'$BUILD_DIR/mongo/db/repl/repl_coordinator_interface',
- '$BUILD_DIR/mongo/util/fail_point',
- '$BUILD_DIR/mongo/db/curop',
- '$BUILD_DIR/mongo/db/db_raii',
'$BUILD_DIR/mongo/db/write_ops',
+ '$BUILD_DIR/mongo/util/fail_point',
],
)
diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp
index 45b4ce57016..263c4d984c3 100644
--- a/src/mongo/db/ops/write_ops_exec.cpp
+++ b/src/mongo/db/ops/write_ops_exec.cpp
@@ -35,13 +35,13 @@
#include "mongo/base/checked_cast.h"
#include "mongo/db/audit.h"
#include "mongo/db/auth/authorization_session.h"
+#include "mongo/db/catalog/catalog_raii.h"
#include "mongo/db/catalog/collection.h"
#include "mongo/db/catalog/database_holder.h"
#include "mongo/db/catalog/document_validation.h"
#include "mongo/db/commands.h"
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/curop_metrics.h"
-#include "mongo/db/db_raii.h"
#include "mongo/db/exec/delete.h"
#include "mongo/db/exec/update.h"
#include "mongo/db/introspect.h"
diff --git a/src/mongo/db/ops/write_ops_retryability.cpp b/src/mongo/db/ops/write_ops_retryability.cpp
index c9525140324..11ec62cf35d 100644
--- a/src/mongo/db/ops/write_ops_retryability.cpp
+++ b/src/mongo/db/ops/write_ops_retryability.cpp
@@ -33,7 +33,6 @@
#include "mongo/db/dbdirectclient.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/ops/find_and_modify_result.h"
-#include "mongo/db/ops/write_ops_exec.h"
#include "mongo/db/query/find_and_modify_request.h"
#include "mongo/logger/redaction.h"
diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp
index 2441a0cc787..ba203c5fd62 100644
--- a/src/mongo/db/pipeline/pipeline_d.cpp
+++ b/src/mongo/db/pipeline/pipeline_d.cpp
@@ -706,10 +706,7 @@ Status PipelineD::MongoDInterface::attachCursorSourceToPipeline(
boost::optional<AutoGetCollectionForReadCommand> autoColl;
if (expCtx->uuid) {
try {
- autoColl.emplace(expCtx->opCtx, expCtx->ns.db(), *expCtx->uuid);
- uassert(ErrorCodes::NamespaceNotFound,
- "No namespace with UUID " + expCtx->uuid->toString(),
- autoColl->getCollection());
+ autoColl.emplace(expCtx->opCtx, *expCtx->uuid);
} catch (const ExceptionFor<ErrorCodes::NamespaceNotFound>& ex) {
// The UUID doesn't exist anymore
return ex.toStatus();
@@ -898,7 +895,7 @@ std::unique_ptr<CollatorInterface> PipelineD::MongoDInterface::_getCollectionDef
auto it = _collatorCache.find(collectionUUID);
if (it == _collatorCache.end()) {
auto collator = [&]() -> std::unique_ptr<CollatorInterface> {
- AutoGetCollection autoColl(opCtx, {dbName, collectionUUID}, MODE_IS);
+ AutoGetCollection autoColl(opCtx, collectionUUID, MODE_IS);
if (!autoColl.getCollection()) {
// This collection doesn't exist, so assume a nullptr default collation
return nullptr;
diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp
index 75ba802b40f..9bd3656b958 100644
--- a/src/mongo/db/query/find.cpp
+++ b/src/mongo/db/query/find.cpp
@@ -554,17 +554,8 @@ std::string runQuery(OperationContext* opCtx,
LOG(2) << "Running query: " << redact(cq->toStringShort());
// Parse, canonicalize, plan, transcribe, and get a plan executor.
- AutoGetCollectionOrViewForReadCommand ctx(opCtx, nss);
- Collection* collection = ctx.getCollection();
-
- if (ctx.getView()) {
- uasserted(ErrorCodes::CommandNotSupportedOnView,
- str::stream()
- << "Namespace "
- << nss.ns()
- << " is a view. Legacy find operations are not supported on views. "
- << "Only clients which support the find command can be used to query views.");
- }
+ AutoGetCollectionForReadCommand ctx(opCtx, nss, AutoGetCollection::ViewMode::kViewsForbidden);
+ Collection* const collection = ctx.getCollection();
{
const QueryRequest& qr = cq->getQueryRequest();
diff --git a/src/mongo/db/repl/sync_tail.cpp b/src/mongo/db/repl/sync_tail.cpp
index 52e796b03cd..28561964f37 100644
--- a/src/mongo/db/repl/sync_tail.cpp
+++ b/src/mongo/db/repl/sync_tail.cpp
@@ -284,9 +284,9 @@ NamespaceString parseUUIDOrNs(OperationContext* opCtx, const OplogEntry& oplogEn
NamespaceStringOrUUID getNsOrUUID(const NamespaceString& nss, const BSONObj& op) {
if (auto ui = op["ui"]) {
- return {nss.db(), uassertStatusOK(UUID::parse(ui))};
+ return uassertStatusOK(UUID::parse(ui));
}
- return {nss};
+ return nss;
}
} // namespace
@@ -364,7 +364,7 @@ Status SyncTail::syncApply(OperationContext* opCtx,
uassert(ErrorCodes::NamespaceNotFound,
str::stream() << "missing database (" << nss.db() << ")",
db);
- OldClientContext ctx(opCtx, autoColl.getNss().ns(), db, /*justCreated*/ false);
+ OldClientContext ctx(opCtx, autoColl.getNss().ns(), db);
return applyOp(ctx.db());
} catch (ExceptionFor<ErrorCodes::NamespaceNotFound>& ex) {
// Delete operations on non-existent namespaces can be treated as successful for