diff options
author | Randolph Tan <randolph@10gen.com> | 2018-09-25 16:52:27 -0400 |
---|---|---|
committer | Randolph Tan <randolph@10gen.com> | 2018-09-27 10:31:35 -0400 |
commit | 5674fa1f3087f65ea326b33f5b81647d4dcfb8d6 (patch) | |
tree | be74b03f086a8a338c5354eaf9b2e5f2d86d8996 /src | |
parent | f58a039003b982d40de3ce1be56563270f6b78de (diff) | |
download | mongo-5674fa1f3087f65ea326b33f5b81647d4dcfb8d6.tar.gz |
SERVER-37239 Router should validate if commands are allowed to run within transactions
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/commands.cpp | 59 | ||||
-rw-r--r-- | src/mongo/db/commands.h | 5 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant.cpp | 57 | ||||
-rw-r--r-- | src/mongo/db/transaction_participant.h | 2 | ||||
-rw-r--r-- | src/mongo/s/commands/strategy.cpp | 2 |
6 files changed, 67 insertions, 60 deletions
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index 921b9d7a397..82cca3d77e9 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -104,6 +104,40 @@ bool checkAuthorizationImplPreParse(OperationContext* opCtx, return false; } +// The command names that are allowed in a multi-document transaction. +const StringMap<int> txnCmdWhitelist = {{"abortTransaction", 1}, + {"aggregate", 1}, + {"commitTransaction", 1}, + {"coordinateCommitTransaction", 1}, + {"delete", 1}, + {"distinct", 1}, + {"doTxn", 1}, + {"find", 1}, + {"findandmodify", 1}, + {"findAndModify", 1}, + {"geoSearch", 1}, + {"getMore", 1}, + {"insert", 1}, + {"killCursors", 1}, + {"prepareTransaction", 1}, + {"update", 1}, + {"voteAbortTransaction", 1}, + {"voteCommitTransaction", 1}}; + +// The command names that are allowed in a multi-document transaction only when test commands are +// enabled. +const StringMap<int> txnCmdForTestingWhitelist = {{"dbHash", 1}}; + + +// The commands that can be run on the 'admin' database in multi-document transactions. +const StringMap<int> txnAdminCommands = {{"abortTransaction", 1}, + {"commitTransaction", 1}, + {"coordinateCommitTransaction", 1}, + {"doTxn", 1}, + {"prepareTransaction", 1}, + {"voteAbortTransaction", 1}, + {"voteCommitTransaction", 1}}; + } // namespace @@ -398,6 +432,31 @@ bool CommandHelpers::uassertShouldAttemptParse(OperationContext* opCtx, } } + +Status CommandHelpers::canUseTransactions(StringData dbName, StringData cmdName) { + if (cmdName == "count"_sd) { + return {ErrorCodes::OperationNotSupportedInTransaction, + "Cannot run 'count' in a multi-document transaction. Please see " + "http://dochub.mongodb.org/core/transaction-count for a recommended alternative."}; + } + + if (txnCmdWhitelist.find(cmdName) == txnCmdWhitelist.cend() && + !(getTestCommandsEnabled() && + txnCmdForTestingWhitelist.find(cmdName) != txnCmdForTestingWhitelist.cend())) { + return {ErrorCodes::OperationNotSupportedInTransaction, + str::stream() << "Cannot run '" << cmdName << "' in a multi-document transaction."}; + } + + if (dbName == "config"_sd || dbName == "local"_sd || + (dbName == "admin"_sd && txnAdminCommands.find(cmdName) == txnAdminCommands.cend())) { + return {ErrorCodes::OperationNotSupportedInTransaction, + str::stream() << "Cannot run command against the '" << dbName + << "' database in a transaction"}; + } + + return Status::OK(); +} + constexpr StringData CommandHelpers::kHelpFieldName; ////////////////////////////////////////////////////////////// diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index f49c9e3792e..d0a29a025ec 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -237,6 +237,11 @@ struct CommandHelpers { const Command* command, const OpMsgRequest& request); + /** + * Returns OK if command is allowed to run under a transaction in the given database. + */ + static Status canUseTransactions(StringData dbName, StringData cmdName); + static constexpr StringData kHelpFieldName = "help"_sd; }; diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index 439bd7e7602..55dedecb657 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -680,7 +680,7 @@ void execCommandDatabase(OperationContext* opCtx, } if (autocommitVal) { - uassertStatusOK(TransactionParticipant::isValid(dbname, command->getName())); + uassertStatusOK(CommandHelpers::canUseTransactions(dbname, command->getName())); } // This constructor will check out the session and start a transaction, if necessary. It diff --git a/src/mongo/db/transaction_participant.cpp b/src/mongo/db/transaction_participant.cpp index d848960eb42..a2c967afbe4 100644 --- a/src/mongo/db/transaction_participant.cpp +++ b/src/mongo/db/transaction_participant.cpp @@ -81,39 +81,6 @@ MONGO_FAIL_POINT_DEFINE(hangAfterReservingPrepareTimestamp); const auto getTransactionParticipant = Session::declareDecoration<TransactionParticipant>(); -// The command names that are allowed in a multi-document transaction. -const StringMap<int> txnCmdWhitelist = {{"abortTransaction", 1}, - {"aggregate", 1}, - {"commitTransaction", 1}, - {"coordinateCommitTransaction", 1}, - {"delete", 1}, - {"distinct", 1}, - {"doTxn", 1}, - {"find", 1}, - {"findandmodify", 1}, - {"findAndModify", 1}, - {"geoSearch", 1}, - {"getMore", 1}, - {"insert", 1}, - {"killCursors", 1}, - {"prepareTransaction", 1}, - {"update", 1}, - {"voteAbortTransaction", 1}, - {"voteCommitTransaction", 1}}; - -// The command names that are allowed in a multi-document transaction only when test commands are -// enabled. -const StringMap<int> txnCmdForTestingWhitelist = {{"dbHash", 1}}; - -// The commands that can be run on the 'admin' database in multi-document transactions. -const StringMap<int> txnAdminCommands = {{"abortTransaction", 1}, - {"commitTransaction", 1}, - {"coordinateCommitTransaction", 1}, - {"doTxn", 1}, - {"prepareTransaction", 1}, - {"voteAbortTransaction", 1}, - {"voteCommitTransaction", 1}}; - // The command names that are allowed in a prepared transaction. const StringMap<int> preparedTxnCmdWhitelist = { {"abortTransaction", 1}, {"commitTransaction", 1}, {"prepareTransaction", 1}}; @@ -1066,30 +1033,6 @@ void TransactionParticipant::_checkIsCommandValidWithTxnState(WithLock wl, preparedTxnCmdWhitelist.find(cmdName) != preparedTxnCmdWhitelist.cend()); } -Status TransactionParticipant::isValid(StringData dbName, StringData cmdName) { - if (cmdName == "count"_sd) { - return {ErrorCodes::OperationNotSupportedInTransaction, - "Cannot run 'count' in a multi-document transaction. Please see " - "http://dochub.mongodb.org/core/transaction-count for a recommended alternative."}; - } - - if (txnCmdWhitelist.find(cmdName) == txnCmdWhitelist.cend() && - !(getTestCommandsEnabled() && - txnCmdForTestingWhitelist.find(cmdName) != txnCmdForTestingWhitelist.cend())) { - return {ErrorCodes::OperationNotSupportedInTransaction, - str::stream() << "Cannot run '" << cmdName << "' in a multi-document transaction."}; - } - - if (dbName == "config"_sd || dbName == "local"_sd || - (dbName == "admin"_sd && txnAdminCommands.find(cmdName) == txnAdminCommands.cend())) { - return {ErrorCodes::OperationNotSupportedInTransaction, - str::stream() << "Cannot run command against the '" << dbName - << "' database in a transaction"}; - } - - return Status::OK(); -} - BSONObj TransactionParticipant::reportStashedState() const { BSONObjBuilder builder; reportStashedState(&builder); diff --git a/src/mongo/db/transaction_participant.h b/src/mongo/db/transaction_participant.h index 70042946744..22b3d1485f1 100644 --- a/src/mongo/db/transaction_participant.h +++ b/src/mongo/db/transaction_participant.h @@ -354,8 +354,6 @@ public: void beginOrContinueTransactionUnconditionally(TxnNumber txnNumber); - static Status isValid(StringData dbName, StringData cmdName); - void transitionToPreparedforTest() { stdx::lock_guard<stdx::mutex> lk(_mutex); _txnState.transitionTo(lk, TransactionState::kPrepared); diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp index 8d07a963fd1..861435ae606 100644 --- a/src/mongo/s/commands/strategy.cpp +++ b/src/mongo/s/commands/strategy.cpp @@ -365,6 +365,8 @@ void runCommand(OperationContext* opCtx, auto startTxnSetting = osi->getStartTransaction(); bool startTransaction = startTxnSetting ? *startTxnSetting : false; + uassertStatusOK(CommandHelpers::canUseTransactions(nss.db(), command->getName())); + txnRouter->beginOrContinueTxn(opCtx, *txnNumber, startTransaction); } |