#pragma once #include #include #include #include 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; TestHint(const String & query_); const auto & serverErrors() const { return server_errors; } const auto & clientErrors() const { return client_errors; } std::optional echoQueries() const { return echo; } private: const String & query; error_vector server_errors{}; error_vector client_errors{}; std::optional 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 { 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 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, ", ")); } };