mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-10 09:32:06 +00:00
Tiny refactoring
This commit is contained in:
parent
68d3fbe1a4
commit
59b0ce21e4
@ -281,6 +281,26 @@ int Client::childMainImpl()
|
||||
|
||||
if (is_interactive)
|
||||
{
|
||||
/// Load Warnings at the beginning of connection
|
||||
if (!config().has("no-warnings"))
|
||||
{
|
||||
try
|
||||
{
|
||||
std::vector<String> messages = loadWarningMessages();
|
||||
if (!messages.empty())
|
||||
{
|
||||
std::cout << "Warnings:" << std::endl;
|
||||
for (const auto & message : messages)
|
||||
std::cout << " * " << message << std::endl;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
/// Ignore exception
|
||||
}
|
||||
}
|
||||
|
||||
runInteractive();
|
||||
}
|
||||
else
|
||||
@ -1766,9 +1786,6 @@ void Client::processOptions(const OptionsDescription & options_description,
|
||||
if (options.count("config-file") && options.count("config"))
|
||||
throw Exception("Two or more configuration files referenced in arguments", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (config().has("multiquery"))
|
||||
is_multiquery = true;
|
||||
|
||||
query_processing_stage = QueryProcessingStage::fromString(options["stage"].as<std::string>());
|
||||
|
||||
/// Save received data into the internal config.
|
||||
@ -1851,18 +1868,21 @@ void Client::processOptions(const OptionsDescription & options_description,
|
||||
|
||||
if (options.count("opentelemetry-traceparent"))
|
||||
{
|
||||
std::string traceparent = options["opentelemetry-traceparent"].as<std::string>();
|
||||
std::string error;
|
||||
String traceparent = options["opentelemetry-traceparent"].as<std::string>();
|
||||
String error;
|
||||
if (!global_context->getClientInfo().client_trace_context.parseTraceparentHeader(traceparent, error))
|
||||
{
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Cannot parse OpenTelemetry traceparent '{}': {}", traceparent, error);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.count("opentelemetry-tracestate"))
|
||||
{
|
||||
global_context->getClientInfo().client_trace_context.tracestate = options["opentelemetry-tracestate"].as<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Client::processConfig()
|
||||
{
|
||||
if (config().has("multiquery"))
|
||||
is_multiquery = true;
|
||||
|
||||
is_default_format = !config().has("vertical") && !config().has("format");
|
||||
if (config().has("vertical"))
|
||||
@ -1883,26 +1903,6 @@ void Client::processOptions(const OptionsDescription & options_description,
|
||||
ClientInfo & client_info = global_context->getClientInfo();
|
||||
client_info.setInitialQuery();
|
||||
client_info.quota_key = config().getString("quota_key", "");
|
||||
|
||||
/// Load Warnings at the beginning of connection
|
||||
if (!config().has("no-warnings"))
|
||||
{
|
||||
try
|
||||
{
|
||||
std::vector<String> messages = loadWarningMessages();
|
||||
if (!messages.empty())
|
||||
{
|
||||
std::cout << "Warnings:" << std::endl;
|
||||
for (const auto & message : messages)
|
||||
std::cout << " * " << message << std::endl;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
/// Ignore exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <Client/IClient.h>
|
||||
#include <Client/ClientBase.h>
|
||||
#include <Core/ExternalTable.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
class Client : public IClient
|
||||
class Client : public ClientBase
|
||||
{
|
||||
public:
|
||||
Client() = default;
|
||||
@ -57,6 +57,8 @@ protected:
|
||||
const CommandLineOptions & options,
|
||||
const std::vector<Arguments> & external_tables_arguments) override;
|
||||
|
||||
void processConfig() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Connection> connection; /// Connection to DB.
|
||||
|
||||
|
@ -257,7 +257,7 @@ void LocalServer::executeParsedQueryImpl()
|
||||
|
||||
try
|
||||
{
|
||||
executeQuery(read_buf, write_buf, /* allow_into_outfile = */ true, query_context, {}, finalize_progress);
|
||||
executeQuery(read_buf, write_buf, /* allow_into_outfile = */ true, query_context, {}, {}, finalize_progress);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -372,11 +372,6 @@ int LocalServer::childMainImpl()
|
||||
if (config().has("query") && config().has("queries-file"))
|
||||
throw Exception("Specify either `query` or `queries-file` option", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
echo_queries = config().hasOption("echo") || config().hasOption("verbose");
|
||||
|
||||
prompt_by_server_display_name = config().getRawString("prompt_by_server_display_name.default", "{display_name} :) ");
|
||||
server_display_name = config().getString("display_name", getFQDNOrHostName());
|
||||
|
||||
/// Prompt may contain the following substitutions in a form of {name}.
|
||||
std::map<String, String> prompt_substitutions{{"display_name", server_display_name}};
|
||||
|
||||
@ -667,6 +662,14 @@ void LocalServer::processOptions(const OptionsDescription &, const CommandLineOp
|
||||
queries_files = options["queries-file"].as<std::vector<std::string>>();
|
||||
}
|
||||
|
||||
|
||||
void LocalServer::processConfig()
|
||||
{
|
||||
echo_queries = config().hasOption("echo") || config().hasOption("verbose");
|
||||
prompt_by_server_display_name = config().getRawString("prompt_by_server_display_name.default", "{display_name} :) ");
|
||||
server_display_name = config().getString("display_name", getFQDNOrHostName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <loggers/Loggers.h>
|
||||
#include <Poco/Util/Application.h>
|
||||
#include <Common/ProgressIndication.h>
|
||||
#include <Client/IClient.h>
|
||||
#include <Client/ClientBase.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -16,7 +16,7 @@ namespace DB
|
||||
/// Lightweight Application for clickhouse-local
|
||||
/// No networking, no extra configs and working directories, no pid and status files, no dictionaries, no logging.
|
||||
/// Quiet mode by default
|
||||
class LocalServer : public IClient, public Loggers
|
||||
class LocalServer : public ClientBase, public Loggers
|
||||
{
|
||||
public:
|
||||
LocalServer();
|
||||
@ -73,6 +73,7 @@ protected:
|
||||
void processOptions(const OptionsDescription & options_description,
|
||||
const CommandLineOptions & options,
|
||||
const std::vector<Arguments> &) override;
|
||||
void processConfig() override;
|
||||
|
||||
bool supportPasswordOption() const override { return false; }
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <Client/IClient.h>
|
||||
#include <Client/ClientBase.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
@ -21,6 +21,8 @@
|
||||
#include <Common/filesystemHelpers.h>
|
||||
#include <Common/Config/configReadClient.h>
|
||||
|
||||
#include <Client/ClientBaseHelpers.h>
|
||||
|
||||
#include <Parsers/parseQuery.h>
|
||||
#include <Parsers/ParserQuery.h>
|
||||
#include <Parsers/formatAST.h>
|
||||
@ -50,165 +52,7 @@ namespace ErrorCodes
|
||||
}
|
||||
|
||||
|
||||
/// Should we celebrate a bit?
|
||||
bool IClient::isNewYearMode()
|
||||
{
|
||||
time_t current_time = time(nullptr);
|
||||
|
||||
/// It's bad to be intrusive.
|
||||
if (current_time % 3 != 0)
|
||||
return false;
|
||||
|
||||
LocalDate now(current_time);
|
||||
return (now.month() == 12 && now.day() >= 20) || (now.month() == 1 && now.day() <= 5);
|
||||
}
|
||||
|
||||
|
||||
bool IClient::isChineseNewYearMode(const String & local_tz)
|
||||
{
|
||||
/// Days of Dec. 20 in Chinese calendar starting from year 2019 to year 2105
|
||||
static constexpr UInt16 chineseNewYearIndicators[]
|
||||
= {18275, 18659, 19014, 19368, 19752, 20107, 20491, 20845, 21199, 21583, 21937, 22292, 22676, 23030, 23414, 23768, 24122, 24506,
|
||||
24860, 25215, 25599, 25954, 26308, 26692, 27046, 27430, 27784, 28138, 28522, 28877, 29232, 29616, 29970, 30354, 30708, 31062,
|
||||
31446, 31800, 32155, 32539, 32894, 33248, 33632, 33986, 34369, 34724, 35078, 35462, 35817, 36171, 36555, 36909, 37293, 37647,
|
||||
38002, 38386, 38740, 39095, 39479, 39833, 40187, 40571, 40925, 41309, 41664, 42018, 42402, 42757, 43111, 43495, 43849, 44233,
|
||||
44587, 44942, 45326, 45680, 46035, 46418, 46772, 47126, 47510, 47865, 48249, 48604, 48958, 49342};
|
||||
|
||||
/// All time zone names are acquired from https://www.iana.org/time-zones
|
||||
static constexpr const char * chineseNewYearTimeZoneIndicators[] = {
|
||||
/// Time zones celebrating Chinese new year.
|
||||
"Asia/Shanghai",
|
||||
"Asia/Chongqing",
|
||||
"Asia/Harbin",
|
||||
"Asia/Urumqi",
|
||||
"Asia/Hong_Kong",
|
||||
"Asia/Chungking",
|
||||
"Asia/Macao",
|
||||
"Asia/Macau",
|
||||
"Asia/Taipei",
|
||||
"Asia/Singapore",
|
||||
|
||||
/// Time zones celebrating Chinese new year but with different festival names. Let's not print the message for now.
|
||||
// "Asia/Brunei",
|
||||
// "Asia/Ho_Chi_Minh",
|
||||
// "Asia/Hovd",
|
||||
// "Asia/Jakarta",
|
||||
// "Asia/Jayapura",
|
||||
// "Asia/Kashgar",
|
||||
// "Asia/Kuala_Lumpur",
|
||||
// "Asia/Kuching",
|
||||
// "Asia/Makassar",
|
||||
// "Asia/Pontianak",
|
||||
// "Asia/Pyongyang",
|
||||
// "Asia/Saigon",
|
||||
// "Asia/Seoul",
|
||||
// "Asia/Ujung_Pandang",
|
||||
// "Asia/Ulaanbaatar",
|
||||
// "Asia/Ulan_Bator",
|
||||
};
|
||||
static constexpr size_t M = sizeof(chineseNewYearTimeZoneIndicators) / sizeof(chineseNewYearTimeZoneIndicators[0]);
|
||||
|
||||
time_t current_time = time(nullptr);
|
||||
|
||||
if (chineseNewYearTimeZoneIndicators + M
|
||||
== std::find_if(chineseNewYearTimeZoneIndicators, chineseNewYearTimeZoneIndicators + M, [&local_tz](const char * tz)
|
||||
{
|
||||
return tz == local_tz;
|
||||
}))
|
||||
return false;
|
||||
|
||||
/// It's bad to be intrusive.
|
||||
if (current_time % 3 != 0)
|
||||
return false;
|
||||
|
||||
auto days = DateLUT::instance().toDayNum(current_time).toUnderType();
|
||||
for (auto d : chineseNewYearIndicators)
|
||||
{
|
||||
/// Let's celebrate until Lantern Festival
|
||||
if (d <= days && d + 25 >= days)
|
||||
return true;
|
||||
else if (d > days)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#if USE_REPLXX
|
||||
void IClient::highlight(const String & query, std::vector<replxx::Replxx::Color> & colors)
|
||||
{
|
||||
using namespace replxx;
|
||||
|
||||
static const std::unordered_map<TokenType, Replxx::Color> token_to_color
|
||||
= {{TokenType::Whitespace, Replxx::Color::DEFAULT},
|
||||
{TokenType::Comment, Replxx::Color::GRAY},
|
||||
{TokenType::BareWord, Replxx::Color::DEFAULT},
|
||||
{TokenType::Number, Replxx::Color::GREEN},
|
||||
{TokenType::StringLiteral, Replxx::Color::CYAN},
|
||||
{TokenType::QuotedIdentifier, Replxx::Color::MAGENTA},
|
||||
{TokenType::OpeningRoundBracket, Replxx::Color::BROWN},
|
||||
{TokenType::ClosingRoundBracket, Replxx::Color::BROWN},
|
||||
{TokenType::OpeningSquareBracket, Replxx::Color::BROWN},
|
||||
{TokenType::ClosingSquareBracket, Replxx::Color::BROWN},
|
||||
{TokenType::DoubleColon, Replxx::Color::BROWN},
|
||||
{TokenType::OpeningCurlyBrace, Replxx::Color::INTENSE},
|
||||
{TokenType::ClosingCurlyBrace, Replxx::Color::INTENSE},
|
||||
|
||||
{TokenType::Comma, Replxx::Color::INTENSE},
|
||||
{TokenType::Semicolon, Replxx::Color::INTENSE},
|
||||
{TokenType::Dot, Replxx::Color::INTENSE},
|
||||
{TokenType::Asterisk, Replxx::Color::INTENSE},
|
||||
{TokenType::Plus, Replxx::Color::INTENSE},
|
||||
{TokenType::Minus, Replxx::Color::INTENSE},
|
||||
{TokenType::Slash, Replxx::Color::INTENSE},
|
||||
{TokenType::Percent, Replxx::Color::INTENSE},
|
||||
{TokenType::Arrow, Replxx::Color::INTENSE},
|
||||
{TokenType::QuestionMark, Replxx::Color::INTENSE},
|
||||
{TokenType::Colon, Replxx::Color::INTENSE},
|
||||
{TokenType::Equals, Replxx::Color::INTENSE},
|
||||
{TokenType::NotEquals, Replxx::Color::INTENSE},
|
||||
{TokenType::Less, Replxx::Color::INTENSE},
|
||||
{TokenType::Greater, Replxx::Color::INTENSE},
|
||||
{TokenType::LessOrEquals, Replxx::Color::INTENSE},
|
||||
{TokenType::GreaterOrEquals, Replxx::Color::INTENSE},
|
||||
{TokenType::Concatenation, Replxx::Color::INTENSE},
|
||||
{TokenType::At, Replxx::Color::INTENSE},
|
||||
{TokenType::DoubleAt, Replxx::Color::MAGENTA},
|
||||
|
||||
{TokenType::EndOfStream, Replxx::Color::DEFAULT},
|
||||
|
||||
{TokenType::Error, Replxx::Color::RED},
|
||||
{TokenType::ErrorMultilineCommentIsNotClosed, Replxx::Color::RED},
|
||||
{TokenType::ErrorSingleQuoteIsNotClosed, Replxx::Color::RED},
|
||||
{TokenType::ErrorDoubleQuoteIsNotClosed, Replxx::Color::RED},
|
||||
{TokenType::ErrorSinglePipeMark, Replxx::Color::RED},
|
||||
{TokenType::ErrorWrongNumber, Replxx::Color::RED},
|
||||
{ TokenType::ErrorMaxQuerySizeExceeded,
|
||||
Replxx::Color::RED }};
|
||||
|
||||
const Replxx::Color unknown_token_color = Replxx::Color::RED;
|
||||
|
||||
Lexer lexer(query.data(), query.data() + query.size());
|
||||
size_t pos = 0;
|
||||
|
||||
for (Token token = lexer.nextToken(); !token.isEnd(); token = lexer.nextToken())
|
||||
{
|
||||
size_t utf8_len = UTF8::countCodePoints(reinterpret_cast<const UInt8 *>(token.begin), token.size());
|
||||
for (size_t code_point_index = 0; code_point_index < utf8_len; ++code_point_index)
|
||||
{
|
||||
if (token_to_color.find(token.type) != token_to_color.end())
|
||||
colors[pos + code_point_index] = token_to_color.at(token.type);
|
||||
else
|
||||
colors[pos + code_point_index] = unknown_token_color;
|
||||
}
|
||||
|
||||
pos += utf8_len;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
ASTPtr IClient::parseQuery(const char *& pos, const char * end, bool allow_multi_statements) const
|
||||
ASTPtr ClientBase::parseQuery(const char *& pos, const char * end, bool allow_multi_statements) const
|
||||
{
|
||||
ParserQuery parser(end);
|
||||
ASTPtr res;
|
||||
@ -295,7 +139,7 @@ static void adjustQueryEnd(const char *& this_query_end, const char * all_querie
|
||||
|
||||
|
||||
/// Flush all buffers.
|
||||
void IClient::resetOutput()
|
||||
void ClientBase::resetOutput()
|
||||
{
|
||||
block_out_stream.reset();
|
||||
logs_out_stream.reset();
|
||||
@ -323,7 +167,7 @@ void IClient::resetOutput()
|
||||
}
|
||||
|
||||
|
||||
void IClient::outputQueryInfo(bool echo_query_)
|
||||
void ClientBase::outputQueryInfo(bool echo_query_)
|
||||
{
|
||||
if (echo_query_)
|
||||
{
|
||||
@ -347,7 +191,7 @@ void IClient::outputQueryInfo(bool echo_query_)
|
||||
}
|
||||
|
||||
|
||||
void IClient::prepareAndExecuteQuery(const String & query)
|
||||
void ClientBase::prepareAndExecuteQuery(const String & query)
|
||||
{
|
||||
/* Parameters are in global variables:
|
||||
* 'parsed_query' -- the query AST,
|
||||
@ -363,7 +207,7 @@ void IClient::prepareAndExecuteQuery(const String & query)
|
||||
}
|
||||
|
||||
|
||||
void IClient::executeParsedQuery(std::optional<bool> echo_query_, bool report_error)
|
||||
void ClientBase::executeParsedQuery(std::optional<bool> echo_query_, bool report_error)
|
||||
{
|
||||
have_error = false;
|
||||
processed_rows = 0;
|
||||
@ -392,7 +236,7 @@ void IClient::executeParsedQuery(std::optional<bool> echo_query_, bool report_er
|
||||
}
|
||||
|
||||
|
||||
bool IClient::processMultiQuery(const String & all_queries_text)
|
||||
bool ClientBase::processMultiQuery(const String & all_queries_text)
|
||||
{
|
||||
// It makes sense not to base any control flow on this, so that it is
|
||||
// the same in tests and in normal usage. The only difference is that in
|
||||
@ -612,7 +456,7 @@ bool IClient::processMultiQuery(const String & all_queries_text)
|
||||
}
|
||||
|
||||
|
||||
bool IClient::processQueryText(const String & text)
|
||||
bool ClientBase::processQueryText(const String & text)
|
||||
{
|
||||
if (exit_strings.end() != exit_strings.find(trim(text, [](char c) { return isWhitespaceASCII(c) || c == ';'; })))
|
||||
return false;
|
||||
@ -635,7 +479,7 @@ bool IClient::processQueryText(const String & text)
|
||||
}
|
||||
|
||||
|
||||
void IClient::runInteractive()
|
||||
void ClientBase::runInteractive()
|
||||
{
|
||||
if (config().has("query_id"))
|
||||
throw Exception("query_id could be specified only in non-interactive mode", ErrorCodes::BAD_ARGUMENTS);
|
||||
@ -742,7 +586,7 @@ void IClient::runInteractive()
|
||||
}
|
||||
|
||||
|
||||
void IClient::runNonInteractive()
|
||||
void ClientBase::runNonInteractive()
|
||||
{
|
||||
String text;
|
||||
|
||||
@ -781,7 +625,7 @@ void IClient::runNonInteractive()
|
||||
}
|
||||
|
||||
|
||||
void IClient::clearTerminal()
|
||||
void ClientBase::clearTerminal()
|
||||
{
|
||||
/// Clear from cursor until end of screen.
|
||||
/// It is needed if garbage is left in terminal.
|
||||
@ -798,7 +642,7 @@ static void showClientVersion()
|
||||
}
|
||||
|
||||
|
||||
int IClient::mainImpl()
|
||||
int ClientBase::mainImpl()
|
||||
{
|
||||
if (isInteractive())
|
||||
is_interactive = true;
|
||||
@ -806,6 +650,8 @@ int IClient::mainImpl()
|
||||
if (config().has("query") && !queries_files.empty())
|
||||
throw Exception("Specify either `query` or `queries-file` option", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
processConfig();
|
||||
|
||||
std::cout << std::fixed << std::setprecision(3);
|
||||
std::cerr << std::fixed << std::setprecision(3);
|
||||
|
||||
@ -825,14 +671,14 @@ int IClient::mainImpl()
|
||||
}
|
||||
|
||||
|
||||
void IClient::initialize(Poco::Util::Application & self)
|
||||
void ClientBase::initialize(Poco::Util::Application & self)
|
||||
{
|
||||
Poco::Util::Application::initialize(self);
|
||||
initializeChild();
|
||||
}
|
||||
|
||||
|
||||
int IClient::main(const std::vector<std::string> & /*args*/)
|
||||
int ClientBase::main(const std::vector<std::string> & /*args*/)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -853,7 +699,7 @@ int IClient::main(const std::vector<std::string> & /*args*/)
|
||||
}
|
||||
|
||||
|
||||
void IClient::init(int argc, char ** argv)
|
||||
void ClientBase::init(int argc, char ** argv)
|
||||
{
|
||||
namespace po = boost::program_options;
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <Client/TestHint.h>
|
||||
#include <Common/ShellCommand.h>
|
||||
|
||||
|
||||
#if USE_REPLXX
|
||||
# include <common/ReplxxLineReader.h>
|
||||
#elif defined(USE_READLINE) && USE_READLINE
|
||||
@ -24,11 +23,11 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
class IClient : public Poco::Util::Application
|
||||
class ClientBase : public Poco::Util::Application
|
||||
{
|
||||
|
||||
public:
|
||||
using Arguments = std::vector<std::string>;
|
||||
using Arguments = std::vector<String>;
|
||||
|
||||
int main(const std::vector<String> & /*args*/) override;
|
||||
|
||||
@ -39,106 +38,10 @@ public:
|
||||
void init(int argc, char ** argv);
|
||||
|
||||
protected:
|
||||
NameSet exit_strings{"exit", "quit", "logout", "учше", "йгше", "дщпщге", "exit;", "quit;", "logout;", "учшеж",
|
||||
"йгшеж", "дщпщгеж", "q", "й", "\\q", "\\Q", "\\й", "\\Й", ":q", "Жй"};
|
||||
|
||||
bool is_interactive = false; /// Use either interactive line editing interface or batch mode.
|
||||
bool is_multiquery = false;
|
||||
|
||||
bool need_render_progress = true;
|
||||
bool written_first_block = false;
|
||||
ProgressIndication progress_indication;
|
||||
|
||||
bool echo_queries = false; /// Print queries before execution in batch mode.
|
||||
bool ignore_error = false; /// In case of errors, don't print error message, continue to next query. Only applicable for non-interactive mode.
|
||||
bool print_time_to_stderr = false; /// Output execution time to stderr in batch mode.
|
||||
|
||||
bool stdin_is_a_tty = false; /// stdin is a terminal.
|
||||
bool stdout_is_a_tty = false; /// stdout is a terminal.
|
||||
|
||||
uint64_t terminal_width = 0;
|
||||
|
||||
/// Settings specified via command line args
|
||||
Settings cmd_settings;
|
||||
|
||||
SharedContextHolder shared_context;
|
||||
ContextMutablePtr global_context;
|
||||
|
||||
QueryFuzzer fuzzer;
|
||||
int query_fuzzer_runs = 0;
|
||||
|
||||
/// If not empty, queries will be read from these files
|
||||
std::vector<String> queries_files;
|
||||
|
||||
std::optional<Suggest> suggest;
|
||||
|
||||
/// Path to a file containing command history.
|
||||
String history_file;
|
||||
|
||||
/// If not empty, run queries from these files before processing every file from 'queries_files'.
|
||||
std::vector<std::string> interleave_queries_files;
|
||||
|
||||
String home_path;
|
||||
|
||||
bool has_vertical_output_suffix = false; /// Is \G present at the end of the query string?
|
||||
|
||||
String prompt_by_server_display_name;
|
||||
String server_display_name;
|
||||
|
||||
/// Current query as it was given to the client.
|
||||
String full_query;
|
||||
/// Parsed query. Is used to determine some settings (e.g. format, output file).
|
||||
ASTPtr parsed_query;
|
||||
// Current query as it will be executed either on server on in clickhouse-local.
|
||||
// It may differ from the full query for INSERT queries, for which the data that follows
|
||||
// the query is stripped and sent separately.
|
||||
String query_to_execute;
|
||||
|
||||
/// If the last query resulted in exception. `server_exception` or
|
||||
/// `client_exception` must be set.
|
||||
bool have_error = false;
|
||||
/// The last exception that was received from the server. Is used for the
|
||||
/// return code in batch mode.
|
||||
std::unique_ptr<Exception> server_exception;
|
||||
/// Likewise, the last exception that occurred on the client.
|
||||
std::unique_ptr<Exception> client_exception;
|
||||
|
||||
/// Buffer that reads from stdin in batch mode.
|
||||
ReadBufferFromFileDescriptor std_in{STDIN_FILENO};
|
||||
/// Console output.
|
||||
WriteBufferFromFileDescriptor std_out{STDOUT_FILENO};
|
||||
std::unique_ptr<ShellCommand> pager_cmd;
|
||||
/// The user can specify to redirect query output to a file.
|
||||
std::optional<WriteBufferFromFile> out_file_buf;
|
||||
BlockOutputStreamPtr block_out_stream;
|
||||
/// The user could specify special file for server logs (stderr by default)
|
||||
std::unique_ptr<WriteBuffer> out_logs_buf;
|
||||
String server_logs_file;
|
||||
BlockOutputStreamPtr logs_out_stream;
|
||||
|
||||
/// We will format query_id in interactive mode in various ways, the default is just to print Query id: ...
|
||||
std::vector<std::pair<String, String>> query_id_formats;
|
||||
QueryProcessingStage::Enum query_processing_stage;
|
||||
|
||||
/// How many rows have been read or written.
|
||||
size_t processed_rows = 0;
|
||||
|
||||
String current_profile;
|
||||
|
||||
static bool isNewYearMode();
|
||||
|
||||
static bool isChineseNewYearMode(const String & local_tz);
|
||||
|
||||
#if USE_REPLXX
|
||||
static void highlight(const String & query, std::vector<replxx::Replxx::Color> & colors);
|
||||
#endif
|
||||
|
||||
bool processMultiQuery(const String & all_queries_text);
|
||||
|
||||
bool processQueryText(const String & text);
|
||||
|
||||
static void clearTerminal();
|
||||
|
||||
void runInteractive();
|
||||
|
||||
void runNonInteractive();
|
||||
@ -194,6 +97,7 @@ protected:
|
||||
virtual void processOptions(const OptionsDescription & options_description,
|
||||
const CommandLineOptions & options,
|
||||
const std::vector<Arguments> & external_tables_arguments) = 0;
|
||||
virtual void processConfig() = 0;
|
||||
|
||||
virtual bool supportPasswordOption() const = 0;
|
||||
|
||||
@ -205,6 +109,7 @@ protected:
|
||||
virtual bool processFile(const String & file) = 0;
|
||||
|
||||
private:
|
||||
static void clearTerminal();
|
||||
|
||||
inline String prompt() const
|
||||
{
|
||||
@ -212,6 +117,86 @@ private:
|
||||
}
|
||||
|
||||
void outputQueryInfo(bool echo_query_);
|
||||
|
||||
|
||||
protected:
|
||||
bool is_interactive = false; /// Use either interactive line editing interface or batch mode.
|
||||
bool is_multiquery = false;
|
||||
|
||||
bool echo_queries = false; /// Print queries before execution in batch mode.
|
||||
bool ignore_error = false; /// In case of errors, don't print error message, continue to next query. Only applicable for non-interactive mode.
|
||||
bool print_time_to_stderr = false; /// Output execution time to stderr in batch mode.
|
||||
|
||||
String home_path;
|
||||
std::vector<String> queries_files; /// If not empty, queries will be read from these files
|
||||
String history_file; /// Path to a file containing command history.
|
||||
std::vector<String> interleave_queries_files; /// If not empty, run queries from these files before processing every file from 'queries_files'.
|
||||
|
||||
bool has_vertical_output_suffix = false; /// Is \G present at the end of the query string?
|
||||
String prompt_by_server_display_name;
|
||||
String server_display_name;
|
||||
|
||||
ProgressIndication progress_indication;
|
||||
bool need_render_progress = true;
|
||||
bool written_first_block = false;
|
||||
size_t processed_rows = 0; /// How many rows have been read or written.
|
||||
|
||||
bool stdin_is_a_tty = false; /// stdin is a terminal.
|
||||
bool stdout_is_a_tty = false; /// stdout is a terminal.
|
||||
uint64_t terminal_width = 0;
|
||||
|
||||
/// Settings specified via command line args
|
||||
Settings cmd_settings;
|
||||
|
||||
SharedContextHolder shared_context;
|
||||
ContextMutablePtr global_context;
|
||||
|
||||
QueryFuzzer fuzzer;
|
||||
int query_fuzzer_runs = 0;
|
||||
|
||||
std::optional<Suggest> suggest;
|
||||
|
||||
/// Current query as it was given to the client.
|
||||
String full_query;
|
||||
/// Parsed query. Is used to determine some settings (e.g. format, output file).
|
||||
ASTPtr parsed_query;
|
||||
// Current query as it will be executed either on server on in clickhouse-local.
|
||||
// It may differ from the full query for INSERT queries, for which the data that follows
|
||||
// the query is stripped and sent separately.
|
||||
String query_to_execute;
|
||||
|
||||
/// If the last query resulted in exception. `server_exception` or
|
||||
/// `client_exception` must be set.
|
||||
bool have_error = false;
|
||||
/// The last exception that was received from the server. Is used for the
|
||||
/// return code in batch mode.
|
||||
std::unique_ptr<Exception> server_exception;
|
||||
/// Likewise, the last exception that occurred on the client.
|
||||
std::unique_ptr<Exception> client_exception;
|
||||
|
||||
/// Buffer that reads from stdin in batch mode.
|
||||
ReadBufferFromFileDescriptor std_in{STDIN_FILENO};
|
||||
/// Console output.
|
||||
WriteBufferFromFileDescriptor std_out{STDOUT_FILENO};
|
||||
std::unique_ptr<ShellCommand> pager_cmd;
|
||||
/// The user can specify to redirect query output to a file.
|
||||
std::optional<WriteBufferFromFile> out_file_buf;
|
||||
BlockOutputStreamPtr block_out_stream;
|
||||
/// The user could specify special file for server logs (stderr by default)
|
||||
std::unique_ptr<WriteBuffer> out_logs_buf;
|
||||
String server_logs_file;
|
||||
BlockOutputStreamPtr logs_out_stream;
|
||||
|
||||
/// We will format query_id in interactive mode in various ways, the default is just to print Query id: ...
|
||||
std::vector<std::pair<String, String>> query_id_formats;
|
||||
QueryProcessingStage::Enum query_processing_stage;
|
||||
|
||||
String current_profile;
|
||||
|
||||
private:
|
||||
NameSet exit_strings{"exit", "quit", "logout", "учше", "йгше", "дщпщге", "exit;", "quit;", "logout;", "учшеж",
|
||||
"йгшеж", "дщпщгеж", "q", "й", "\\q", "\\Q", "\\й", "\\Й", ":q", "Жй"};
|
||||
|
||||
};
|
||||
|
||||
}
|
172
src/Client/ClientBaseHelpers.h
Normal file
172
src/Client/ClientBaseHelpers.h
Normal file
@ -0,0 +1,172 @@
|
||||
#include <Core/Types.h>
|
||||
#include <common/DateLUT.h>
|
||||
#include <common/LocalDate.h>
|
||||
#include <Parsers/Lexer.h>
|
||||
#include <Common/UTF8Helpers.h>
|
||||
|
||||
#if USE_REPLXX
|
||||
# include <common/ReplxxLineReader.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/// Should we celebrate a bit?
|
||||
bool isNewYearMode()
|
||||
{
|
||||
time_t current_time = time(nullptr);
|
||||
|
||||
/// It's bad to be intrusive.
|
||||
if (current_time % 3 != 0)
|
||||
return false;
|
||||
|
||||
LocalDate now(current_time);
|
||||
return (now.month() == 12 && now.day() >= 20) || (now.month() == 1 && now.day() <= 5);
|
||||
}
|
||||
|
||||
|
||||
bool isChineseNewYearMode(const String & local_tz)
|
||||
{
|
||||
/// Days of Dec. 20 in Chinese calendar starting from year 2019 to year 2105
|
||||
static constexpr UInt16 chineseNewYearIndicators[]
|
||||
= {18275, 18659, 19014, 19368, 19752, 20107, 20491, 20845, 21199, 21583, 21937, 22292, 22676, 23030, 23414, 23768, 24122, 24506,
|
||||
24860, 25215, 25599, 25954, 26308, 26692, 27046, 27430, 27784, 28138, 28522, 28877, 29232, 29616, 29970, 30354, 30708, 31062,
|
||||
31446, 31800, 32155, 32539, 32894, 33248, 33632, 33986, 34369, 34724, 35078, 35462, 35817, 36171, 36555, 36909, 37293, 37647,
|
||||
38002, 38386, 38740, 39095, 39479, 39833, 40187, 40571, 40925, 41309, 41664, 42018, 42402, 42757, 43111, 43495, 43849, 44233,
|
||||
44587, 44942, 45326, 45680, 46035, 46418, 46772, 47126, 47510, 47865, 48249, 48604, 48958, 49342};
|
||||
|
||||
/// All time zone names are acquired from https://www.iana.org/time-zones
|
||||
static constexpr const char * chineseNewYearTimeZoneIndicators[] = {
|
||||
/// Time zones celebrating Chinese new year.
|
||||
"Asia/Shanghai",
|
||||
"Asia/Chongqing",
|
||||
"Asia/Harbin",
|
||||
"Asia/Urumqi",
|
||||
"Asia/Hong_Kong",
|
||||
"Asia/Chungking",
|
||||
"Asia/Macao",
|
||||
"Asia/Macau",
|
||||
"Asia/Taipei",
|
||||
"Asia/Singapore",
|
||||
|
||||
/// Time zones celebrating Chinese new year but with different festival names. Let's not print the message for now.
|
||||
// "Asia/Brunei",
|
||||
// "Asia/Ho_Chi_Minh",
|
||||
// "Asia/Hovd",
|
||||
// "Asia/Jakarta",
|
||||
// "Asia/Jayapura",
|
||||
// "Asia/Kashgar",
|
||||
// "Asia/Kuala_Lumpur",
|
||||
// "Asia/Kuching",
|
||||
// "Asia/Makassar",
|
||||
// "Asia/Pontianak",
|
||||
// "Asia/Pyongyang",
|
||||
// "Asia/Saigon",
|
||||
// "Asia/Seoul",
|
||||
// "Asia/Ujung_Pandang",
|
||||
// "Asia/Ulaanbaatar",
|
||||
// "Asia/Ulan_Bator",
|
||||
};
|
||||
static constexpr size_t M = sizeof(chineseNewYearTimeZoneIndicators) / sizeof(chineseNewYearTimeZoneIndicators[0]);
|
||||
|
||||
time_t current_time = time(nullptr);
|
||||
|
||||
if (chineseNewYearTimeZoneIndicators + M
|
||||
== std::find_if(chineseNewYearTimeZoneIndicators, chineseNewYearTimeZoneIndicators + M, [&local_tz](const char * tz)
|
||||
{
|
||||
return tz == local_tz;
|
||||
}))
|
||||
return false;
|
||||
|
||||
/// It's bad to be intrusive.
|
||||
if (current_time % 3 != 0)
|
||||
return false;
|
||||
|
||||
auto days = DateLUT::instance().toDayNum(current_time).toUnderType();
|
||||
for (auto d : chineseNewYearIndicators)
|
||||
{
|
||||
/// Let's celebrate until Lantern Festival
|
||||
if (d <= days && d + 25 >= days)
|
||||
return true;
|
||||
else if (d > days)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#if USE_REPLXX
|
||||
static void highlight(const String & query, std::vector<replxx::Replxx::Color> & colors)
|
||||
{
|
||||
using namespace replxx;
|
||||
|
||||
static const std::unordered_map<TokenType, Replxx::Color> token_to_color
|
||||
= {{TokenType::Whitespace, Replxx::Color::DEFAULT},
|
||||
{TokenType::Comment, Replxx::Color::GRAY},
|
||||
{TokenType::BareWord, Replxx::Color::DEFAULT},
|
||||
{TokenType::Number, Replxx::Color::GREEN},
|
||||
{TokenType::StringLiteral, Replxx::Color::CYAN},
|
||||
{TokenType::QuotedIdentifier, Replxx::Color::MAGENTA},
|
||||
{TokenType::OpeningRoundBracket, Replxx::Color::BROWN},
|
||||
{TokenType::ClosingRoundBracket, Replxx::Color::BROWN},
|
||||
{TokenType::OpeningSquareBracket, Replxx::Color::BROWN},
|
||||
{TokenType::ClosingSquareBracket, Replxx::Color::BROWN},
|
||||
{TokenType::DoubleColon, Replxx::Color::BROWN},
|
||||
{TokenType::OpeningCurlyBrace, Replxx::Color::INTENSE},
|
||||
{TokenType::ClosingCurlyBrace, Replxx::Color::INTENSE},
|
||||
|
||||
{TokenType::Comma, Replxx::Color::INTENSE},
|
||||
{TokenType::Semicolon, Replxx::Color::INTENSE},
|
||||
{TokenType::Dot, Replxx::Color::INTENSE},
|
||||
{TokenType::Asterisk, Replxx::Color::INTENSE},
|
||||
{TokenType::Plus, Replxx::Color::INTENSE},
|
||||
{TokenType::Minus, Replxx::Color::INTENSE},
|
||||
{TokenType::Slash, Replxx::Color::INTENSE},
|
||||
{TokenType::Percent, Replxx::Color::INTENSE},
|
||||
{TokenType::Arrow, Replxx::Color::INTENSE},
|
||||
{TokenType::QuestionMark, Replxx::Color::INTENSE},
|
||||
{TokenType::Colon, Replxx::Color::INTENSE},
|
||||
{TokenType::Equals, Replxx::Color::INTENSE},
|
||||
{TokenType::NotEquals, Replxx::Color::INTENSE},
|
||||
{TokenType::Less, Replxx::Color::INTENSE},
|
||||
{TokenType::Greater, Replxx::Color::INTENSE},
|
||||
{TokenType::LessOrEquals, Replxx::Color::INTENSE},
|
||||
{TokenType::GreaterOrEquals, Replxx::Color::INTENSE},
|
||||
{TokenType::Concatenation, Replxx::Color::INTENSE},
|
||||
{TokenType::At, Replxx::Color::INTENSE},
|
||||
{TokenType::DoubleAt, Replxx::Color::MAGENTA},
|
||||
|
||||
{TokenType::EndOfStream, Replxx::Color::DEFAULT},
|
||||
|
||||
{TokenType::Error, Replxx::Color::RED},
|
||||
{TokenType::ErrorMultilineCommentIsNotClosed, Replxx::Color::RED},
|
||||
{TokenType::ErrorSingleQuoteIsNotClosed, Replxx::Color::RED},
|
||||
{TokenType::ErrorDoubleQuoteIsNotClosed, Replxx::Color::RED},
|
||||
{TokenType::ErrorSinglePipeMark, Replxx::Color::RED},
|
||||
{TokenType::ErrorWrongNumber, Replxx::Color::RED},
|
||||
{ TokenType::ErrorMaxQuerySizeExceeded,
|
||||
Replxx::Color::RED }};
|
||||
|
||||
const Replxx::Color unknown_token_color = Replxx::Color::RED;
|
||||
|
||||
Lexer lexer(query.data(), query.data() + query.size());
|
||||
size_t pos = 0;
|
||||
|
||||
for (Token token = lexer.nextToken(); !token.isEnd(); token = lexer.nextToken())
|
||||
{
|
||||
size_t utf8_len = UTF8::countCodePoints(reinterpret_cast<const UInt8 *>(token.begin), token.size());
|
||||
for (size_t code_point_index = 0; code_point_index < utf8_len; ++code_point_index)
|
||||
{
|
||||
if (token_to_color.find(token.type) != token_to_color.end())
|
||||
colors[pos + code_point_index] = token_to_color.at(token.type);
|
||||
else
|
||||
colors[pos + code_point_index] = unknown_token_color;
|
||||
}
|
||||
|
||||
pos += utf8_len;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user