2018-08-20 03:34:10 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
#include <sstream>
|
|
|
|
#include <iostream>
|
|
|
|
#include <Core/Types.h>
|
|
|
|
#include <Common/Exception.h>
|
2018-12-27 13:09:11 +00:00
|
|
|
#include <Parsers/Lexer.h>
|
2018-08-20 03:34:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
2021-06-01 05:54:28 +00:00
|
|
|
/// Checks expected server and client error codes in --testmode.
|
|
|
|
///
|
|
|
|
/// The following comment hints are supported:
|
|
|
|
///
|
|
|
|
/// - "-- { serverError 60 }" -- in case of you are expecting server error.
|
|
|
|
///
|
|
|
|
/// - "-- { clientError 20 }" -- in case of you are expecting client error.
|
|
|
|
///
|
|
|
|
/// 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 --testmode -nm
|
|
|
|
///
|
|
|
|
// Here the client parses the query but it is incorrect, so it expects
|
|
|
|
/// SYNTAX_ERROR (62).
|
|
|
|
///
|
|
|
|
/// - echo 'select foo -- { serverError 47 }' | clickhouse-client --testmode -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 }"
|
2018-08-20 03:34:10 +00:00
|
|
|
class TestHint
|
|
|
|
{
|
|
|
|
public:
|
2021-01-18 12:15:46 +00:00
|
|
|
TestHint(bool enabled_, const String & query_) :
|
|
|
|
query(query_)
|
2018-08-20 03:34:10 +00:00
|
|
|
{
|
|
|
|
if (!enabled_)
|
|
|
|
return;
|
|
|
|
|
2021-01-18 12:15:46 +00:00
|
|
|
// Don't parse error hints in leading comments, because it feels weird.
|
|
|
|
// Leading 'echo' hint is OK.
|
|
|
|
bool is_leading_hint = true;
|
|
|
|
|
2018-12-27 13:09:11 +00:00
|
|
|
Lexer lexer(query.data(), query.data() + query.size());
|
2018-08-20 03:34:10 +00:00
|
|
|
|
2018-12-27 13:09:11 +00:00
|
|
|
for (Token token = lexer.nextToken(); !token.isEnd(); token = lexer.nextToken())
|
2018-08-20 03:34:10 +00:00
|
|
|
{
|
2021-01-18 12:15:46 +00:00
|
|
|
if (token.type != TokenType::Comment
|
|
|
|
&& token.type != TokenType::Whitespace)
|
|
|
|
{
|
|
|
|
is_leading_hint = false;
|
|
|
|
}
|
|
|
|
else if (token.type == TokenType::Comment)
|
2018-08-20 03:34:10 +00:00
|
|
|
{
|
2018-12-28 16:45:45 +00:00
|
|
|
String comment(token.begin, token.begin + token.size());
|
|
|
|
|
|
|
|
if (!comment.empty())
|
2018-12-27 13:09:11 +00:00
|
|
|
{
|
2018-12-28 16:45:45 +00:00
|
|
|
size_t pos_start = comment.find('{', 0);
|
|
|
|
if (pos_start != String::npos)
|
|
|
|
{
|
|
|
|
size_t pos_end = comment.find('}', pos_start);
|
|
|
|
if (pos_end != String::npos)
|
|
|
|
{
|
|
|
|
String hint(comment.begin() + pos_start + 1, comment.begin() + pos_end);
|
2021-01-18 12:15:46 +00:00
|
|
|
parse(hint, is_leading_hint);
|
2018-12-28 16:45:45 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-27 13:09:11 +00:00
|
|
|
}
|
2018-08-20 03:34:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int serverError() const { return server_error; }
|
|
|
|
int clientError() const { return client_error; }
|
2021-06-01 05:54:28 +00:00
|
|
|
std::optional<bool> echoQueries() const { return echo; }
|
2018-08-20 03:34:10 +00:00
|
|
|
|
|
|
|
private:
|
2020-07-21 14:05:30 +00:00
|
|
|
const String & query;
|
2018-08-20 03:34:10 +00:00
|
|
|
int server_error = 0;
|
|
|
|
int client_error = 0;
|
2021-06-01 05:54:28 +00:00
|
|
|
std::optional<bool> echo;
|
2018-08-20 03:34:10 +00:00
|
|
|
|
2021-01-18 12:15:46 +00:00
|
|
|
void parse(const String & hint, bool is_leading_hint)
|
2018-08-20 03:34:10 +00:00
|
|
|
{
|
2020-11-10 18:22:26 +00:00
|
|
|
std::stringstream ss; // STYLE_CHECK_ALLOW_STD_STRING_STREAM
|
2019-01-21 16:14:26 +00:00
|
|
|
ss << hint;
|
|
|
|
String item;
|
|
|
|
|
2018-08-20 03:34:10 +00:00
|
|
|
while (!ss.eof())
|
|
|
|
{
|
|
|
|
ss >> item;
|
2019-01-21 16:14:26 +00:00
|
|
|
if (ss.eof())
|
|
|
|
break;
|
2018-08-20 03:34:10 +00:00
|
|
|
|
2021-01-18 12:15:46 +00:00
|
|
|
if (!is_leading_hint)
|
|
|
|
{
|
|
|
|
if (item == "serverError")
|
|
|
|
ss >> server_error;
|
|
|
|
else if (item == "clientError")
|
|
|
|
ss >> client_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item == "echo")
|
2021-06-01 05:54:28 +00:00
|
|
|
echo.emplace(true);
|
|
|
|
if (item == "echoOn")
|
|
|
|
echo.emplace(true);
|
|
|
|
if (item == "echoOff")
|
|
|
|
echo.emplace(false);
|
2018-08-20 03:34:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool allErrorsExpected(int actual_server_error, int actual_client_error) const
|
|
|
|
{
|
|
|
|
return (server_error || client_error) && (server_error == actual_server_error) && (client_error == actual_client_error);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool lostExpectedError(int actual_server_error, int actual_client_error) const
|
|
|
|
{
|
|
|
|
return (server_error && !actual_server_error) || (client_error && !actual_client_error);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|