Skip to content

Commit 82f77b6

Browse files
authored
v1::exception and v1::server_error (CXX-3237, CXX-3238) (#1501)
1 parent 6a176cf commit 82f77b6

File tree

11 files changed

+1033
-12
lines changed

11 files changed

+1033
-12
lines changed

src/mongocxx/include/mongocxx/v1/exception.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ class exception : public std::system_error {
116116
class internal;
117117

118118
private:
119+
MONGOCXX_ABI_NO_EXPORT /* explicit(false) */
120+
exception(std::error_code ec, char const* message, std::unique_ptr<impl> impl);
121+
119122
MONGOCXX_ABI_NO_EXPORT /* explicit(false) */
120123
exception(std::error_code ec, std::unique_ptr<impl> impl);
121124

src/mongocxx/include/mongocxx/v1/server_error.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,12 @@ class server_error : public v1::exception {
6666
///
6767
bsoncxx::v1::document::view MONGOCXX_ABI_CDECL raw() const;
6868

69+
class internal;
70+
6971
private:
72+
MONGOCXX_ABI_NO_EXPORT /* explicit(false) */
73+
server_error(int code, char const* message, std::unique_ptr<impl> impl);
74+
7075
MONGOCXX_ABI_NO_EXPORT void key_function() const override;
7176
};
7277

src/mongocxx/lib/mongocxx/v1/exception.cpp

Lines changed: 234 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,114 @@
1616

1717
//
1818

19+
#include <bsoncxx/v1/array/value.hpp>
20+
#include <bsoncxx/v1/document/value.hpp>
21+
#include <bsoncxx/v1/document/view.hpp>
22+
#include <bsoncxx/v1/stdx/string_view.hpp>
23+
#include <bsoncxx/v1/types/id.hpp>
24+
#include <bsoncxx/v1/types/view.hpp>
25+
26+
#include <mongocxx/v1/server_error.hh>
27+
28+
#include <cstring>
1929
#include <memory>
2030
#include <string>
2131
#include <system_error>
2232
#include <utility>
2333

34+
#include <bsoncxx/private/bson.hh>
2435
#include <bsoncxx/private/immortal.hh>
2536
#include <bsoncxx/private/make_unique.hh>
2637

38+
#include <mongocxx/private/mongoc.hh>
39+
2740
namespace mongocxx {
2841
namespace v1 {
2942

43+
namespace {
44+
45+
bool common_equivalence(v1::source_errc errc, int v, std::error_condition const& ec) noexcept {
46+
if (ec.category() == v1::source_error_category()) {
47+
return v != 0 ? errc == static_cast<v1::source_errc>(ec.value()) : false;
48+
}
49+
50+
if (ec.category() == v1::type_error_category()) {
51+
return v != 0 ? v1::type_errc::runtime_error == static_cast<v1::type_errc>(ec.value()) : false;
52+
}
53+
54+
return false;
55+
}
56+
57+
std::error_category const& mongoc_error_category() {
58+
class type final : public std::error_category {
59+
char const* name() const noexcept override {
60+
return "mongoc_error_code_t";
61+
}
62+
63+
std::string message(int v) const noexcept override {
64+
return std::string(this->name()) + ':' + std::to_string(v);
65+
}
66+
67+
bool equivalent(int v, std::error_condition const& ec) const noexcept override {
68+
return common_equivalence(v1::source_errc::mongoc, v, ec);
69+
}
70+
};
71+
72+
static bsoncxx::immortal<type> const instance;
73+
74+
return instance.value();
75+
}
76+
77+
std::error_category const& mongocrypt_error_category() {
78+
class type final : public std::error_category {
79+
char const* name() const noexcept override {
80+
return "mongocrypt_status_t";
81+
}
82+
83+
std::string message(int v) const noexcept override {
84+
return std::string(this->name()) + ':' + std::to_string(v);
85+
}
86+
87+
bool equivalent(int v, std::error_condition const& ec) const noexcept override {
88+
return common_equivalence(v1::source_errc::mongocrypt, v, ec);
89+
}
90+
};
91+
92+
static bsoncxx::immortal<type> const instance;
93+
94+
return instance.value();
95+
}
96+
97+
std::error_category const& unknown_error_category() {
98+
class type final : public std::error_category {
99+
char const* name() const noexcept override {
100+
return "unknown";
101+
}
102+
103+
std::string message(int v) const noexcept override {
104+
return std::string(this->name()) + ':' + std::to_string(v);
105+
}
106+
107+
bool equivalent(int v, std::error_condition const& ec) const noexcept override {
108+
if (ec.category() == v1::source_error_category()) {
109+
return false;
110+
}
111+
112+
if (ec.category() == v1::type_error_category()) {
113+
return v != 0 ? v1::type_errc::runtime_error == static_cast<v1::type_errc>(ec.value()) : false;
114+
}
115+
116+
return false;
117+
}
118+
};
119+
120+
static bsoncxx::immortal<type> const instance;
121+
122+
return instance.value();
123+
}
124+
125+
} // namespace
126+
30127
std::error_category const& source_error_category() {
31128
class type final : public std::error_category {
32129
char const* name() const noexcept override {
@@ -85,7 +182,22 @@ std::error_category const& type_error_category() {
85182
return instance.value();
86183
}
87184

88-
class exception::impl {};
185+
class exception::impl {
186+
public:
187+
bsoncxx::v1::array::value _error_labels;
188+
};
189+
190+
bool exception::has_error_label(bsoncxx::v1::stdx::string_view label) const {
191+
for (auto const& e : _impl->_error_labels) {
192+
if (e.type_view() == bsoncxx::v1::types::b_string{label}) {
193+
return true;
194+
}
195+
}
196+
return false;
197+
}
198+
199+
exception::exception(std::error_code ec, char const* message, std::unique_ptr<impl> impl)
200+
: std::system_error{ec, message}, _impl{std::move(impl)} {}
89201

90202
exception::exception(std::error_code ec, std::unique_ptr<impl> impl) : std::system_error{ec}, _impl{std::move(impl)} {}
91203

@@ -99,9 +211,130 @@ exception::exception(std::error_code ec, std::unique_ptr<impl> impl) : std::syst
99211
// vtable.
100212
void exception::key_function() const {}
101213

214+
namespace {
215+
216+
v1::exception make_exception(bson_error_t const& error) {
217+
auto const code = static_cast<int>(error.code);
218+
auto const raw_category = static_cast<int>(error.reserved);
219+
auto const message = static_cast<char const*>(error.message);
220+
auto const has_message = message[0] != '\0';
221+
222+
// Undocumented: see mongoc-error-private.h.
223+
switch (raw_category) {
224+
// MONGOC_ERROR_CATEGORY_BSON / BSON_ERROR_CATEGORY
225+
// Unlikely. Convert to MONGOC_ERROR_BSON_INVALID (18).
226+
case 1: {
227+
std::string what;
228+
what += "bson error code ";
229+
what += std::to_string(code);
230+
if (has_message) {
231+
what += ": ";
232+
what += message;
233+
}
234+
return v1::exception::internal::make(MONGOC_ERROR_BSON_INVALID, mongoc_error_category(), what.c_str());
235+
}
236+
237+
// MONGOC_ERROR_CATEGORY
238+
case 2: {
239+
if (has_message) {
240+
return v1::exception::internal::make(code, mongoc_error_category(), message);
241+
} else {
242+
return v1::exception::internal::make(code, mongoc_error_category());
243+
}
244+
}
245+
246+
// MONGOC_ERROR_CATEGORY_SERVER
247+
// Unlikely. Throw as `v1::exception` but use the correct error category.
248+
case 3: {
249+
if (has_message) {
250+
return v1::exception::internal::make(code, v1::server_error::internal::category(), message);
251+
} else {
252+
return v1::exception::internal::make(code, v1::server_error::internal::category());
253+
}
254+
}
255+
256+
// MONGOC_ERROR_CATEGORY_CRYPT
257+
case 4: {
258+
if (has_message) {
259+
return v1::exception::internal::make(code, mongocrypt_error_category(), message);
260+
} else {
261+
return v1::exception::internal::make(code, mongocrypt_error_category());
262+
}
263+
}
264+
265+
// MONGOC_ERROR_CATEGORY_SASL
266+
// Unlikely. Convert to MONGOC_ERROR_CLIENT_AUTHENTICATE (11).
267+
case 5: {
268+
std::string what;
269+
what += "sasl error code ";
270+
what += std::to_string(code);
271+
if (has_message) {
272+
what += ": ";
273+
what += message;
274+
}
275+
return v1::exception::internal::make(
276+
MONGOC_ERROR_CLIENT_AUTHENTICATE, mongoc_error_category(), what.c_str());
277+
}
278+
279+
// Unlikely.
280+
default: {
281+
std::string what;
282+
what += "unknown error category ";
283+
what += std::to_string(raw_category);
284+
if (has_message) {
285+
what += ": ";
286+
what += message;
287+
}
288+
289+
return v1::exception::internal::make(code, unknown_error_category(), what.c_str());
290+
}
291+
}
292+
}
293+
294+
} // namespace
295+
102296
exception exception::internal::make(std::error_code ec) {
103297
return {ec, bsoncxx::make_unique<impl>()};
104298
}
105299

300+
exception exception::internal::make(int code, std::error_category const& category, char const* message) {
301+
return {std::error_code{code, category}, message, bsoncxx::make_unique<impl>()};
302+
}
303+
304+
exception exception::internal::make(int code, std::error_category const& category) {
305+
return {std::error_code{code, category}, bsoncxx::make_unique<impl>()};
306+
}
307+
308+
void exception::internal::set_error_labels(exception& self, bsoncxx::v1::document::view v) {
309+
auto& _error_labels = self._impl->_error_labels;
310+
311+
auto const e = v["errorLabels"];
312+
313+
if (e && e.type_id() == bsoncxx::v1::types::id::k_array) {
314+
_error_labels = e.get_array().value;
315+
} else {
316+
_error_labels = bsoncxx::v1::array::value{};
317+
}
318+
}
319+
320+
void throw_exception(bson_error_t const& error) {
321+
throw make_exception(error);
322+
}
323+
324+
void throw_exception(bson_error_t const& error, bsoncxx::v1::document::value doc) {
325+
// Server-side error.
326+
if (auto const code = doc["code"]) {
327+
if (code.type_id() == bsoncxx::v1::types::id::k_int32) {
328+
auto const ex = make_exception(error);
329+
throw v1::server_error::internal::make(int{code.get_int32().value}, ex.what(), std::move(doc), ex.code());
330+
}
331+
}
332+
333+
// Client-side error.
334+
auto ex = make_exception(error);
335+
exception::internal::set_error_labels(ex, doc);
336+
throw ex;
337+
}
338+
106339
} // namespace v1
107340
} // namespace mongocxx

src/mongocxx/lib/mongocxx/v1/exception.hh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@
1616

1717
//
1818

19+
#include <bsoncxx/v1/document/value.hpp>
20+
#include <bsoncxx/v1/document/view.hpp>
21+
1922
#include <system_error>
2023

24+
#include <bsoncxx/private/bson.hh>
25+
2126
#include <mongocxx/private/export.hh>
2227

2328
namespace mongocxx {
@@ -26,7 +31,22 @@ namespace v1 {
2631
class exception::internal {
2732
public:
2833
static MONGOCXX_ABI_EXPORT_CDECL_TESTING(exception) make(std::error_code ec);
34+
static MONGOCXX_ABI_EXPORT_CDECL_TESTING(exception)
35+
make(int code, std::error_category const& category, char const* message);
36+
static MONGOCXX_ABI_EXPORT_CDECL_TESTING(exception) make(int code, std::error_category const& category);
37+
38+
static MONGOCXX_ABI_EXPORT_CDECL_TESTING(void) set_error_labels(exception& self, bsoncxx::v1::document::view v);
2939
};
3040

41+
[[noreturn]] MONGOCXX_ABI_EXPORT_CDECL_TESTING(void) throw_exception(bson_error_t const& error);
42+
43+
[[noreturn]] MONGOCXX_ABI_EXPORT_CDECL_TESTING(void) throw_exception(
44+
bson_error_t const& error,
45+
bsoncxx::v1::document::value raw);
46+
47+
[[noreturn]] inline void throw_exception(bson_error_t const& error, bsoncxx::v1::document::view raw) {
48+
throw_exception(error, bsoncxx::v1::document::value{raw});
49+
}
50+
3151
} // namespace v1
3252
} // namespace mongocxx

0 commit comments

Comments
 (0)