SERVER-81187 Disallow usages of AutoGetCollection from the query module (#41460)

GitOrigin-RevId: dfe9d61eddd9f753467ca4eb6d5fcb6f18966ab7
This commit is contained in:
Jordi Serra Torrens 2025-09-18 14:11:06 +02:00 committed by MongoDB Bot
parent e871a782ad
commit f78f330b77
18 changed files with 277 additions and 52 deletions

View File

@ -65,6 +65,7 @@ Checks: '-*,
modernize-unary-static-assert,
modernize-use-override,
mongo-assert-check,
mongo-banned-auto-get-usage-check,
mongo-banned-names-check,
mongo-bypass-database-metadata-access-check,
mongo-cctype-check,

2
.github/CODEOWNERS vendored
View File

@ -3003,6 +3003,7 @@ WORKSPACE.bazel @10gen/devprod-build @svc-auto-approve-bot
/src/mongo/tools/mongo_tidy_checks/**/MongoMacroDefinitionLeaksCheck.* @10gen/server-networking-and-observability @svc-auto-approve-bot
/src/mongo/tools/mongo_tidy_checks/**/MongoTraceCheck.* @10gen/server-networking-and-observability @svc-auto-approve-bot
/src/mongo/tools/mongo_tidy_checks/**/MongoUnstructuredLogCheck.* @10gen/server-networking-and-observability @svc-auto-approve-bot
/src/mongo/tools/mongo_tidy_checks/**/MongoBannedAutoGetUsageCheck.* @10gen/server-catalog-and-routing @svc-auto-approve-bot
# The following patterns are parsed from ./src/mongo/tools/mongo_tidy_checks/tests/OWNERS.yml
/src/mongo/tools/mongo_tidy_checks/tests/**/* @10gen/server-programmability @svc-auto-approve-bot
@ -3010,6 +3011,7 @@ WORKSPACE.bazel @10gen/devprod-build @svc-auto-approve-bot
/src/mongo/tools/mongo_tidy_checks/tests/**/test_MongoMacroDefinitionLeaksCheck.* @10gen/server-networking-and-observability @svc-auto-approve-bot
/src/mongo/tools/mongo_tidy_checks/tests/**/test_MongoTraceCheck.* @10gen/server-networking-and-observability @svc-auto-approve-bot
/src/mongo/tools/mongo_tidy_checks/tests/**/test_MongoUnstructuredLogCheck.* @10gen/server-networking-and-observability @svc-auto-approve-bot
/src/mongo/tools/mongo_tidy_checks/tests/**/test_MongoBannedAutoGetUsageCheck.* @10gen/server-catalog-and-routing @svc-auto-approve-bot
# The following patterns are parsed from ./src/mongo/tools/mongobridge_tool/OWNERS.yml
/src/mongo/tools/mongobridge_tool/**/* @10gen/server-networking-and-observability @svc-auto-approve-bot

View File

@ -76,7 +76,13 @@ void SamplingEstimatorTest::insertDocuments(const NamespaceString& nss,
int batchSize) {
std::vector<InsertStatement> inserts{docs.begin(), docs.end()};
AutoGetCollection agc(operationContext(), nss, LockMode::MODE_IX);
const auto coll = acquireCollection(
operationContext(),
CollectionAcquisitionRequest(nss,
PlacementConcern(boost::none, ShardVersion::UNSHARDED()),
repl::ReadConcernArgs::get(operationContext()),
AcquisitionPrerequisites::kWrite),
MODE_IX);
{
size_t currentInsertion = 0;
while (currentInsertion < inserts.size()) {
@ -84,8 +90,10 @@ void SamplingEstimatorTest::insertDocuments(const NamespaceString& nss,
int insertionsBeforeCommit = 0;
while (true) {
ASSERT_OK(collection_internal::insertDocument(
operationContext(), *agc, inserts[currentInsertion], nullptr /* opDebug */));
ASSERT_OK(collection_internal::insertDocument(operationContext(),
coll.getCollectionPtr(),
inserts[currentInsertion],
nullptr /* opDebug */));
insertionsBeforeCommit++;
currentInsertion++;
@ -112,10 +120,16 @@ std::vector<BSONObj> SamplingEstimatorTest::createDocuments(int num) {
void SamplingEstimatorTest::createIndex(const BSONObj& spec) {
WriteUnitOfWork wuow(operationContext());
AutoGetCollection autoColl(operationContext(), _kTestNss, MODE_X);
CollectionWriter collection(operationContext(), _kTestNss);
auto coll = acquireCollection(
operationContext(),
CollectionAcquisitionRequest(_kTestNss,
PlacementConcern(boost::none, ShardVersion::UNSHARDED()),
repl::ReadConcernArgs::get(operationContext()),
AcquisitionPrerequisites::kWrite),
MODE_X);
CollectionWriter collectionWriter(operationContext(), &coll);
IndexBuildsCoordinator::createIndexesOnEmptyCollection(
operationContext(), collection, {spec}, /*fromMigrate=*/false);
operationContext(), collectionWriter, {spec}, /*fromMigrate=*/false);
wuow.commit();
}
@ -189,20 +203,25 @@ void createCollAndInsertDocuments(OperationContext* opCtx,
shard_role_details::getRecoveryUnit(opCtx)->abandonSnapshot();
WriteUnitOfWork wunit(opCtx);
AutoGetCollection collRaii(opCtx, nss, MODE_X);
auto db = collRaii.ensureDbExists(opCtx);
invariant(db->createCollection(opCtx, nss, {}));
AutoGetDb db(opCtx, nss.dbName(), MODE_X);
db.ensureDbExists(opCtx);
invariant(db.getDb()->createCollection(opCtx, nss, {}));
wunit.commit();
});
std::vector<InsertStatement> inserts{docs.begin(), docs.end()};
AutoGetCollection agc(opCtx, nss, LockMode::MODE_IX);
auto coll = acquireCollection(
opCtx,
CollectionAcquisitionRequest(nss,
PlacementConcern(boost::none, ShardVersion::UNSHARDED()),
repl::ReadConcernArgs::get(opCtx),
AcquisitionPrerequisites::kWrite),
MODE_IX);
{
WriteUnitOfWork wuow{opCtx};
ASSERT_OK(collection_internal::insertDocuments(
opCtx, *agc, inserts.begin(), inserts.end(), nullptr /* opDebug */));
opCtx, coll.getCollectionPtr(), inserts.begin(), inserts.end(), nullptr /* opDebug */));
wuow.commit();
}
}

View File

@ -67,10 +67,16 @@ protected:
void StatsCacheLoaderTest::createStatsCollection(NamespaceString nss) {
auto opCtx = operationContext();
AutoGetCollection autoColl(opCtx, nss, MODE_IX);
auto db = autoColl.ensureDbExists(opCtx);
WriteUnitOfWork wuow(opCtx);
ASSERT(db->createCollection(opCtx, nss));
AutoGetDb db(opCtx, nss.dbName(), MODE_IX);
auto coll = acquireCollection(
operationContext(),
CollectionAcquisitionRequest(nss,
PlacementConcern(boost::none, ShardVersion::UNSHARDED()),
repl::ReadConcernArgs::get(operationContext()),
AcquisitionPrerequisites::kWrite),
MODE_IX);
ASSERT(db.ensureDbExists(opCtx)->createCollection(opCtx, nss));
wuow.commit();
}
@ -116,12 +122,17 @@ TEST_F(StatsCacheLoaderTest, VerifyStatsLoadsScalar) {
createStatsCollection(statsNss);
// Write serialized stats path to collection.
AutoGetCollection autoColl(operationContext(), statsNss, MODE_IX);
const CollectionPtr& coll = autoColl.getCollection();
auto coll = acquireCollection(
operationContext(),
CollectionAcquisitionRequest(statsNss,
PlacementConcern(boost::none, ShardVersion::UNSHARDED()),
repl::ReadConcernArgs::get(operationContext()),
AcquisitionPrerequisites::kWrite),
MODE_IX);
{
WriteUnitOfWork wuow(operationContext());
ASSERT_OK(collection_internal::insertDocument(
operationContext(), coll, InsertStatement(serialized), nullptr));
operationContext(), coll.getCollectionPtr(), InsertStatement(serialized), nullptr));
wuow.commit();
}
@ -186,12 +197,17 @@ TEST_F(StatsCacheLoaderTest, VerifyStatsLoadsArray) {
createStatsCollection(statsNss);
// Write serialized stats path to collection.
AutoGetCollection autoColl(operationContext(), statsNss, MODE_IX);
const CollectionPtr& coll = autoColl.getCollection();
auto coll = acquireCollection(
operationContext(),
CollectionAcquisitionRequest(statsNss,
PlacementConcern(boost::none, ShardVersion::UNSHARDED()),
repl::ReadConcernArgs::get(operationContext()),
AcquisitionPrerequisites::kWrite),
MODE_IX);
{
WriteUnitOfWork wuow(operationContext());
ASSERT_OK(collection_internal::insertDocument(
operationContext(), coll, InsertStatement(serialized), nullptr));
operationContext(), coll.getCollectionPtr(), InsertStatement(serialized), nullptr));
wuow.commit();
}

