diff options
Diffstat (limited to 'src/mongo/db')
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 |