diff options
author | James Wahlin <james@mongodb.com> | 2018-11-06 16:18:20 -0500 |
---|---|---|
committer | James Wahlin <james@mongodb.com> | 2018-11-16 14:17:51 -0500 |
commit | 410656e971aff8f491a87337a17d04bd866389ba (patch) | |
tree | f369934d0fc7b2b89095d2c77df1ecd460bc1ddf /src/mongo/db | |
parent | 63e43f1bb47f7bddf3dc37ad03a2bbee6d2a9423 (diff) | |
download | mongo-410656e971aff8f491a87337a17d04bd866389ba.tar.gz |
SERVER-37124 Disambiguate DuplicateKey error and return keyPattern details in errorInfo object
Diffstat (limited to 'src/mongo/db')
20 files changed, 160 insertions, 22 deletions
diff --git a/src/mongo/db/auth/role_graph.cpp b/src/mongo/db/auth/role_graph.cpp index 0ed793d1052..db93d71ab00 100644 --- a/src/mongo/db/auth/role_graph.cpp +++ b/src/mongo/db/auth/role_graph.cpp @@ -81,7 +81,7 @@ bool RoleGraph::_roleExistsDontCreateBuiltin(const RoleName& role) { Status RoleGraph::createRole(const RoleName& role) { if (roleExists(role)) { - return Status(ErrorCodes::DuplicateKey, + return Status(ErrorCodes::Error(51007), mongoutils::str::stream() << "Role: " << role.getFullName() << " already exists"); } @@ -403,7 +403,7 @@ Status RoleGraph::replaceRole(const RoleName& roleName, for (size_t i = 0; i < roles.size(); ++i) { const RoleName& grantedRole = roles[i]; status = createRole(grantedRole); - fassert(17170, status.isOK() || status == ErrorCodes::DuplicateKey); + fassert(17170, status.isOK() || status.code() == 51007); fassert(17171, addRoleToRole(roleName, grantedRole)); } fassert(17172, addPrivilegesToRole(roleName, privileges)); diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index f190e6c41e4..18d112112b9 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -419,7 +419,7 @@ Status insertRoleDocument(OperationContext* opCtx, const BSONObj& roleObj) { if (status.code() == ErrorCodes::DuplicateKey) { std::string name = roleObj[AuthorizationManager::ROLE_NAME_FIELD_NAME].String(); std::string source = roleObj[AuthorizationManager::ROLE_DB_FIELD_NAME].String(); - return Status(ErrorCodes::DuplicateKey, + return Status(ErrorCodes::Error(51002), str::stream() << "Role \"" << name << "@" << source << "\" already exists"); } if (status.code() == ErrorCodes::UnknownError) { @@ -478,7 +478,7 @@ Status insertPrivilegeDocument(OperationContext* opCtx, const BSONObj& userObj) if (status.code() == ErrorCodes::DuplicateKey) { std::string name = userObj[AuthorizationManager::USER_NAME_FIELD_NAME].String(); std::string source = userObj[AuthorizationManager::USER_DB_FIELD_NAME].String(); - return Status(ErrorCodes::DuplicateKey, + return Status(ErrorCodes::Error(51003), str::stream() << "User \"" << name << "@" << source << "\" already exists"); } if (status.code() == ErrorCodes::UnknownError) { diff --git a/src/mongo/db/commands/write_commands/write_commands.cpp b/src/mongo/db/commands/write_commands/write_commands.cpp index 5d980e5814c..ed2fee3df8a 100644 --- a/src/mongo/db/commands/write_commands/write_commands.cpp +++ b/src/mongo/db/commands/write_commands/write_commands.cpp @@ -51,6 +51,7 @@ #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/server_parameters.h" #include "mongo/db/stats/counters.h" +#include "mongo/db/storage/duplicate_key_error_info.h" #include "mongo/db/transaction_participant.h" #include "mongo/db/write_concern.h" #include "mongo/s/stale_exception.h" @@ -150,7 +151,11 @@ void serializeReply(OperationContext* opCtx, } } else { error.append("code", int(status.code())); + if (auto const extraInfo = status.extraInfo()) { + extraInfo->serialize(&error); + } } + error.append("errmsg", errorMessage(status.reason())); errors.push_back(error.obj()); } diff --git a/src/mongo/db/repl/apply_ops.cpp b/src/mongo/db/repl/apply_ops.cpp index 9edb6be9bc8..ff37a113538 100644 --- a/src/mongo/db/repl/apply_ops.cpp +++ b/src/mongo/db/repl/apply_ops.cpp @@ -551,7 +551,7 @@ Status applyOps(OperationContext* opCtx, result->append("codeName", ErrorCodes::errorString(ex.code())); result->append("errmsg", ex.what()); result->append("results", ab.arr()); - return Status(ex.code(), ex.what()); + return ex.toStatus(); } return Status::OK(); diff --git a/src/mongo/db/repl/database_cloner.cpp b/src/mongo/db/repl/database_cloner.cpp index b9c1e23edea..e23e2ea145e 100644 --- a/src/mongo/db/repl/database_cloner.cpp +++ b/src/mongo/db/repl/database_cloner.cpp @@ -346,7 +346,7 @@ void DatabaseCloner::_listCollectionsCallback(const StatusWith<Fetcher::QueryRes const std::string collectionName = nameElement.String(); if (seen.find(collectionName) != seen.end()) { _finishCallback_inlock(lk, - {ErrorCodes::DuplicateKey, + {ErrorCodes::Error(51005), str::stream() << "collection info contains duplicate collection name " << "'" diff --git a/src/mongo/db/repl/database_cloner_test.cpp b/src/mongo/db/repl/database_cloner_test.cpp index 83c428340a7..7e594adbc59 100644 --- a/src/mongo/db/repl/database_cloner_test.cpp +++ b/src/mongo/db/repl/database_cloner_test.cpp @@ -541,7 +541,7 @@ TEST_F(DatabaseClonerTest, CollectionInfoNameDuplicate) { << _options2.toBSON())))); } - ASSERT_EQUALS(ErrorCodes::DuplicateKey, getStatus().code()); + ASSERT_EQUALS(51005, getStatus().code()); ASSERT_STRING_CONTAINS(getStatus().reason(), "duplicate collection name 'a'"); ASSERT_FALSE(_databaseCloner->isActive()); ASSERT_EQUALS(DatabaseCloner::State::kComplete, _databaseCloner->getState_forTest()); diff --git a/src/mongo/db/repl/repl_set_config.cpp b/src/mongo/db/repl/repl_set_config.cpp index e79ffa8fe89..731b2ed8f9f 100644 --- a/src/mongo/db/repl/repl_set_config.cpp +++ b/src/mongo/db/repl/repl_set_config.cpp @@ -353,7 +353,7 @@ Status ReplSetConfig::_parseSettingsSubdocument(const BSONObj& settings) { for (auto&& modeElement : gleModes) { if (_customWriteConcernModes.find(modeElement.fieldNameStringData()) != _customWriteConcernModes.end()) { - return Status(ErrorCodes::DuplicateKey, + return Status(ErrorCodes::Error(51001), str::stream() << kSettingsFieldName << '.' << kGetLastErrorModesFieldName << " contains multiple fields named " << modeElement.fieldName()); diff --git a/src/mongo/db/repl/repl_set_config_checks.cpp b/src/mongo/db/repl/repl_set_config_checks.cpp index 4105db31efe..07cff16f595 100644 --- a/src/mongo/db/repl/repl_set_config_checks.cpp +++ b/src/mongo/db/repl/repl_set_config_checks.cpp @@ -79,7 +79,7 @@ StatusWith<int> findSelfInConfig(ReplicationCoordinatorExternalState* externalSt << " all map to this node in new configuration version " << newConfig.getConfigVersion() << " for replica set " << newConfig.getReplSetName(); - return StatusWith<int>(ErrorCodes::DuplicateKey, message); + return StatusWith<int>(ErrorCodes::InvalidReplicaSetConfig, message); } int myIndex = std::distance(newConfig.membersBegin(), meConfigs.front()); diff --git a/src/mongo/db/repl/repl_set_config_checks_test.cpp b/src/mongo/db/repl/repl_set_config_checks_test.cpp index 482ca06e992..edea5453109 100644 --- a/src/mongo/db/repl/repl_set_config_checks_test.cpp +++ b/src/mongo/db/repl/repl_set_config_checks_test.cpp @@ -90,7 +90,7 @@ TEST_F(ServiceContextTest, ValidateConfigForInitiate_MustFindSelf) { validateConfigForInitiate(¬PresentExternalState, config, getGlobalServiceContext()) .getStatus()); ASSERT_EQUALS( - ErrorCodes::DuplicateKey, + ErrorCodes::InvalidReplicaSetConfig, validateConfigForInitiate(&presentTwiceExternalState, config, getGlobalServiceContext()) .getStatus()); ASSERT_EQUALS(1, @@ -703,7 +703,7 @@ TEST_F(ServiceContextTest, ValidateConfigForReconfig_MustFindSelf) { ¬PresentExternalState, oldConfig, newConfig, getGlobalServiceContext(), false) .getStatus()); ASSERT_EQUALS( - ErrorCodes::DuplicateKey, + ErrorCodes::InvalidReplicaSetConfig, validateConfigForReconfig( &presentThriceExternalState, oldConfig, newConfig, getGlobalServiceContext(), false) .getStatus()); @@ -718,7 +718,7 @@ TEST_F(ServiceContextTest, ValidateConfigForReconfig_MustFindSelf) { ¬PresentExternalState, oldConfig, newConfig, getGlobalServiceContext(), true) .getStatus()); ASSERT_EQUALS( - ErrorCodes::DuplicateKey, + ErrorCodes::InvalidReplicaSetConfig, validateConfigForReconfig( &presentThriceExternalState, oldConfig, newConfig, getGlobalServiceContext(), true) .getStatus()); diff --git a/src/mongo/db/repl/repl_set_config_test.cpp b/src/mongo/db/repl/repl_set_config_test.cpp index ebcc4b62773..8da994e68d7 100644 --- a/src/mongo/db/repl/repl_set_config_test.cpp +++ b/src/mongo/db/repl/repl_set_config_test.cpp @@ -820,7 +820,7 @@ TEST(ReplSetConfig, ParseFailsWithDuplicateGetLastErrorModesField) { << BSON("getLastErrorModes" << BSON("one" << BSON("tag" << 1) << "one" << BSON("tag" << 1))))); - ASSERT_EQUALS(ErrorCodes::DuplicateKey, status); + ASSERT_EQUALS(51001, status.code()); } TEST(ReplSetConfig, ParseFailsWithNonObjectGetLastErrorModesEntryField) { diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp index eaca1134471..3f237917497 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl.cpp @@ -556,7 +556,7 @@ void ReplicationCoordinatorImpl::_finishLoadLocalConfig( validateConfigForStartUp(_externalState.get(), localConfig, getServiceContext()); if (!myIndex.isOK()) { if (myIndex.getStatus() == ErrorCodes::NodeNotFound || - myIndex.getStatus() == ErrorCodes::DuplicateKey) { + myIndex.getStatus() == ErrorCodes::InvalidReplicaSetConfig) { warning() << "Locally stored replica set configuration does not have a valid entry " "for the current node; waiting for reconfig or remote heartbeat; Got \"" << myIndex.getStatus() << "\" while validating " << localConfig.toBSON(); diff --git a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp index f61fcd057b6..72fc9a9cbfe 100644 --- a/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl_heartbeat.cpp @@ -594,7 +594,7 @@ void ReplicationCoordinatorImpl::_heartbeatReconfigFinish( log() << "Cannot find self in new replica set configuration; I must be removed; " << myIndex.getStatus(); break; - case ErrorCodes::DuplicateKey: + case ErrorCodes::InvalidReplicaSetConfig: error() << "Several entries in new config represent this node; " "Removing self until an acceptable configuration arrives; " << myIndex.getStatus(); diff --git a/src/mongo/db/s/migration_destination_manager.cpp b/src/mongo/db/s/migration_destination_manager.cpp index 8e19ad74a65..4511d070a79 100644 --- a/src/mongo/db/s/migration_destination_manager.cpp +++ b/src/mongo/db/s/migration_destination_manager.cpp @@ -401,7 +401,7 @@ void MigrationDestinationManager::cloneDocumentsFromDonor( } } catch (...) { stdx::lock_guard<Client> lk(*opCtx->getClient()); - opCtx->getServiceContext()->killOperation(opCtx, exceptionToStatus().code()); + opCtx->getServiceContext()->killOperation(opCtx, ErrorCodes::Error(51008)); log() << "Batch insertion failed " << causedBy(redact(exceptionToStatus())); } }}; diff --git a/src/mongo/db/s/migration_destination_manager_test.cpp b/src/mongo/db/s/migration_destination_manager_test.cpp index 3d2b7fb4ba3..1b1cf8e12f3 100644 --- a/src/mongo/db/s/migration_destination_manager_test.cpp +++ b/src/mongo/db/s/migration_destination_manager_test.cpp @@ -152,10 +152,10 @@ TEST_F(MigrationDestinationManagerTest, CloneDocumentsCatchesInsertErrors) { ASSERT_THROWS_CODE_AND_WHAT(MigrationDestinationManager::cloneDocumentsFromDonor( operationContext(), insertBatchFn, fetchBatchFn), DBException, - ErrorCodes::FailedToParse, + 51008, "operation was interrupted"); - ASSERT_EQ(operationContext()->getKillStatus(), ErrorCodes::FailedToParse); + ASSERT_EQ(operationContext()->getKillStatus(), 51008); } } // namespace diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript index ad1295dfb0a..f25c138ced7 100644 --- a/src/mongo/db/storage/SConscript +++ b/src/mongo/db/storage/SConscript @@ -33,6 +33,17 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + 'duplicate_key_error_info', + ], + ) + +env.Library( + target='duplicate_key_error_info', + source=[ + 'duplicate_key_error_info.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', ], ) diff --git a/src/mongo/db/storage/duplicate_key_error_info.cpp b/src/mongo/db/storage/duplicate_key_error_info.cpp new file mode 100644 index 00000000000..911b3de0381 --- /dev/null +++ b/src/mongo/db/storage/duplicate_key_error_info.cpp @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2018-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 "mongo/platform/basic.h" + +#include "mongo/db/storage/duplicate_key_error_info.h" + +#include "mongo/base/init.h" +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/util/assert_util.h" + +namespace mongo { +namespace { + +MONGO_INIT_REGISTER_ERROR_EXTRA_INFO(DuplicateKeyErrorInfo); + +} // namespace + +void DuplicateKeyErrorInfo::serialize(BSONObjBuilder* bob) const { + bob->append("keyPattern", _keyPattern); +} + +std::shared_ptr<const ErrorExtraInfo> DuplicateKeyErrorInfo::parse(const BSONObj& obj) { + return std::make_shared<DuplicateKeyErrorInfo>(obj["keyPattern"].Obj()); +} + +} // namespace mongo diff --git a/src/mongo/db/storage/duplicate_key_error_info.h b/src/mongo/db/storage/duplicate_key_error_info.h new file mode 100644 index 00000000000..06a17cce94c --- /dev/null +++ b/src/mongo/db/storage/duplicate_key_error_info.h @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2018-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 "mongo/base/error_extra_info.h" +#include "mongo/bson/bsonobj.h" +#include "mongo/bson/bsonobjbuilder.h" + +namespace mongo { + +/** + * Represents an error returned from the storage engine when an attempt to insert a + * key into a unique index fails because the same key already exists. + */ +class DuplicateKeyErrorInfo final : public ErrorExtraInfo { +public: + static constexpr auto code = ErrorCodes::DuplicateKey; + + static std::shared_ptr<const ErrorExtraInfo> parse(const BSONObj&); + + explicit DuplicateKeyErrorInfo(const BSONObj& keyPattern) + : _keyPattern(keyPattern.getOwned()) {} + + void serialize(BSONObjBuilder* bob) const override; + + BSONObj toBSON() const { + BSONObjBuilder bob; + serialize(&bob); + return bob.obj(); + } + + const BSONObj& getKeyPattern() const { + return _keyPattern; + } + +private: + const BSONObj _keyPattern; +}; + +} // namespace mongo diff --git a/src/mongo/db/storage/index_entry_comparison.cpp b/src/mongo/db/storage/index_entry_comparison.cpp index 242df839462..67613639633 100644 --- a/src/mongo/db/storage/index_entry_comparison.cpp +++ b/src/mongo/db/storage/index_entry_comparison.cpp @@ -29,10 +29,12 @@ */ #include "mongo/platform/basic.h" +#include "mongo/db/storage/index_entry_comparison.h" + #include <ostream> #include "mongo/db/jsobj.h" -#include "mongo/db/storage/index_entry_comparison.h" +#include "mongo/db/storage/duplicate_key_error_info.h" namespace mongo { @@ -193,7 +195,7 @@ Status buildDupKeyErrorStatus(const BSONObj& key, } sb << builder.obj(); - return Status(ErrorCodes::DuplicateKey, sb.str()); + return Status(DuplicateKeyErrorInfo(keyPattern), sb.str()); } } // namespace mongo diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp index f02781c956d..fb524735923 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp @@ -188,7 +188,7 @@ Status WiredTigerUtil::getApplicationMetadata(OperationContext* opCtx, while ((ret = parser.next(&keyItem, &valueItem)) == 0) { const StringData key(keyItem.str, keyItem.len); if (keysSeen.count(key)) { - return Status(ErrorCodes::DuplicateKey, + return Status(ErrorCodes::Error(50998), str::stream() << "app_metadata must not contain duplicate keys. " << "Found multiple instances of key '" << key diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_util_test.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_util_test.cpp index 58f9aa63548..a3431d77cd3 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_util_test.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_util_test.cpp @@ -186,7 +186,7 @@ TEST_F(WiredTigerUtilMetadataTest, GetApplicationMetadataDuplicateKeys) { StatusWith<BSONObj> result = WiredTigerUtil::getApplicationMetadata(getOperationContext(), getURI()); ASSERT_NOT_OK(result.getStatus()); - ASSERT_EQUALS(ErrorCodes::DuplicateKey, result.getStatus().code()); + ASSERT_EQUALS(50998, result.getStatus().code()); } TEST_F(WiredTigerUtilMetadataTest, GetApplicationMetadataTypes) { |