View File

@ -152,8 +152,14 @@ void MultipleCollectionAccessorTest::installShardedCollectionMetadata(
ASSERT(!chunks.empty());
const auto uuid = [&] {
AutoGetCollection autoColl(opCtx, nss, MODE_IX);
return autoColl.getCollection()->uuid();
auto coll = acquireCollection(
operationContext(),
CollectionAcquisitionRequest(nss,
PlacementConcern(boost::none, ShardVersion::UNSHARDED()),
repl::ReadConcernArgs::get(operationContext()),
AcquisitionPrerequisites::kWrite),
MODE_IX);
return coll.uuid();
}();
const std::string shardKey("skey");
@ -182,7 +188,13 @@ void MultipleCollectionAccessorTest::installShardedCollectionMetadata(
const auto collectionMetadata =
CollectionMetadata(ChunkManager(rtHandle, boost::none), kMyShardName);
AutoGetCollection coll(opCtx, nss, MODE_IX);
auto coll = acquireCollection(
operationContext(),
CollectionAcquisitionRequest(nss,
PlacementConcern(boost::none, ShardVersion::UNSHARDED()),
repl::ReadConcernArgs::get(operationContext()),
AcquisitionPrerequisites::kWrite),
MODE_IX);
CollectionShardingRuntime::assertCollectionLockedAndAcquireExclusive(opCtx, nss)
->setFilteringMetadata(opCtx, collectionMetadata);
}

View File

@ -97,22 +97,10 @@ public:
SbeStageBuilderTestFixture::tearDown();
}
void insertDocuments(const NamespaceString& nss, const std::vector<BSONObj>& docs) {
std::vector<InsertStatement> inserts{docs.begin(), docs.end()};
AutoGetCollection agc(operationContext(), nss, LockMode::MODE_IX);
{
WriteUnitOfWork wuow{operationContext()};
ASSERT_OK(collection_internal::insertDocuments(
operationContext(), *agc, inserts.begin(), inserts.end(), nullptr /* opDebug */));
wuow.commit();
}
}
void insertDocuments(const std::vector<BSONObj>& localDocs,
const std::vector<BSONObj>& foreignDocs) {
insertDocuments(_nss, localDocs);
insertDocuments(_foreignNss, foreignDocs);
SbeStageBuilderTestFixture::insertDocuments(_nss, localDocs);
SbeStageBuilderTestFixture::insertDocuments(_foreignNss, foreignDocs);
}
struct CompiledTree {

View File

@ -119,6 +119,28 @@ SbeStageBuilderTestFixture::buildPlanStage(std::unique_ptr<QuerySolution> queryS
return {slots, std::move(stage), std::move(data), expCtx};
}
void SbeStageBuilderTestFixture::insertDocuments(const NamespaceString& nss,
const std::vector<BSONObj>& docs) {
std::vector<InsertStatement> inserts{docs.begin(), docs.end()};
auto coll = acquireCollection(
operationContext(),
CollectionAcquisitionRequest(nss,
PlacementConcern(boost::none, ShardVersion::UNSHARDED()),
repl::ReadConcernArgs::get(operationContext()),
AcquisitionPrerequisites::kWrite),
MODE_IX);
{
WriteUnitOfWork wuow{operationContext()};
ASSERT_OK(collection_internal::insertDocuments(operationContext(),
coll.getCollectionPtr(),
inserts.begin(),
inserts.end(),
nullptr /* opDebug */));
wuow.commit();
}
}
void GoldenSbeStageBuilderTestFixture::createCollection(const std::vector<BSONObj>& docs,
boost::optional<BSONObj> indexKeyPattern,
CollectionOptions options) {
@ -133,7 +155,7 @@ void GoldenSbeStageBuilderTestFixture::createCollection(const std::vector<BSONOb
{BSON("v" << 2 << "name" << DBClientBase::genIndexName(*indexKeyPattern) << "key"
<< *indexKeyPattern)}));
}
insertDocuments(docs);
insertDocuments(_nss, docs);
}
void GoldenSbeStageBuilderTestFixture::runTest(std::unique_ptr<QuerySolutionNode> root,
@ -196,18 +218,6 @@ std::string GoldenSbeStageBuilderTestFixture::replaceUuid(std::string input, UUI
return input;
}
void GoldenSbeStageBuilderTestFixture::insertDocuments(const std::vector<BSONObj>& docs) {
std::vector<InsertStatement> inserts{docs.begin(), docs.end()};
AutoGetCollection agc(operationContext(), _nss, LockMode::MODE_IX);
{
WriteUnitOfWork wuow{operationContext()};
ASSERT_OK(collection_internal::insertDocuments(
operationContext(), *agc, inserts.begin(), inserts.end(), nullptr /* opDebug */));
wuow.commit();
}
}
void GoldenSbeExprBuilderTestFixture::setUp() {
SbeStageBuilderTestFixture::setUp();
_expCtx = new ExpressionContextForTest();

View File

@ -131,6 +131,8 @@ public:
}
protected:
void insertDocuments(const NamespaceString& nss, const std::vector<BSONObj>& docs);
const NamespaceString _nss =
NamespaceString::createNamespaceString_forTest("testdb.sbe_stage_builder");
};
@ -147,7 +149,6 @@ public:
protected:
// Random uuid will fail the golden data test, replace it to a constant string.
std::string replaceUuid(std::string input, UUID uuid);
void insertDocuments(const std::vector<BSONObj>& docs);
void createCollection(const std::vector<BSONObj>& docs,
boost::optional<BSONObj> indexKeyPattern,
CollectionOptions options = {});

