Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/mongocxx/include/mongocxx/v1/exception.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ class exception : public std::system_error {
class internal;

private:
MONGOCXX_ABI_NO_EXPORT /* explicit(false) */
exception(std::error_code ec, char const* message, std::unique_ptr<impl> impl);

MONGOCXX_ABI_NO_EXPORT /* explicit(false) */
exception(std::error_code ec, std::unique_ptr<impl> impl);

Expand Down
5 changes: 5 additions & 0 deletions src/mongocxx/include/mongocxx/v1/server_error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,12 @@ class server_error : public v1::exception {
///
bsoncxx::v1::document::view MONGOCXX_ABI_CDECL raw() const;

class internal;

private:
MONGOCXX_ABI_NO_EXPORT /* explicit(false) */
server_error(int code, char const* message, std::unique_ptr<impl> impl);

MONGOCXX_ABI_NO_EXPORT void key_function() const override;
};

Expand Down
235 changes: 234 additions & 1 deletion src/mongocxx/lib/mongocxx/v1/exception.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,114 @@

//

#include <bsoncxx/v1/array/value.hpp>
#include <bsoncxx/v1/document/value.hpp>
#include <bsoncxx/v1/document/view.hpp>
#include <bsoncxx/v1/stdx/string_view.hpp>
#include <bsoncxx/v1/types/id.hpp>
#include <bsoncxx/v1/types/view.hpp>

#include <mongocxx/v1/server_error.hh>

#include <cstring>
#include <memory>
#include <string>
#include <system_error>
#include <utility>

#include <bsoncxx/private/bson.hh>
#include <bsoncxx/private/immortal.hh>
#include <bsoncxx/private/make_unique.hh>

#include <mongocxx/private/mongoc.hh>

namespace mongocxx {
namespace v1 {

namespace {

bool common_equivalence(v1::source_errc errc, int v, std::error_condition const& ec) noexcept {
if (ec.category() == v1::source_error_category()) {
return v != 0 ? errc == static_cast<v1::source_errc>(ec.value()) : false;
}

if (ec.category() == v1::type_error_category()) {
return v != 0 ? v1::type_errc::runtime_error == static_cast<v1::type_errc>(ec.value()) : false;
}

return false;
}

std::error_category const& mongoc_error_category() {
class type final : public std::error_category {
char const* name() const noexcept override {
return "mongoc_error_code_t";
}

std::string message(int v) const noexcept override {
return std::string(this->name()) + ':' + std::to_string(v);
}

bool equivalent(int v, std::error_condition const& ec) const noexcept override {
return common_equivalence(v1::source_errc::mongoc, v, ec);
}
};

static bsoncxx::immortal<type> const instance;

return instance.value();
}

std::error_category const& mongocrypt_error_category() {
class type final : public std::error_category {
char const* name() const noexcept override {
return "mongocrypt_status_t";
}

std::string message(int v) const noexcept override {
return std::string(this->name()) + ':' + std::to_string(v);
}

bool equivalent(int v, std::error_condition const& ec) const noexcept override {
return common_equivalence(v1::source_errc::mongocrypt, v, ec);
}
};

static bsoncxx::immortal<type> const instance;

return instance.value();
}

std::error_category const& unknown_error_category() {
class type final : public std::error_category {
char const* name() const noexcept override {
return "unknown";
}

std::string message(int v) const noexcept override {
return std::string(this->name()) + ':' + std::to_string(v);
}

bool equivalent(int v, std::error_condition const& ec) const noexcept override {
if (ec.category() == v1::source_error_category()) {
return false;
}

if (ec.category() == v1::type_error_category()) {
return v != 0 ? v1::type_errc::runtime_error == static_cast<v1::type_errc>(ec.value()) : false;
}

return false;
}
};

static bsoncxx::immortal<type> const instance;

return instance.value();
}

} // namespace

std::error_category const& source_error_category() {
class type final : public std::error_category {
char const* name() const noexcept override {
Expand Down Expand Up @@ -85,7 +182,22 @@ std::error_category const& type_error_category() {
return instance.value();
}

class exception::impl {};
class exception::impl {
public:
bsoncxx::v1::array::value _error_labels;
};

bool exception::has_error_label(bsoncxx::v1::stdx::string_view label) const {
for (auto const& e : _impl->_error_labels) {
if (e.type_view() == bsoncxx::v1::types::b_string{label}) {
return true;
}
}
return false;
}

exception::exception(std::error_code ec, char const* message, std::unique_ptr<impl> impl)
: std::system_error{ec, message}, _impl{std::move(impl)} {}

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

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

namespace {

v1::exception make_exception(bson_error_t const& error) {
auto const code = static_cast<int>(error.code);
auto const raw_category = static_cast<int>(error.reserved);
auto const message = static_cast<char const*>(error.message);
auto const has_message = message[0] != '\0';

// Undocumented: see mongoc-error-private.h.
switch (raw_category) {
// MONGOC_ERROR_CATEGORY_BSON / BSON_ERROR_CATEGORY
// Unlikely. Convert to MONGOC_ERROR_BSON_INVALID (18).
case 1: {
std::string what;
what += "bson error code ";
what += std::to_string(code);
if (has_message) {
what += ": ";
what += message;
}
return v1::exception::internal::make(MONGOC_ERROR_BSON_INVALID, mongoc_error_category(), what.c_str());
}

// MONGOC_ERROR_CATEGORY
case 2: {
if (has_message) {
return v1::exception::internal::make(code, mongoc_error_category(), message);
} else {
return v1::exception::internal::make(code, mongoc_error_category());
}
}

// MONGOC_ERROR_CATEGORY_SERVER
// Unlikely. Throw as `v1::exception` but use the correct error category.
case 3: {
if (has_message) {
return v1::exception::internal::make(code, v1::server_error::internal::category(), message);
} else {
return v1::exception::internal::make(code, v1::server_error::internal::category());
}
}

// MONGOC_ERROR_CATEGORY_CRYPT
case 4: {
if (has_message) {
return v1::exception::internal::make(code, mongocrypt_error_category(), message);
} else {
return v1::exception::internal::make(code, mongocrypt_error_category());
}
}

// MONGOC_ERROR_CATEGORY_SASL
// Unlikely. Convert to MONGOC_ERROR_CLIENT_AUTHENTICATE (11).
case 5: {
std::string what;
what += "sasl error code ";
what += std::to_string(code);
if (has_message) {
what += ": ";
what += message;
}
return v1::exception::internal::make(
MONGOC_ERROR_CLIENT_AUTHENTICATE, mongoc_error_category(), what.c_str());
}

// Unlikely.
default: {
std::string what;
what += "unknown error category ";
what += std::to_string(raw_category);
if (has_message) {
what += ": ";
what += message;
}

return v1::exception::internal::make(code, unknown_error_category(), what.c_str());
}
}
}

} // namespace

exception exception::internal::make(std::error_code ec) {
return {ec, bsoncxx::make_unique<impl>()};
}

exception exception::internal::make(int code, std::error_category const& category, char const* message) {
return {std::error_code{code, category}, message, bsoncxx::make_unique<impl>()};
}

exception exception::internal::make(int code, std::error_category const& category) {
return {std::error_code{code, category}, bsoncxx::make_unique<impl>()};
}

void exception::internal::set_error_labels(exception& self, bsoncxx::v1::document::view v) {
auto& _error_labels = self._impl->_error_labels;

auto const e = v["errorLabels"];

if (e && e.type_id() == bsoncxx::v1::types::id::k_array) {
_error_labels = e.get_array().value;
} else {
_error_labels = bsoncxx::v1::array::value{};
}
}

void throw_exception(bson_error_t const& error) {
throw make_exception(error);
}

void throw_exception(bson_error_t const& error, bsoncxx::v1::document::value doc) {
// Server-side error.
if (auto const code = doc["code"]) {
if (code.type_id() == bsoncxx::v1::types::id::k_int32) {
auto const ex = make_exception(error);
throw v1::server_error::internal::make(int{code.get_int32().value}, ex.what(), std::move(doc), ex.code());
}
}

// Client-side error.
auto ex = make_exception(error);
exception::internal::set_error_labels(ex, doc);
throw ex;
}

} // namespace v1
} // namespace mongocxx
20 changes: 20 additions & 0 deletions src/mongocxx/lib/mongocxx/v1/exception.hh
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@

//

#include <bsoncxx/v1/document/value.hpp>
#include <bsoncxx/v1/document/view.hpp>

#include <system_error>

#include <bsoncxx/private/bson.hh>

#include <mongocxx/private/export.hh>

namespace mongocxx {
Expand All @@ -26,7 +31,22 @@ namespace v1 {
class exception::internal {
public:
static MONGOCXX_ABI_EXPORT_CDECL_TESTING(exception) make(std::error_code ec);
static MONGOCXX_ABI_EXPORT_CDECL_TESTING(exception)
make(int code, std::error_category const& category, char const* message);
static MONGOCXX_ABI_EXPORT_CDECL_TESTING(exception) make(int code, std::error_category const& category);

static MONGOCXX_ABI_EXPORT_CDECL_TESTING(void) set_error_labels(exception& self, bsoncxx::v1::document::view v);
};

[[noreturn]] MONGOCXX_ABI_EXPORT_CDECL_TESTING(void) throw_exception(bson_error_t const& error);

[[noreturn]] MONGOCXX_ABI_EXPORT_CDECL_TESTING(void) throw_exception(
bson_error_t const& error,
bsoncxx::v1::document::value raw);

[[noreturn]] inline void throw_exception(bson_error_t const& error, bsoncxx::v1::document::view raw) {
throw_exception(error, bsoncxx::v1::document::value{raw});
}

} // namespace v1
} // namespace mongocxx
Loading