mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-12 10:34:21 +00:00
Improve messages for errors in access storages.
This commit is contained in:
parent
92b9f4a88d
commit
7d1951a79b
@ -24,16 +24,141 @@ namespace
|
|||||||
using EntityType = IAccessStorage::EntityType;
|
using EntityType = IAccessStorage::EntityType;
|
||||||
using EntityTypeInfo = IAccessStorage::EntityTypeInfo;
|
using EntityTypeInfo = IAccessStorage::EntityTypeInfo;
|
||||||
|
|
||||||
bool isNotFoundErrorCode(int error_code)
|
|
||||||
|
String outputID(const UUID & id)
|
||||||
{
|
{
|
||||||
if (error_code == ErrorCodes::ACCESS_ENTITY_NOT_FOUND)
|
return "ID(" + toString(id) + ")";
|
||||||
return true;
|
}
|
||||||
|
|
||||||
for (auto type : ext::range(EntityType::MAX))
|
String outputTypeAndNameOrID(const IAccessStorage & storage, const UUID & id)
|
||||||
if (error_code == EntityTypeInfo::get(type).not_found_error_code)
|
{
|
||||||
return true;
|
auto entity = storage.tryRead(id);
|
||||||
|
if (entity)
|
||||||
|
return entity->outputTypeAndName();
|
||||||
|
return outputID(id);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
|
template <typename Func, typename ResultType = std::result_of_t<Func()>>
|
||||||
|
ResultType doTry(const Func & func)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return func();
|
||||||
|
}
|
||||||
|
catch (Exception &)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <bool ignore_errors, typename T, typename ApplyFunc, typename GetNameFunc = std::nullptr_t,
|
||||||
|
typename ResultTypeOfApplyFunc = std::result_of_t<ApplyFunc(T)>,
|
||||||
|
typename ResultType = std::conditional_t<std::is_same_v<ResultTypeOfApplyFunc, void>, void, std::vector<ResultTypeOfApplyFunc>>>
|
||||||
|
ResultType applyToMultipleEntities(
|
||||||
|
const std::vector<T> & multiple_entities,
|
||||||
|
const ApplyFunc & apply_function,
|
||||||
|
const char * error_message_format [[maybe_unused]] = nullptr,
|
||||||
|
const GetNameFunc & get_name_function [[maybe_unused]] = nullptr)
|
||||||
|
{
|
||||||
|
std::optional<Exception> exception;
|
||||||
|
std::vector<bool> success;
|
||||||
|
|
||||||
|
auto helper = [&](const auto & apply_and_store_result_function)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i != multiple_entities.size(); ++i)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
apply_and_store_result_function(multiple_entities[i]);
|
||||||
|
if constexpr (!ignore_errors)
|
||||||
|
success[i] = true;
|
||||||
|
}
|
||||||
|
catch (Exception & e)
|
||||||
|
{
|
||||||
|
if (!ignore_errors && !exception)
|
||||||
|
exception.emplace(e);
|
||||||
|
}
|
||||||
|
catch (Poco::Exception & e)
|
||||||
|
{
|
||||||
|
if (!ignore_errors && !exception)
|
||||||
|
exception.emplace(Exception::CreateFromPocoTag{}, e);
|
||||||
|
}
|
||||||
|
catch (std::exception & e)
|
||||||
|
{
|
||||||
|
if (!ignore_errors && !exception)
|
||||||
|
exception.emplace(Exception::CreateFromSTDTag{}, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<ResultType, void>)
|
||||||
|
{
|
||||||
|
if (multiple_entities.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (multiple_entities.size() == 1)
|
||||||
|
{
|
||||||
|
apply_function(multiple_entities.front());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (!ignore_errors)
|
||||||
|
success.resize(multiple_entities.size(), false);
|
||||||
|
|
||||||
|
helper(apply_function);
|
||||||
|
|
||||||
|
if (ignore_errors || !exception)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResultType result;
|
||||||
|
if (multiple_entities.empty())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (multiple_entities.size() == 1)
|
||||||
|
{
|
||||||
|
result.emplace_back(apply_function(multiple_entities.front()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.reserve(multiple_entities.size());
|
||||||
|
if constexpr (!ignore_errors)
|
||||||
|
success.resize(multiple_entities.size(), false);
|
||||||
|
|
||||||
|
helper([&](const T & entity) { result.emplace_back(apply_function(entity)); });
|
||||||
|
|
||||||
|
if (ignore_errors || !exception)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (!ignore_errors)
|
||||||
|
{
|
||||||
|
Strings succeeded_names_list;
|
||||||
|
Strings failed_names_list;
|
||||||
|
for (size_t i = 0; i != multiple_entities.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto & entity = multiple_entities[i];
|
||||||
|
String name = get_name_function(entity);
|
||||||
|
if (success[i])
|
||||||
|
succeeded_names_list.emplace_back(name);
|
||||||
|
else
|
||||||
|
failed_names_list.emplace_back(name);
|
||||||
|
}
|
||||||
|
String succeeded_names = boost::algorithm::join(succeeded_names_list, ", ");
|
||||||
|
String failed_names = boost::algorithm::join(failed_names_list, ", ");
|
||||||
|
if (succeeded_names.empty())
|
||||||
|
succeeded_names = "none";
|
||||||
|
|
||||||
|
String error_message = error_message_format;
|
||||||
|
boost::replace_all(error_message, "{succeeded_names}", succeeded_names);
|
||||||
|
boost::replace_all(error_message, "{failed_names}", failed_names);
|
||||||
|
exception->addMessage(error_message);
|
||||||
|
exception->rethrow();
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,14 +216,7 @@ bool IAccessStorage::exists(const UUID & id) const
|
|||||||
|
|
||||||
AccessEntityPtr IAccessStorage::tryReadBase(const UUID & id) const
|
AccessEntityPtr IAccessStorage::tryReadBase(const UUID & id) const
|
||||||
{
|
{
|
||||||
try
|
return doTry([&] { return readImpl(id); });
|
||||||
{
|
|
||||||
return readImpl(id);
|
|
||||||
}
|
|
||||||
catch (Exception &)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -110,14 +228,7 @@ String IAccessStorage::readName(const UUID & id) const
|
|||||||
|
|
||||||
std::optional<String> IAccessStorage::tryReadName(const UUID & id) const
|
std::optional<String> IAccessStorage::tryReadName(const UUID & id) const
|
||||||
{
|
{
|
||||||
try
|
return doTry([&] { return std::optional<String>{readNameImpl(id)}; });
|
||||||
{
|
|
||||||
return readNameImpl(id);
|
|
||||||
}
|
|
||||||
catch (Exception &)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -129,56 +240,25 @@ UUID IAccessStorage::insert(const AccessEntityPtr & entity)
|
|||||||
|
|
||||||
std::vector<UUID> IAccessStorage::insert(const std::vector<AccessEntityPtr> & multiple_entities)
|
std::vector<UUID> IAccessStorage::insert(const std::vector<AccessEntityPtr> & multiple_entities)
|
||||||
{
|
{
|
||||||
std::vector<UUID> ids;
|
return applyToMultipleEntities</* ignore_errors = */ false>(
|
||||||
ids.reserve(multiple_entities.size());
|
multiple_entities,
|
||||||
String error_message;
|
[this](const AccessEntityPtr & entity) { return insertImpl(entity, /* replace_if_exists = */ false); },
|
||||||
for (const auto & entity : multiple_entities)
|
"Couldn't insert {failed_names}. Successfully inserted: {succeeded_names}",
|
||||||
{
|
[](const AccessEntityPtr & entity) { return entity->outputTypeAndName(); });
|
||||||
try
|
|
||||||
{
|
|
||||||
ids.push_back(insertImpl(entity, false));
|
|
||||||
}
|
|
||||||
catch (Exception & e)
|
|
||||||
{
|
|
||||||
if (e.code() != ErrorCodes::ACCESS_ENTITY_ALREADY_EXISTS)
|
|
||||||
throw;
|
|
||||||
error_message += (error_message.empty() ? "" : ". ") + e.message();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!error_message.empty())
|
|
||||||
throw Exception(error_message, ErrorCodes::ACCESS_ENTITY_ALREADY_EXISTS);
|
|
||||||
return ids;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::optional<UUID> IAccessStorage::tryInsert(const AccessEntityPtr & entity)
|
std::optional<UUID> IAccessStorage::tryInsert(const AccessEntityPtr & entity)
|
||||||
{
|
{
|
||||||
try
|
return doTry([&] { return std::optional<UUID>{insertImpl(entity, false)}; });
|
||||||
{
|
|
||||||
return insertImpl(entity, false);
|
|
||||||
}
|
|
||||||
catch (Exception &)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<UUID> IAccessStorage::tryInsert(const std::vector<AccessEntityPtr> & multiple_entities)
|
std::vector<UUID> IAccessStorage::tryInsert(const std::vector<AccessEntityPtr> & multiple_entities)
|
||||||
{
|
{
|
||||||
std::vector<UUID> ids;
|
return applyToMultipleEntities</* ignore_errors = */ true>(
|
||||||
ids.reserve(multiple_entities.size());
|
multiple_entities,
|
||||||
for (const auto & entity : multiple_entities)
|
[this](const AccessEntityPtr & entity) { return insertImpl(entity, /* replace_if_exists = */ false); });
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ids.push_back(insertImpl(entity, false));
|
|
||||||
}
|
|
||||||
catch (Exception &)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -190,11 +270,11 @@ UUID IAccessStorage::insertOrReplace(const AccessEntityPtr & entity)
|
|||||||
|
|
||||||
std::vector<UUID> IAccessStorage::insertOrReplace(const std::vector<AccessEntityPtr> & multiple_entities)
|
std::vector<UUID> IAccessStorage::insertOrReplace(const std::vector<AccessEntityPtr> & multiple_entities)
|
||||||
{
|
{
|
||||||
std::vector<UUID> ids;
|
return applyToMultipleEntities</* ignore_errors = */ false>(
|
||||||
ids.reserve(multiple_entities.size());
|
multiple_entities,
|
||||||
for (const auto & entity : multiple_entities)
|
[this](const AccessEntityPtr & entity) { return insertImpl(entity, /* replace_if_exists = */ true); },
|
||||||
ids.push_back(insertImpl(entity, true));
|
"Couldn't insert {failed_names}. Successfully inserted: {succeeded_names}",
|
||||||
return ids;
|
[](const AccessEntityPtr & entity) -> String { return entity->outputTypeAndName(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -206,60 +286,25 @@ void IAccessStorage::remove(const UUID & id)
|
|||||||
|
|
||||||
void IAccessStorage::remove(const std::vector<UUID> & ids)
|
void IAccessStorage::remove(const std::vector<UUID> & ids)
|
||||||
{
|
{
|
||||||
String error_message;
|
applyToMultipleEntities</* ignore_errors = */ false>(
|
||||||
std::optional<int> error_code;
|
ids,
|
||||||
for (const auto & id : ids)
|
[this](const UUID & id) { removeImpl(id); },
|
||||||
{
|
"Couldn't remove {failed_names}. Successfully removed: {succeeded_names}",
|
||||||
try
|
[this](const UUID & id) { return outputTypeAndNameOrID(*this, id); });
|
||||||
{
|
|
||||||
removeImpl(id);
|
|
||||||
}
|
|
||||||
catch (Exception & e)
|
|
||||||
{
|
|
||||||
if (!isNotFoundErrorCode(e.code()))
|
|
||||||
throw;
|
|
||||||
error_message += (error_message.empty() ? "" : ". ") + e.message();
|
|
||||||
if (error_code && (*error_code != e.code()))
|
|
||||||
error_code = ErrorCodes::ACCESS_ENTITY_NOT_FOUND;
|
|
||||||
else
|
|
||||||
error_code = e.code();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!error_message.empty())
|
|
||||||
throw Exception(error_message, *error_code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool IAccessStorage::tryRemove(const UUID & id)
|
bool IAccessStorage::tryRemove(const UUID & id)
|
||||||
{
|
{
|
||||||
try
|
return doTry([&] { removeImpl(id); return true; });
|
||||||
{
|
|
||||||
removeImpl(id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception &)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<UUID> IAccessStorage::tryRemove(const std::vector<UUID> & ids)
|
std::vector<UUID> IAccessStorage::tryRemove(const std::vector<UUID> & ids)
|
||||||
{
|
{
|
||||||
std::vector<UUID> removed;
|
return applyToMultipleEntities</* ignore_errors = */ true>(
|
||||||
removed.reserve(ids.size());
|
ids,
|
||||||
for (const auto & id : ids)
|
[this](const UUID & id) { removeImpl(id); return id; });
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
removeImpl(id);
|
|
||||||
removed.push_back(id);
|
|
||||||
}
|
|
||||||
catch (Exception &)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return removed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -271,60 +316,25 @@ void IAccessStorage::update(const UUID & id, const UpdateFunc & update_func)
|
|||||||
|
|
||||||
void IAccessStorage::update(const std::vector<UUID> & ids, const UpdateFunc & update_func)
|
void IAccessStorage::update(const std::vector<UUID> & ids, const UpdateFunc & update_func)
|
||||||
{
|
{
|
||||||
String error_message;
|
applyToMultipleEntities</* ignore_errors = */ false>(
|
||||||
std::optional<int> error_code;
|
ids,
|
||||||
for (const auto & id : ids)
|
[this, &update_func](const UUID & id) { updateImpl(id, update_func); },
|
||||||
{
|
"Couldn't update {failed_names}. Successfully updated: {succeeded_names}",
|
||||||
try
|
[this](const UUID & id) { return outputTypeAndNameOrID(*this, id); });
|
||||||
{
|
|
||||||
updateImpl(id, update_func);
|
|
||||||
}
|
|
||||||
catch (Exception & e)
|
|
||||||
{
|
|
||||||
if (!isNotFoundErrorCode(e.code()))
|
|
||||||
throw;
|
|
||||||
error_message += (error_message.empty() ? "" : ". ") + e.message();
|
|
||||||
if (error_code && (*error_code != e.code()))
|
|
||||||
error_code = ErrorCodes::ACCESS_ENTITY_NOT_FOUND;
|
|
||||||
else
|
|
||||||
error_code = e.code();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!error_message.empty())
|
|
||||||
throw Exception(error_message, *error_code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool IAccessStorage::tryUpdate(const UUID & id, const UpdateFunc & update_func)
|
bool IAccessStorage::tryUpdate(const UUID & id, const UpdateFunc & update_func)
|
||||||
{
|
{
|
||||||
try
|
return doTry([&] { updateImpl(id, update_func); return true; });
|
||||||
{
|
|
||||||
updateImpl(id, update_func);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception &)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<UUID> IAccessStorage::tryUpdate(const std::vector<UUID> & ids, const UpdateFunc & update_func)
|
std::vector<UUID> IAccessStorage::tryUpdate(const std::vector<UUID> & ids, const UpdateFunc & update_func)
|
||||||
{
|
{
|
||||||
std::vector<UUID> updated;
|
return applyToMultipleEntities</* ignore_errors = */ true>(
|
||||||
updated.reserve(ids.size());
|
ids,
|
||||||
for (const auto & id : ids)
|
[this, &update_func](const UUID & id) { updateImpl(id, update_func); return id; });
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
updateImpl(id, update_func);
|
|
||||||
updated.push_back(id);
|
|
||||||
}
|
|
||||||
catch (Exception &)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return updated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -388,7 +398,7 @@ Poco::Logger * IAccessStorage::getLogger() const
|
|||||||
|
|
||||||
void IAccessStorage::throwNotFound(const UUID & id) const
|
void IAccessStorage::throwNotFound(const UUID & id) const
|
||||||
{
|
{
|
||||||
throw Exception("ID {" + toString(id) + "} not found in [" + getStorageName() + "]", ErrorCodes::ACCESS_ENTITY_NOT_FOUND);
|
throw Exception(outputID(id) + " not found in [" + getStorageName() + "]", ErrorCodes::ACCESS_ENTITY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -402,7 +412,7 @@ void IAccessStorage::throwNotFound(EntityType type, const String & name) const
|
|||||||
void IAccessStorage::throwBadCast(const UUID & id, EntityType type, const String & name, EntityType required_type)
|
void IAccessStorage::throwBadCast(const UUID & id, EntityType type, const String & name, EntityType required_type)
|
||||||
{
|
{
|
||||||
throw Exception(
|
throw Exception(
|
||||||
"ID {" + toString(id) + "}: " + outputEntityTypeAndName(type, name) + " expected to be of type " + toString(required_type),
|
outputID(id) + ": " + outputEntityTypeAndName(type, name) + " expected to be of type " + toString(required_type),
|
||||||
ErrorCodes::LOGICAL_ERROR);
|
ErrorCodes::LOGICAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,7 +420,7 @@ void IAccessStorage::throwBadCast(const UUID & id, EntityType type, const String
|
|||||||
void IAccessStorage::throwIDCollisionCannotInsert(const UUID & id, EntityType type, const String & name, EntityType existing_type, const String & existing_name) const
|
void IAccessStorage::throwIDCollisionCannotInsert(const UUID & id, EntityType type, const String & name, EntityType existing_type, const String & existing_name) const
|
||||||
{
|
{
|
||||||
throw Exception(
|
throw Exception(
|
||||||
outputEntityTypeAndName(type, name) + ": cannot insert because the ID {" + toString(id) + "} is already used by "
|
outputEntityTypeAndName(type, name) + ": cannot insert because the " + outputID(id) + " is already used by "
|
||||||
+ outputEntityTypeAndName(existing_type, existing_name) + " in [" + getStorageName() + "]",
|
+ outputEntityTypeAndName(existing_type, existing_name) + " in [" + getStorageName() + "]",
|
||||||
ErrorCodes::ACCESS_ENTITY_ALREADY_EXISTS);
|
ErrorCodes::ACCESS_ENTITY_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user