View File

@ -11,6 +11,7 @@ cc_library(
name = "mongo_tidy_checks_static",
srcs = [
"MongoAssertCheck.cpp",
"MongoBannedAutoGetUsageCheck.cpp",
"MongoBannedNamesCheck.cpp",
"MongoBypassDatabaseMetadataAccessCheck.cpp",
"MongoCctypeCheck.cpp",
@ -35,6 +36,7 @@ cc_library(
],
hdrs = [
"MongoAssertCheck.h",
"MongoBannedAutoGetUsageCheck.h",
"MongoBannedNamesCheck.h",
"MongoBypassDatabaseMetadataAccessCheck.h",
"MongoCctypeCheck.h",

View File

@ -0,0 +1,73 @@
/**
* Copyright (C) 2025-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "MongoBannedAutoGetUsageCheck.h"
#include <array>
#include <string_view>
using namespace clang;
using namespace clang::ast_matchers;
namespace mongo::tidy {
MongoBannedAutoGetUsageCheck::MongoBannedAutoGetUsageCheck(clang::StringRef Name,
clang::tidy::ClangTidyContext* Context)
: ClangTidyCheck(Name, Context) {}
void MongoBannedAutoGetUsageCheck::registerMatchers(MatchFinder* Finder) {
Finder->addMatcher(
varDecl(hasType(cxxRecordDecl(hasName("AutoGetCollection")))).bind("AutoGetCollectionDec"),
this);
}
void MongoBannedAutoGetUsageCheck::check(const MatchFinder::MatchResult& Result) {
const auto& sourceManager = *Result.SourceManager;
// Get the location of the current source file
const auto filePath = sourceManager.getFilename(
sourceManager.getLocForStartOfFile(sourceManager.getMainFileID()));
// Only check on the file paths belonging to modules that are forbidden to use
// AutoGetCollection.
static constexpr std::array<std::string_view, 2> forbiddenDirs = {
"src/mongo/db/query/", "src/mongo/tools/mongo_tidy_checks/tests/"};
if (std::none_of(forbiddenDirs.begin(),
forbiddenDirs.end(),
[&filePath](const std::string_view& dir) { return filePath.contains(dir); })) {
return;
}
if (const auto matchedVar = Result.Nodes.getNodeAs<VarDecl>("AutoGetCollectionDec")) {
diag(matchedVar->getBeginLoc(),
"AutoGetCollection is not allowed to be used from the query modules. Use ShardRole "
"CollectionAcquisitions instead.");
}
}
} // namespace mongo::tidy

View File

@ -0,0 +1,47 @@
/**
* Copyright (C) 2025-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include <clang-tidy/ClangTidy.h>
#include <clang-tidy/ClangTidyCheck.h>
namespace mongo::tidy {
/**
* A clang-tidy check that bans the usage of AutoGetCollection in certain modules.
*/
class MongoBannedAutoGetUsageCheck : public clang::tidy::ClangTidyCheck {
public:
MongoBannedAutoGetUsageCheck(clang::StringRef Name, clang::tidy::ClangTidyContext* Context);
void registerMatchers(clang::ast_matchers::MatchFinder* Finder) override;
void check(const clang::ast_matchers::MatchFinder::MatchResult& Result) override;
};
} // namespace mongo::tidy

View File

@ -28,6 +28,7 @@
*/
#include "MongoAssertCheck.h"
#include "MongoBannedAutoGetUsageCheck.h"
#include "MongoBannedNamesCheck.h"
#include "MongoBypassDatabaseMetadataAccessCheck.h"
#include "MongoCctypeCheck.h"
@ -89,6 +90,8 @@ public:
"mongo-invariant-ddl-coordinator-check");
CheckFactories.registerCheck<MongoBypassDatabaseMetadataAccessCheck>(
"mongo-bypass-database-metadata-access-check");
CheckFactories.registerCheck<MongoBannedAutoGetUsageCheck>(
"mongo-banned-auto-get-usage-check");
}
};

