ClickHouse/src/Client/TestHint.h
2023-03-06 12:31:54 +01:00

117 lines
3.6 KiB
C++

#pragma once
#include <optional>
#include <vector>
#include <fmt/format.h>
#include <Core/Types.h>
namespace DB
{
/// Checks expected server and client error codes.
///
/// The following comment hints are supported:
///
/// - "-- { serverError 60 }" -- in case of you are expecting server error.
/// - "-- { serverError 16 | 36 }" -- in case of you are expecting one of the 2 errors
///
/// - "-- { clientError 20 }" -- in case of you are expecting client error.
/// - "-- { clientError 20 | 60 | 92 }" -- It's expected that the client will return one of the 3 errors.
///
/// - "-- { serverError FUNCTION_THROW_IF_VALUE_IS_NON_ZERO }" -- by error name.
/// - "-- { serverError NO_SUCH_COLUMN_IN_TABLE | BAD_ARGUMENTS }" -- by error name.
///
/// - "-- { clientError FUNCTION_THROW_IF_VALUE_IS_NON_ZERO }" -- by error name.
///
/// Remember that the client parse the query first (not the server), so for
/// example if you are expecting syntax error, then you should use
/// clientError not serverError.
///
/// Examples:
///
/// - echo 'select / -- { clientError 62 }' | clickhouse-client -nm
///
// Here the client parses the query but it is incorrect, so it expects
/// SYNTAX_ERROR (62).
///
/// - echo 'select foo -- { serverError 47 }' | clickhouse-client -nm
///
/// But here the query is correct, but there is no such column "foo", so it
/// is UNKNOWN_IDENTIFIER server error.
///
/// The following hints will control the query echo mode (i.e print each query):
///
/// - "-- { echo }"
/// - "-- { echoOn }"
/// - "-- { echoOff }"
class TestHint
{
public:
using error_vector = std::vector<int>;
TestHint(const String & query_);
const auto & serverErrors() const { return server_errors; }
const auto & clientErrors() const { return client_errors; }
std::optional<bool> echoQueries() const { return echo; }
private:
const String & query;
error_vector server_errors{};
error_vector client_errors{};
std::optional<bool> echo;
void parse(const String & hint, bool is_leading_hint);
bool allErrorsExpected(int actual_server_error, int actual_client_error) const
{
if (actual_server_error && std::find(server_errors.begin(), server_errors.end(), actual_server_error) == server_errors.end())
return false;
if (!actual_server_error && server_errors.size())
return false;
if (actual_client_error && std::find(client_errors.begin(), client_errors.end(), actual_client_error) == client_errors.end())
return false;
if (!actual_client_error && client_errors.size())
return false;
return true;
}
bool lostExpectedError(int actual_server_error, int actual_client_error) const
{
return (server_errors.size() && !actual_server_error) || (client_errors.size() && !actual_client_error);
}
};
}
template <>
struct fmt::formatter<DB::TestHint::error_vector>
{
static constexpr auto parse(format_parse_context & ctx)
{
const auto * it = ctx.begin();
const auto * end = ctx.end();
/// Only support {}.
if (it != end && *it != '}')
throw format_error("Invalid format");
return it;
}
template <typename FormatContext>
auto format(const DB::TestHint::error_vector & error_vector, FormatContext & ctx)
{
if (error_vector.empty())
return format_to(ctx.out(), "{}", 0);
else if (error_vector.size() == 1)
return format_to(ctx.out(), "{}", error_vector[0]);
else
return format_to(ctx.out(), "One of [{}]", fmt::join(error_vector, ", "));
}
};