From 1bbbfc6e1b4b532942e97a6f16b6bf21c014183c Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 2 Jun 2020 06:25:19 +0300 Subject: [PATCH] Fix \G in clickhouse-client multiline mode #9933 --- base/common/LineReader.cpp | 44 ++++++++++++++++++++++-------- base/common/LineReader.h | 11 ++++++-- base/common/ReadlineLineReader.cpp | 5 ++-- base/common/ReadlineLineReader.h | 2 +- base/common/ReplxxLineReader.cpp | 5 ++-- base/common/ReplxxLineReader.h | 2 +- programs/client/Client.cpp | 9 ++++-- 7 files changed, 55 insertions(+), 23 deletions(-) diff --git a/base/common/LineReader.cpp b/base/common/LineReader.cpp index 0d06e5ef225..dd2e09b0393 100644 --- a/base/common/LineReader.cpp +++ b/base/common/LineReader.cpp @@ -67,8 +67,8 @@ LineReader::Suggest::WordsRange LineReader::Suggest::getCompletions(const String }); } -LineReader::LineReader(const String & history_file_path_, char extender_, char delimiter_) - : history_file_path(history_file_path_), extender(extender_), delimiter(delimiter_) +LineReader::LineReader(const String & history_file_path_, bool multiline_, Patterns extenders_, Patterns delimiters_) + : history_file_path(history_file_path_), multiline(multiline_), extenders(std::move(extenders_)), delimiters(std::move(delimiters_)) { /// FIXME: check extender != delimiter } @@ -76,38 +76,60 @@ LineReader::LineReader(const String & history_file_path_, char extender_, char d String LineReader::readLine(const String & first_prompt, const String & second_prompt) { String line; - bool is_multiline = false; + bool need_next_line = false; - while (auto status = readOneLine(is_multiline ? second_prompt : first_prompt)) + while (auto status = readOneLine(need_next_line ? second_prompt : first_prompt)) { if (status == RESET_LINE) { line.clear(); - is_multiline = false; + need_next_line = false; continue; } if (input.empty()) { - if (!line.empty() && !delimiter && !hasInputData()) + if (!line.empty() && !multiline && !hasInputData()) break; else continue; } - is_multiline = (input.back() == extender) || (delimiter && input.back() != delimiter) || hasInputData(); - - if (input.back() == extender) +#if !defined(ARCADIA_BUILD) /// C++20 + const char * has_extender = nullptr; + for (const auto * extender : extenders) { - input = input.substr(0, input.size() - 1); + if (input.ends_with(extender)) + { + has_extender = extender; + break; + } + } + + const char * has_delimiter = nullptr; + for (const auto * delimiter : delimiters) + { + if (input.ends_with(delimiter)) + { + has_delimiter = delimiter; + break; + } + } + + need_next_line = has_extender || (multiline && !has_delimiter) || hasInputData(); + + if (has_extender) + { + input.resize(input.size() - strlen(has_extender)); trim(input); if (input.empty()) continue; } +#endif line += (line.empty() ? "" : " ") + input; - if (!is_multiline) + if (!need_next_line) break; } diff --git a/base/common/LineReader.h b/base/common/LineReader.h index 3e64bc858ad..95066ad0dba 100644 --- a/base/common/LineReader.h +++ b/base/common/LineReader.h @@ -21,7 +21,10 @@ public: WordsRange getCompletions(const String & prefix, size_t prefix_length) const; }; - LineReader(const String & history_file_path, char extender, char delimiter = 0); /// if delimiter != 0, then it's multiline mode + using Patterns = std::vector; + + /// if delimiter is set, then it's multiline mode + LineReader(const String & history_file_path, bool multiline, Patterns extenders, Patterns delimiters); virtual ~LineReader() {} /// Reads the whole line until delimiter (in multiline mode) or until the last line without extender. @@ -51,8 +54,10 @@ protected: String input; private: - const char extender; - const char delimiter; + bool multiline; + + Patterns extenders; + Patterns delimiters; String prev_line; diff --git a/base/common/ReadlineLineReader.cpp b/base/common/ReadlineLineReader.cpp index ee9a37d2168..700051907b9 100644 --- a/base/common/ReadlineLineReader.cpp +++ b/base/common/ReadlineLineReader.cpp @@ -56,8 +56,9 @@ static char * generate(const char * text, int state) return nextMatch(); }; -ReadlineLineReader::ReadlineLineReader(const Suggest & suggest_, const String & history_file_path_, char extender_, char delimiter_) - : LineReader(history_file_path_, extender_, delimiter_) +ReadlineLineReader::ReadlineLineReader( + const Suggest & suggest_, const String & history_file_path_, bool multiline_, Patterns extender_, Patterns delimiter_) + : LineReader(history_file_path_, multiline_, std::move(extender_), std::move(delimiter_)) { suggest = &suggest_; diff --git a/base/common/ReadlineLineReader.h b/base/common/ReadlineLineReader.h index 395ae56c724..4af1d147157 100644 --- a/base/common/ReadlineLineReader.h +++ b/base/common/ReadlineLineReader.h @@ -8,7 +8,7 @@ class ReadlineLineReader : public LineReader { public: - ReadlineLineReader(const Suggest & suggest, const String & history_file_path, char extender, char delimiter = 0); + ReadlineLineReader(const Suggest & suggest, const String & history_file_path, bool multiline, Patterns extender_, Patterns delimiter_); ~ReadlineLineReader() override; void enableBracketedPaste() override; diff --git a/base/common/ReplxxLineReader.cpp b/base/common/ReplxxLineReader.cpp index 52c42235f1b..68e8408f8b7 100644 --- a/base/common/ReplxxLineReader.cpp +++ b/base/common/ReplxxLineReader.cpp @@ -16,8 +16,9 @@ void trim(String & s) } -ReplxxLineReader::ReplxxLineReader(const Suggest & suggest, const String & history_file_path_, char extender_, char delimiter_) - : LineReader(history_file_path_, extender_, delimiter_) +ReplxxLineReader::ReplxxLineReader( + const Suggest & suggest, const String & history_file_path_, bool multiline_, Patterns extender_, Patterns delimiter_) + : LineReader(history_file_path_, multiline_, std::move(extender_), std::move(delimiter_)) { using namespace std::placeholders; using Replxx = replxx::Replxx; diff --git a/base/common/ReplxxLineReader.h b/base/common/ReplxxLineReader.h index e7821f54ad3..a28cad98f72 100644 --- a/base/common/ReplxxLineReader.h +++ b/base/common/ReplxxLineReader.h @@ -7,7 +7,7 @@ class ReplxxLineReader : public LineReader { public: - ReplxxLineReader(const Suggest & suggest, const String & history_file_path, char extender, char delimiter = 0); + ReplxxLineReader(const Suggest & suggest, const String & history_file_path, bool multiline, Patterns extenders_, Patterns delimiter_); ~ReplxxLineReader() override; void enableBracketedPaste() override; diff --git a/programs/client/Client.cpp b/programs/client/Client.cpp index f45daa737fd..1c2e0925c2a 100644 --- a/programs/client/Client.cpp +++ b/programs/client/Client.cpp @@ -498,12 +498,15 @@ private: if (!history_file.empty() && !Poco::File(history_file).exists()) Poco::File(history_file).createFile(); + LineReader::Patterns query_extenders = {"\\"}; + LineReader::Patterns query_delimiters = {";", "\\G"}; + #if USE_REPLXX - ReplxxLineReader lr(Suggest::instance(), history_file, '\\', config().has("multiline") ? ';' : 0); + ReplxxLineReader lr(Suggest::instance(), history_file, config().has("multiline"), query_extenders, query_delimiters); #elif defined(USE_READLINE) && USE_READLINE - ReadlineLineReader lr(Suggest::instance(), history_file, '\\', config().has("multiline") ? ';' : 0); + ReadlineLineReader lr(Suggest::instance(), history_file, config().has("multiline"), query_extenders, query_delimiters); #else - LineReader lr(history_file, '\\', config().has("multiline") ? ';' : 0); + LineReader lr(history_file, config().has("multiline"), query_extenders, query_delimiters); #endif /// Enable bracketed-paste-mode only when multiquery is enabled and multiline is