View File

@ -15,3 +15,6 @@ filters:
- "MongoUnstructuredLogCheck.*":
approvers:
- 10gen/server-networking-and-observability
- "MongoBannedAutoGetUsageCheck.*":
approvers:
- 10gen/server-catalog-and-routing

View File

@ -33,6 +33,7 @@ tests = [
"test_MongoMacroDefinitionLeaksCheck",
"test_MongoRandCheck",
"test_MongoRWMutexCheck",
"test_MongoBannedAutoGetUsageCheck",
"test_MongoBannedNamesCheck",
"test_MongoNoUniqueAddressCheck",
"test_MongoStringDataConstRefCheck1",

View File

@ -307,6 +307,10 @@ class MongoTidyTests(unittest.TestCase):
self.run_clang_tidy()
def test_MongoBannedAutoGetUsageCheck(self):
self.expected_output = ("AutoGetCollection is not allowed to be used from the query modules. Use ShardRole CollectionAcquisitions instead.")
self.run_clang_tidy()
if __name__ == "__main__":
unittest.main()

View File

@ -15,3 +15,6 @@ filters:
- "test_MongoUnstructuredLogCheck.*":
approvers:
- 10gen/server-networking-and-observability
- "test_MongoBannedAutoGetUsageCheck.*":
approvers:
- 10gen/server-catalog-and-routing

View File

@ -0,0 +1,38 @@
/**
* Copyright (C) 2025-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
namespace mongo {
class AutoGetCollection {};
int fun() {
AutoGetCollection agc;
}
} // namespace mongo

View File

@ -0,0 +1,2 @@
Checks: '-*,mongo-banned-auto-get-usage-check'
WarningsAsErrors: '*'