Fix \G in clickhouse-client multiline mode #9933

This commit is contained in:
Alexey Milovidov 2020-06-02 06:25:19 +03:00
parent 92ac608447
commit 1bbbfc6e1b
7 changed files with 55 additions and 23 deletions

View File

@ -67,8 +67,8 @@ LineReader::Suggest::WordsRange LineReader::Suggest::getCompletions(const String
}); });
} }
LineReader::LineReader(const String & history_file_path_, char extender_, char delimiter_) LineReader::LineReader(const String & history_file_path_, bool multiline_, Patterns extenders_, Patterns delimiters_)
: history_file_path(history_file_path_), extender(extender_), delimiter(delimiter_) : history_file_path(history_file_path_), multiline(multiline_), extenders(std::move(extenders_)), delimiters(std::move(delimiters_))
{ {
/// FIXME: check extender != delimiter /// 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 LineReader::readLine(const String & first_prompt, const String & second_prompt)
{ {
String line; 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) if (status == RESET_LINE)
{ {
line.clear(); line.clear();
is_multiline = false; need_next_line = false;
continue; continue;
} }
if (input.empty()) if (input.empty())
{ {
if (!line.empty() && !delimiter && !hasInputData()) if (!line.empty() && !multiline && !hasInputData())
break; break;
else else
continue; continue;
} }
is_multiline = (input.back() == extender) || (delimiter && input.back() != delimiter) || hasInputData(); #if !defined(ARCADIA_BUILD) /// C++20
const char * has_extender = nullptr;
if (input.back() == extender) 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); trim(input);
if (input.empty()) if (input.empty())
continue; continue;
} }
#endif
line += (line.empty() ? "" : " ") + input; line += (line.empty() ? "" : " ") + input;
if (!is_multiline) if (!need_next_line)
break; break;
} }

View File

@ -21,7 +21,10 @@ public:
WordsRange getCompletions(const String & prefix, size_t prefix_length) const; 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<const char *>;
/// if delimiter is set, then it's multiline mode
LineReader(const String & history_file_path, bool multiline, Patterns extenders, Patterns delimiters);
virtual ~LineReader() {} virtual ~LineReader() {}
/// Reads the whole line until delimiter (in multiline mode) or until the last line without extender. /// Reads the whole line until delimiter (in multiline mode) or until the last line without extender.
@ -51,8 +54,10 @@ protected:
String input; String input;
private: private:
const char extender; bool multiline;
const char delimiter;
Patterns extenders;
Patterns delimiters;
String prev_line; String prev_line;

View File

@ -56,8 +56,9 @@ static char * generate(const char * text, int state)
return nextMatch(); return nextMatch();
}; };
ReadlineLineReader::ReadlineLineReader(const Suggest & suggest_, const String & history_file_path_, char extender_, char delimiter_) ReadlineLineReader::ReadlineLineReader(
: LineReader(history_file_path_, extender_, delimiter_) 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_; suggest = &suggest_;

View File

@ -8,7 +8,7 @@
class ReadlineLineReader : public LineReader class ReadlineLineReader : public LineReader
{ {
public: 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; ~ReadlineLineReader() override;
void enableBracketedPaste() override; void enableBracketedPaste() override;

View File

@ -16,8 +16,9 @@ void trim(String & s)
} }
ReplxxLineReader::ReplxxLineReader(const Suggest & suggest, const String & history_file_path_, char extender_, char delimiter_) ReplxxLineReader::ReplxxLineReader(
: LineReader(history_file_path_, extender_, delimiter_) 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 namespace std::placeholders;
using Replxx = replxx::Replxx; using Replxx = replxx::Replxx;

View File

@ -7,7 +7,7 @@
class ReplxxLineReader : public LineReader class ReplxxLineReader : public LineReader
{ {
public: 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; ~ReplxxLineReader() override;
void enableBracketedPaste() override; void enableBracketedPaste() override;

View File

@ -498,12 +498,15 @@ private:
if (!history_file.empty() && !Poco::File(history_file).exists()) if (!history_file.empty() && !Poco::File(history_file).exists())
Poco::File(history_file).createFile(); Poco::File(history_file).createFile();
LineReader::Patterns query_extenders = {"\\"};
LineReader::Patterns query_delimiters = {";", "\\G"};
#if USE_REPLXX #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 #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 #else
LineReader lr(history_file, '\\', config().has("multiline") ? ';' : 0); LineReader lr(history_file, config().has("multiline"), query_extenders, query_delimiters);
#endif #endif
/// Enable bracketed-paste-mode only when multiquery is enabled and multiline is /// Enable bracketed-paste-mode only when multiquery is enabled and multiline is