diff options
-rw-r--r-- | jstests/aggregation/collection_uuid_coll_stats_index_stats.js | 8 | ||||
-rw-r--r-- | jstests/core/collection_uuid_coll_mod.js | 10 | ||||
-rw-r--r-- | jstests/core/collection_uuid_drop.js | 10 | ||||
-rw-r--r-- | jstests/core/collection_uuid_find.js | 10 | ||||
-rw-r--r-- | jstests/core/collection_uuid_index_commands.js | 8 | ||||
-rw-r--r-- | jstests/core/collection_uuid_rename_collection.js | 14 | ||||
-rw-r--r-- | jstests/core/collection_uuid_write_commands.js | 8 | ||||
-rw-r--r-- | src/mongo/db/catalog/drop_collection.cpp | 9 | ||||
-rw-r--r-- | src/mongo/s/commands/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_collection_mod_cmd.cpp | 14 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_drop_collection_cmd.cpp | 8 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_rename_collection_cmd.cpp | 12 | ||||
-rw-r--r-- | src/mongo/s/query/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/s/query/cluster_aggregate.cpp | 8 | ||||
-rw-r--r-- | src/mongo/s/query/cluster_find.cpp | 7 |
15 files changed, 124 insertions, 4 deletions
diff --git a/jstests/aggregation/collection_uuid_coll_stats_index_stats.js b/jstests/aggregation/collection_uuid_coll_stats_index_stats.js index b0779a310f4..67c8ca65113 100644 --- a/jstests/aggregation/collection_uuid_coll_stats_index_stats.js +++ b/jstests/aggregation/collection_uuid_coll_stats_index_stats.js @@ -55,6 +55,14 @@ const testCommand = function(cmd, cmdObj) { validateErrorResponse(res, testDB.getName(), uuid, coll2.getName(), coll.getName()); assert(!testDB.getCollectionNames().includes(coll2.getName())); + jsTestLog("The command '" + cmd + + "' fails with CollectionUUIDMismatch even if the database does not exist."); + const nonexistentDB = testDB.getSiblingDB(testDB.getName() + '_nonexistent'); + cmdObj[cmd] = 'nonexistent'; + res = assert.commandFailedWithCode(nonexistentDB.runCommand(cmdObj), + ErrorCodes.CollectionUUIDMismatch); + validateErrorResponse(res, nonexistentDB.getName(), uuid, 'nonexistent', null); + jsTestLog("The command '" + cmd + "' succeeds on view when no UUID is provided."); const viewName = "view"; assert.commandWorked(testDB.runCommand( diff --git a/jstests/core/collection_uuid_coll_mod.js b/jstests/core/collection_uuid_coll_mod.js index 2e984d81713..218a758c0b3 100644 --- a/jstests/core/collection_uuid_coll_mod.js +++ b/jstests/core/collection_uuid_coll_mod.js @@ -66,4 +66,14 @@ assert.eq(res.collectionUUID, uuid); assert.eq(res.expectedCollection, coll2.getName()); assert.eq(res.actualCollection, coll.getName()); assert(!testDB.getCollectionNames().includes(coll2.getName())); + +// 6. The command fails with CollectionUUIDMismatch even if the database does not exist. +const nonexistentDB = testDB.getSiblingDB(testDB.getName() + '_nonexistent'); +res = assert.commandFailedWithCode( + nonexistentDB.runCommand({collMod: 'nonexistent', collectionUUID: uuid}), + ErrorCodes.CollectionUUIDMismatch); +assert.eq(res.db, nonexistentDB.getName()); +assert.eq(res.collectionUUID, uuid); +assert.eq(res.expectedCollection, 'nonexistent'); +assert.eq(res.actualCollection, null); })(); diff --git a/jstests/core/collection_uuid_drop.js b/jstests/core/collection_uuid_drop.js index 747a4b114ed..a3465795292 100644 --- a/jstests/core/collection_uuid_drop.js +++ b/jstests/core/collection_uuid_drop.js @@ -67,6 +67,16 @@ assert.eq(res.expectedCollection, coll2.getName()); assert.eq(res.actualCollection, coll.getName()); assert(!testDB.getCollectionNames().includes(coll2.getName())); +// The command fails with CollectionUUIDMismatch even if the database does not exist. +const nonexistentDB = testDB.getSiblingDB(testDB.getName() + '_nonexistent'); +res = assert.commandFailedWithCode( + nonexistentDB.runCommand({drop: 'nonexistent', collectionUUID: uuid}), + ErrorCodes.CollectionUUIDMismatch); +assert.eq(res.db, nonexistentDB.getName()); +assert.eq(res.collectionUUID, uuid); +assert.eq(res.expectedCollection, 'nonexistent'); +assert.eq(res.actualCollection, null); + // The command fails when the provided UUID corresponds to a different collection, even if the // provided namespace is a view. const view = 'view'; diff --git a/jstests/core/collection_uuid_find.js b/jstests/core/collection_uuid_find.js index 93d6d011266..241435c1350 100644 --- a/jstests/core/collection_uuid_find.js +++ b/jstests/core/collection_uuid_find.js @@ -66,6 +66,16 @@ assert.eq(res.expectedCollection, coll2.getName()); assert.eq(res.actualCollection, coll.getName()); assert(!testDB.getCollectionNames().includes(coll2.getName())); +// The command fails with CollectionUUIDMismatch even if the database does not exist. +const nonexistentDB = testDB.getSiblingDB(testDB.getName() + '_nonexistent'); +res = assert.commandFailedWithCode( + nonexistentDB.runCommand({find: 'nonexistent', collectionUUID: uuid}), + ErrorCodes.CollectionUUIDMismatch); +assert.eq(res.db, nonexistentDB.getName()); +assert.eq(res.collectionUUID, uuid); +assert.eq(res.expectedCollection, 'nonexistent'); +assert.eq(res.actualCollection, null); + // The command fails when the provided UUID corresponds to a different collection, even if the // provided namespace is a view. const viewName = 'view'; diff --git a/jstests/core/collection_uuid_index_commands.js b/jstests/core/collection_uuid_index_commands.js index 76ee83d8336..785183b8019 100644 --- a/jstests/core/collection_uuid_index_commands.js +++ b/jstests/core/collection_uuid_index_commands.js @@ -88,6 +88,14 @@ const testCommand = function(cmd, cmdObj) { validateErrorResponse(res, testDB.getName(), uuid, coll2.getName(), coll.getName()); assert(!testDB.getCollectionNames().includes(coll2.getName())); + jsTestLog("The command '" + cmd + + "' fails with CollectionUUIDMismatch even if the database does not exist."); + const nonexistentDB = testDB.getSiblingDB(testDB.getName() + '_nonexistent'); + cmdObj[cmd] = 'nonexistent'; + res = assert.commandFailedWithCode(nonexistentDB.runCommand(cmdObj), + ErrorCodes.CollectionUUIDMismatch); + validateErrorResponse(res, nonexistentDB.getName(), uuid, 'nonexistent', null); + jsTestLog("Only collections in the same database are specified by actualCollection."); const otherDB = testDB.getSiblingDB(testDB.getName() + '_2'); assert.commandWorked(otherDB.dropDatabase()); diff --git a/jstests/core/collection_uuid_rename_collection.js b/jstests/core/collection_uuid_rename_collection.js index 85e8507c9d2..bc294fd7aab 100644 --- a/jstests/core/collection_uuid_rename_collection.js +++ b/jstests/core/collection_uuid_rename_collection.js @@ -176,6 +176,20 @@ assert.eq(res.expectedCollection, coll2.getName()); assert.eq(res.actualCollection, coll.getName()); assert(!testDB.getCollectionNames().includes(coll2.getName())); +// The command fails with CollectionUUIDMismatch even if the database does not exist. +const nonexistentDB = testDB.getSiblingDB(testDB.getName() + '_nonexistent'); +res = assert.commandFailedWithCode(testDB.adminCommand({ + renameCollection: nonexistentDB.getName() + '.nonexistent', + to: nonexistentDB.getName() + '.nonexistent_2', + dropTarget: true, + collectionUUID: uuid(coll), +}), + ErrorCodes.CollectionUUIDMismatch); +assert.eq(res.db, nonexistentDB.getName()); +assert.eq(res.collectionUUID, uuid(coll)); +assert.eq(res.expectedCollection, 'nonexistent'); +assert.eq(res.actualCollection, null); + // The collectionUUID parameter cannot be provided when renaming a collection between databases. const otherDBColl = db.getSiblingDB(jsTestName() + '_2').coll; otherDBColl.drop(); diff --git a/jstests/core/collection_uuid_write_commands.js b/jstests/core/collection_uuid_write_commands.js index 0ab9794df6f..080ce1772d5 100644 --- a/jstests/core/collection_uuid_write_commands.js +++ b/jstests/core/collection_uuid_write_commands.js @@ -64,6 +64,14 @@ var testCommand = function(cmd, cmdObj) { validateErrorResponse(res, testDB.getName(), uuid, coll2.getName(), coll.getName()); assert(!testDB.getCollectionNames().includes(coll2.getName())); + jsTestLog("The command '" + cmd + + "' fails with CollectionUUIDMismatch even if the database does not exist."); + const nonexistentDB = testDB.getSiblingDB(testDB.getName() + '_nonexistent'); + cmdObj[cmd] = 'nonexistent'; + res = assert.commandFailedWithCode(nonexistentDB.runCommand(cmdObj), + ErrorCodes.CollectionUUIDMismatch); + validateErrorResponse(res, nonexistentDB.getName(), uuid, 'nonexistent', null); + jsTestLog("Only collections in the same database are specified by actualCollection."); const otherDB = testDB.getSiblingDB(testDB.getName() + '_2'); assert.commandWorked(otherDB.dropDatabase()); diff --git a/src/mongo/db/catalog/drop_collection.cpp b/src/mongo/db/catalog/drop_collection.cpp index c8849d06825..cc164c4aa0f 100644 --- a/src/mongo/db/catalog/drop_collection.cpp +++ b/src/mongo/db/catalog/drop_collection.cpp @@ -35,6 +35,7 @@ #include "mongo/db/audit.h" #include "mongo/db/catalog/collection_catalog.h" #include "mongo/db/catalog/collection_uuid_mismatch.h" +#include "mongo/db/catalog/collection_uuid_mismatch_info.h" #include "mongo/db/catalog/index_catalog.h" #include "mongo/db/client.h" #include "mongo/db/concurrency/exception_util.h" @@ -357,7 +358,13 @@ Status _dropCollection(OperationContext* opCtx, AutoGetDb autoDb(opCtx, collectionName.db(), MODE_IX); auto db = autoDb.getDb(); if (!db) { - return Status(ErrorCodes::NamespaceNotFound, "ns not found"); + return expectedUUID + ? Status{CollectionUUIDMismatchInfo(collectionName.db().toString(), + *expectedUUID, + collectionName.coll().toString(), + boost::none), + "Database does not exist"} + : Status(ErrorCodes::NamespaceNotFound, "ns not found"); } if (CollectionCatalog::get(opCtx)->lookupCollectionByNamespace(opCtx, collectionName)) { diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript index 7e7e076e49d..c4bcd08df66 100644 --- a/src/mongo/s/commands/SConscript +++ b/src/mongo/s/commands/SConscript @@ -103,6 +103,7 @@ env.Library( LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/api_parameters', '$BUILD_DIR/mongo/db/auth/auth_checks', + '$BUILD_DIR/mongo/db/catalog/collection_uuid_mismatch_info', '$BUILD_DIR/mongo/db/change_stream_options_manager', '$BUILD_DIR/mongo/db/commands/cluster_server_parameter_cmds_idl', '$BUILD_DIR/mongo/db/commands/cluster_server_parameter_commands_invocation', diff --git a/src/mongo/s/commands/cluster_collection_mod_cmd.cpp b/src/mongo/s/commands/cluster_collection_mod_cmd.cpp index ecab8d581f3..f2c38f38bca 100644 --- a/src/mongo/s/commands/cluster_collection_mod_cmd.cpp +++ b/src/mongo/s/commands/cluster_collection_mod_cmd.cpp @@ -32,6 +32,7 @@ #include "mongo/db/auth/authorization_checks.h" #include "mongo/db/auth/authorization_session.h" +#include "mongo/db/catalog/collection_uuid_mismatch_info.h" #include "mongo/db/coll_mod_gen.h" #include "mongo/db/coll_mod_reply_validation.h" #include "mongo/db/commands.h" @@ -96,8 +97,17 @@ public: "namespace"_attr = nss, "command"_attr = redact(cmdObj)); - const auto dbInfo = - uassertStatusOK(Grid::get(opCtx)->catalogCache()->getDatabase(opCtx, cmd.getDbName())); + auto swDbInfo = Grid::get(opCtx)->catalogCache()->getDatabase(opCtx, cmd.getDbName()); + if (swDbInfo == ErrorCodes::NamespaceNotFound) { + uassert(CollectionUUIDMismatchInfo(cmd.getDbName().toString(), + *cmd.getCollectionUUID(), + nss.coll().toString(), + boost::none), + "Database does not exist", + !cmd.getCollectionUUID()); + } + const auto dbInfo = uassertStatusOK(swDbInfo); + ShardsvrCollMod collModCommand(nss); collModCommand.setCollModRequest(cmd.getCollModRequest()); auto cmdResponse = diff --git a/src/mongo/s/commands/cluster_drop_collection_cmd.cpp b/src/mongo/s/commands/cluster_drop_collection_cmd.cpp index b28d250f21c..12d3b138aaa 100644 --- a/src/mongo/s/commands/cluster_drop_collection_cmd.cpp +++ b/src/mongo/s/commands/cluster_drop_collection_cmd.cpp @@ -32,6 +32,7 @@ #include "mongo/base/status.h" #include "mongo/db/auth/authorization_session.h" +#include "mongo/db/catalog/collection_uuid_mismatch_info.h" #include "mongo/db/commands.h" #include "mongo/db/drop_gen.h" #include "mongo/db/operation_context.h" @@ -118,6 +119,13 @@ public: // Ensure our reply conforms to the IDL-defined reply structure. return DropReply::parse({"drop"}, resultObj); } catch (const ExceptionFor<ErrorCodes::NamespaceNotFound>&) { + uassert(CollectionUUIDMismatchInfo(request().getDbName().toString(), + *request().getCollectionUUID(), + request().getNamespace().coll().toString(), + boost::none), + "Database does not exist", + !request().getCollectionUUID()); + // If the namespace isn't found, treat the drop as a success but inform about the // failure. DropReply reply; diff --git a/src/mongo/s/commands/cluster_rename_collection_cmd.cpp b/src/mongo/s/commands/cluster_rename_collection_cmd.cpp index 6a3fec1c4d2..98584802646 100644 --- a/src/mongo/s/commands/cluster_rename_collection_cmd.cpp +++ b/src/mongo/s/commands/cluster_rename_collection_cmd.cpp @@ -31,6 +31,7 @@ #include "mongo/platform/basic.h" #include "mongo/db/auth/authorization_session.h" +#include "mongo/db/catalog/collection_uuid_mismatch_info.h" #include "mongo/db/commands.h" #include "mongo/db/commands/rename_collection_common.h" #include "mongo/db/commands/rename_collection_gen.h" @@ -94,7 +95,16 @@ public: renameCollRequest.setRenameCollectionRequest(renameCollReq); auto catalogCache = Grid::get(opCtx)->catalogCache(); - const auto dbInfo = uassertStatusOK(catalogCache->getDatabase(opCtx, fromNss.db())); + auto swDbInfo = Grid::get(opCtx)->catalogCache()->getDatabase(opCtx, fromNss.db()); + if (swDbInfo == ErrorCodes::NamespaceNotFound) { + uassert(CollectionUUIDMismatchInfo(fromNss.db().toString(), + *request().getCollectionUUID(), + fromNss.coll().toString(), + boost::none), + "Database does not exist", + !request().getCollectionUUID()); + } + const auto dbInfo = uassertStatusOK(swDbInfo); auto cri = uassertStatusOK(catalogCache->getCollectionRoutingInfo(opCtx, fromNss)); auto shard = uassertStatusOK( diff --git a/src/mongo/s/query/SConscript b/src/mongo/s/query/SConscript index ab67239ff80..90f67801d66 100644 --- a/src/mongo/s/query/SConscript +++ b/src/mongo/s/query/SConscript @@ -43,6 +43,7 @@ env.Library( 'cluster_query', ], LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/catalog/collection_uuid_mismatch_info', '$BUILD_DIR/mongo/db/timeseries/timeseries_options', ], ) diff --git a/src/mongo/s/query/cluster_aggregate.cpp b/src/mongo/s/query/cluster_aggregate.cpp index b990c1c9bfc..edd41446142 100644 --- a/src/mongo/s/query/cluster_aggregate.cpp +++ b/src/mongo/s/query/cluster_aggregate.cpp @@ -36,6 +36,7 @@ #include "mongo/db/api_parameters.h" #include "mongo/db/auth/authorization_session.h" +#include "mongo/db/catalog/collection_uuid_mismatch_info.h" #include "mongo/db/client.h" #include "mongo/db/commands.h" #include "mongo/db/curop.h" @@ -327,6 +328,13 @@ Status ClusterAggregate::runAggregate(OperationContext* opCtx, sharded_agg_helpers::getExecutionNsRoutingInfo(opCtx, namespaces.executionNss); if (!executionNsRoutingInfoStatus.isOK()) { + uassert(CollectionUUIDMismatchInfo(request.getDbName().toString(), + *request.getCollectionUUID(), + request.getNamespace().coll().toString(), + boost::none), + "Database does not exist", + !request.getCollectionUUID()); + if (liteParsedPipeline.startsWithCollStats()) { uassertStatusOKWithContext(executionNsRoutingInfoStatus, "Unable to retrieve information for $collStats stage"); diff --git a/src/mongo/s/query/cluster_find.cpp b/src/mongo/s/query/cluster_find.cpp index 7d6f93623f9..bf0030c54c9 100644 --- a/src/mongo/s/query/cluster_find.cpp +++ b/src/mongo/s/query/cluster_find.cpp @@ -516,6 +516,13 @@ CursorId ClusterFind::runQuery(OperationContext* opCtx, for (size_t retries = 1; retries <= kMaxRetries; ++retries) { auto swCM = getCollectionRoutingInfoForTxnCmd(opCtx, query.nss()); if (swCM == ErrorCodes::NamespaceNotFound) { + uassert(CollectionUUIDMismatchInfo(query.nss().db().toString(), + *findCommand.getCollectionUUID(), + query.nss().coll().toString(), + boost::none), + "Database does not exist", + !findCommand.getCollectionUUID()); + // If the database doesn't exist, we successfully return an empty result set without // creating a cursor. return CursorId(0); |