diff options
author | Mathias Stearn <mathias@10gen.com> | 2017-10-12 14:14:21 -0400 |
---|---|---|
committer | Mathias Stearn <mathias@10gen.com> | 2017-11-02 14:25:21 -0400 |
commit | d3e1fd3c1a35b0da6734dbdb28c275f8fa5cc159 (patch) | |
tree | 0b5fddbceb454bbd1a3506099f018dd3133f2234 /src/mongo/base | |
parent | a0ebc55db521792632fb3ece87edafec327cc2a9 (diff) | |
download | mongo-d3e1fd3c1a35b0da6734dbdb28c275f8fa5cc159.tar.gz |
SERVER-31622 Support catching DBException by code or category
Diffstat (limited to 'src/mongo/base')
-rw-r--r-- | src/mongo/base/error_codes.tpl.cpp | 24 | ||||
-rw-r--r-- | src/mongo/base/error_codes.tpl.h | 69 | ||||
-rw-r--r-- | src/mongo/base/generate_error_codes.py | 33 | ||||
-rw-r--r-- | src/mongo/base/status.h | 8 | ||||
-rw-r--r-- | src/mongo/base/status_test.cpp | 13 |
5 files changed, 129 insertions, 18 deletions
diff --git a/src/mongo/base/error_codes.tpl.cpp b/src/mongo/base/error_codes.tpl.cpp index c1188c3bd15..3c8d777dc45 100644 --- a/src/mongo/base/error_codes.tpl.cpp +++ b/src/mongo/base/error_codes.tpl.cpp @@ -74,4 +74,28 @@ bool ErrorCodes::is${cat.name}(Error err) { } //#end for +void error_details::throwExceptionForStatus(const Status& status) { + /** + * This type is used for all exceptions that don't have a more specific type. It is defined + * locally in this function to prevent anyone from catching it specifically separately from + * AssertionException. + */ + class NonspecificAssertionException final : public AssertionException { + public: + using AssertionException::AssertionException; + + private: + void defineOnlyInFinalSubclassToPreventSlicing() final {} + }; + + switch (status.code()) { + //#for $ec in $codes + case ErrorCodes::$ec.name: + throw ExceptionFor<ErrorCodes::$ec.name>(status); + //#end for + default: + throw NonspecificAssertionException(status); + } +} + } // namespace mongo diff --git a/src/mongo/base/error_codes.tpl.h b/src/mongo/base/error_codes.tpl.h index a5ebd55042b..a2b2cbb7f06 100644 --- a/src/mongo/base/error_codes.tpl.h +++ b/src/mongo/base/error_codes.tpl.h @@ -33,9 +33,18 @@ #include <string> #include "mongo/base/string_data.h" +#include "mongo/platform/compiler.h" namespace mongo { +class Status; + +enum class ErrorCategory { + //#for $cat in $categories + ${cat.name}, + //#end for +}; + /** * This is a generated class containing a table of error codes and their corresponding error * strings. The class is derived from the definitions in src/mongo/base/error_codes.err file and the @@ -72,11 +81,69 @@ public: return static_cast<Error>(code); } + /** + * Generic predicate to test if a given error code is in a category. + * + * This version is intended to simplify forwarding by Status and DBException. Non-generic + * callers should just use the specific isCategoryName() methods instead. + */ + template <ErrorCategory category> + static bool isA(Error code); + //#for $cat in $categories - static bool is${cat.name}(Error err); + static bool is${cat.name}(Error code); //#end for }; std::ostream& operator<<(std::ostream& stream, ErrorCodes::Error code); +//#for $cat in $categories +template <> +inline bool ErrorCodes::isA<ErrorCategory::$cat.name>(Error code) { + return is${cat.name}(code); +} +//#end for + +/** + * This namespace contains implementation details for our error handling code and should not be used + * directly in general code. + */ +namespace error_details { + +template <int32_t code> +constexpr bool isNamedCode = false; +//#for $ec in $codes +template <> +constexpr bool isNamedCode<ErrorCodes::$ec.name> = true; +//#end for + +MONGO_COMPILER_NORETURN void throwExceptionForStatus(const Status& status); + +template <ErrorCategory... categories> +struct CategoryList; + +template <ErrorCodes::Error code> +struct ErrorCategoriesForImpl { + using type = CategoryList<>; +}; + +//#for $ec in $codes: +//#if $ec.categories +template <> +struct ErrorCategoriesForImpl<ErrorCodes::$ec.name> { + using type = CategoryList< + //#for $i, $cat in enumerate($ec.categories) + //#set $comma = '' if i == len($ec.categories) - 1 else ', ' + ErrorCategory::$cat$comma + //#end for + >; +}; +//#end if +//#end for + +template <ErrorCodes::Error code> +using ErrorCategoriesFor = typename ErrorCategoriesForImpl<code>::type; + +} // namespace error_details + } // namespace mongo diff --git a/src/mongo/base/generate_error_codes.py b/src/mongo/base/generate_error_codes.py index d3da9970692..420ee964ffe 100644 --- a/src/mongo/base/generate_error_codes.py +++ b/src/mongo/base/generate_error_codes.py @@ -56,13 +56,16 @@ def render_template(template_path, **kw): useCache=False) return str(template(**kw)) -ErrorCode = namedtuple('ErrorCode', ['name', 'code']) -def makeErrorCode(name, code): - return ErrorCode(name, code) +class ErrorCode: + def __init__(self, name, code): + self.name = name + self.code = code + self.categories = [] -ErrorClass = namedtuple('ErrorClass', ['name', 'codes']) -def makeErrorClass(name, codes): - return ErrorClass(name, codes) +class ErrorClass: + def __init__(self, name, codes): + self.name = name + self.codes = codes def main(argv): # Parse and validate argv. @@ -107,9 +110,10 @@ def parse_error_definitions_from_file(errors_filename): error_codes = [] error_classes = [] eval(errors_code, - dict(error_code=lambda *args, **kw: error_codes.append(makeErrorCode(*args, **kw)), - error_class=lambda *args: error_classes.append(makeErrorClass(*args)))) + dict(error_code=lambda *args, **kw: error_codes.append(ErrorCode(*args, **kw)), + error_class=lambda *args: error_classes.append(ErrorClass(*args)))) error_codes.sort(key=lambda x: x.code) + return error_codes, error_classes def check_for_conflicts(error_codes, error_classes): @@ -157,13 +161,16 @@ def has_duplicate_error_classes(error_classes): return failed def has_missing_error_codes(error_codes, error_classes): - code_names = set(ec[0] for ec in error_codes) + code_names = dict((ec.name, ec) for ec in error_codes) failed = False - for class_name, class_code_names in error_classes: - for name in class_code_names: - if name not in code_names: - sys.stdout.write('Undeclared error code %s in class %s\n' % (name, class_name)) + for category in error_classes: + for name in category.codes: + try: + code_names[name].categories.append(category.name) + except KeyError: + sys.stdout.write('Undeclared error code %s in class %s\n' % (name, category.name)) failed = True + return failed if __name__ == '__main__': diff --git a/src/mongo/base/status.h b/src/mongo/base/status.h index 3a4462d057d..7c9a3c0ed3c 100644 --- a/src/mongo/base/status.h +++ b/src/mongo/base/status.h @@ -131,6 +131,14 @@ public: std::string toString() const; /** + * Returns true if this Status's code is a member of the given category. + */ + template <ErrorCategory category> + bool isA() const { + return ErrorCodes::isA<category>(code()); + } + + /** * Call this method to indicate that it is your intention to ignore a returned status. Ignoring * is only possible if the value being ignored is an xvalue -- it is not appropriate to create a * status variable and then ignore it. diff --git a/src/mongo/base/status_test.cpp b/src/mongo/base/status_test.cpp index c473c24b3e0..57b2ac8a2d1 100644 --- a/src/mongo/base/status_test.cpp +++ b/src/mongo/base/status_test.cpp @@ -36,17 +36,21 @@ #include "mongo/unittest/unittest.h" #include "mongo/util/assert_util.h" +namespace mongo { namespace { -using mongo::ErrorCodes; -using mongo::Status; - TEST(Basic, Accessors) { Status status(ErrorCodes::MaxError, "error"); ASSERT_EQUALS(status.code(), ErrorCodes::MaxError); ASSERT_EQUALS(status.reason(), "error"); } +TEST(Basic, IsA) { + ASSERT(!Status(ErrorCodes::BadValue, "").isA<ErrorCategory::Interruption>()); + ASSERT(Status(ErrorCodes::Interrupted, "").isA<ErrorCategory::Interruption>()); + ASSERT(!Status(ErrorCodes::Interrupted, "").isA<ErrorCategory::ShutdownError>()); +} + TEST(Basic, OKIsAValidStatus) { Status status = Status::OK(); ASSERT_EQUALS(status.code(), ErrorCodes::OK); @@ -249,4 +253,5 @@ TEST(Transformers, ExceptionToStatus) { ASSERT_TRUE(fromBoostExcept.reason().find("boost::exception") != std::string::npos); } -} // unnamed namespace +} // namespace +} // namespace mongo |