summaryrefslogtreecommitdiff
path: root/src/mongo/base
diff options
context:
space:
mode:
authorMathias Stearn <mathias@10gen.com>2017-10-12 14:14:21 -0400
committerMathias Stearn <mathias@10gen.com>2017-11-02 14:25:21 -0400
commitd3e1fd3c1a35b0da6734dbdb28c275f8fa5cc159 (patch)
tree0b5fddbceb454bbd1a3506099f018dd3133f2234 /src/mongo/base
parenta0ebc55db521792632fb3ece87edafec327cc2a9 (diff)
downloadmongo-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.cpp24
-rw-r--r--src/mongo/base/error_codes.tpl.h69
-rw-r--r--src/mongo/base/generate_error_codes.py33
-rw-r--r--src/mongo/base/status.h8
-rw-r--r--src/mongo/base/status_test.cpp13
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