From 047e5e30df799c43b98f1746b0ae0251293d8a99 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 15 Aug 2014 04:08:15 +0400 Subject: [PATCH] dbms: Client: don't leave progress indicator in the middle of data; colors almost compatible with white backgrounds [#METR-11125]. --- dbms/src/Client/Client.cpp | 93 +++++++++++-------- .../DataStreams/PrettyBlockOutputStream.cpp | 2 +- .../PrettyCompactBlockOutputStream.cpp | 14 +-- .../PrettySpaceBlockOutputStream.cpp | 10 +- dbms/src/Parsers/formatAST.cpp | 2 +- 5 files changed, 66 insertions(+), 55 deletions(-) diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index f5fd7729980..70ccdd02f7f 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -49,6 +49,16 @@ #include + +/// http://en.wikipedia.org/wiki/ANSI_escape_code +#define SAVE_CURSOR_POSITION "\033[s" +#define RESTORE_CURSOR_POSITION "\033[u" +#define CLEAR_TO_END_OF_LINE "\033[K" +/// Эти коды, возможно, поддерживаются не везде. +#define DISABLE_LINE_WRAPPING "\033[?7l" +#define ENABLE_LINE_WRAPPING "\033[?7h" + + /** Клиент командной строки СУБД ClickHouse. */ @@ -61,11 +71,7 @@ using Poco::SharedPtr; class Client : public Poco::Util::Application { public: - Client() : is_interactive(true), stdin_is_not_tty(false), - format_max_block_size(0), std_in(STDIN_FILENO), std_out(STDOUT_FILENO), processed_rows(0), - rows_read_on_server(0), bytes_read_on_server(0), written_progress_chars(0), written_first_block(false) - { - } + Client() {} private: typedef std::unordered_set StringSet; @@ -77,24 +83,24 @@ private: "q", "й", "\\q", "\\Q", "\\й", "\\Й", ":q", "Жй" }; - bool is_interactive; /// Использовать readline интерфейс или batch режим. - bool stdin_is_not_tty; /// stdin - не терминал. + bool is_interactive = true; /// Использовать readline интерфейс или batch режим. + bool stdin_is_not_tty = false; /// stdin - не терминал. SharedPtr connection; /// Соединение с БД. String query; /// Текущий запрос. String format; /// Формат вывода результата в консоль. - size_t format_max_block_size; /// Максимальный размер блока при выводе в консоль. + size_t format_max_block_size = 0; /// Максимальный размер блока при выводе в консоль. String insert_format; /// Формат данных для INSERT-а при чтении их из stdin в batch режиме - size_t insert_format_max_block_size; /// Максимальный размер блока при чтении данных INSERT-а. + size_t insert_format_max_block_size = 0; /// Максимальный размер блока при чтении данных INSERT-а. Context context; /// Чтение из stdin для batch режима - ReadBufferFromFileDescriptor std_in; + ReadBufferFromFileDescriptor std_in {STDIN_FILENO}; /// Вывод в консоль - WriteBufferFromFileDescriptor std_out; + WriteBufferFromFileDescriptor std_out {STDOUT_FILENO}; BlockOutputStreamPtr block_std_out; String home_path; @@ -105,7 +111,7 @@ private: String history_file; /// Строк прочитано или записано. - size_t processed_rows; + size_t processed_rows = 0; /// Распарсенный запрос. Оттуда берутся некоторые настройки (формат). ASTPtr parsed_query; @@ -115,10 +121,10 @@ private: Stopwatch watch; - size_t rows_read_on_server; - size_t bytes_read_on_server; - size_t written_progress_chars; - bool written_first_block; + size_t rows_read_on_server = 0; + size_t bytes_read_on_server = 0; + size_t written_progress_chars = 0; + bool written_first_block = false; /// Информация о внешних таблицах std::list external_tables; @@ -755,12 +761,7 @@ private: void onData(Block & block) { if (written_progress_chars) - { - for (size_t i = 0; i < written_progress_chars; ++i) - std::cerr << "\b \b"; - - written_progress_chars = 0; - } + clearProgress(); if (!block) return; @@ -813,8 +814,18 @@ private: } + void clearProgress() + { + std::cerr << RESTORE_CURSOR_POSITION CLEAR_TO_END_OF_LINE; + written_progress_chars = 0; + } + + void writeProgress() { + if (!is_interactive) + return; + static size_t increment = 0; static const char * indicators[8] = { @@ -825,30 +836,30 @@ private: "\033[1;34m←\033[0m", "\033[1;35m↖\033[0m", "\033[1;36m↑\033[0m", - "\033[1;37m↗\033[0m", + "\033[1m↗\033[0m", }; - if (is_interactive) - { - std::cerr << std::string(written_progress_chars, '\b'); + if (written_progress_chars) + clearProgress(); + else + std::cerr << SAVE_CURSOR_POSITION; - std::stringstream message; - message << indicators[increment % 8] - << std::fixed << std::setprecision(3) - << " Progress: " << rows_read_on_server << " rows, " << bytes_read_on_server / 1000000.0 << " MB"; + std::stringstream message; + message << indicators[increment % 8] + << std::fixed << std::setprecision(3) + << " Progress: " << rows_read_on_server << " rows, " << bytes_read_on_server / 1000000.0 << " MB"; - size_t elapsed_ns = watch.elapsed(); - if (elapsed_ns) - message << " (" - << rows_read_on_server * 1000000000.0 / elapsed_ns << " rows/s., " - << bytes_read_on_server * 1000.0 / elapsed_ns << " MB/s.) "; - else - message << ". "; + size_t elapsed_ns = watch.elapsed(); + if (elapsed_ns) + message << " (" + << rows_read_on_server * 1000000000.0 / elapsed_ns << " rows/s., " + << bytes_read_on_server * 1000.0 / elapsed_ns << " MB/s.) "; + else + message << ". "; - written_progress_chars = message.str().size() - 13; - std::cerr << message.rdbuf(); - ++increment; - } + written_progress_chars = message.str().size() - 13; + std::cerr << DISABLE_LINE_WRAPPING << message.rdbuf() << ENABLE_LINE_WRAPPING; + ++increment; } diff --git a/dbms/src/DataStreams/PrettyBlockOutputStream.cpp b/dbms/src/DataStreams/PrettyBlockOutputStream.cpp index 01f4f86ce72..bbcbac06065 100644 --- a/dbms/src/DataStreams/PrettyBlockOutputStream.cpp +++ b/dbms/src/DataStreams/PrettyBlockOutputStream.cpp @@ -140,7 +140,7 @@ void PrettyBlockOutputStream::write(const Block & block_) const ColumnWithNameAndType & col = block.getByPosition(i); if (!no_escapes) - writeCString("\033[1;37m", ostr); + writeCString("\033[1m", ostr); if (col.type->isNumeric()) { diff --git a/dbms/src/DataStreams/PrettyCompactBlockOutputStream.cpp b/dbms/src/DataStreams/PrettyCompactBlockOutputStream.cpp index 4ac18c596e8..530a1ef12f2 100644 --- a/dbms/src/DataStreams/PrettyCompactBlockOutputStream.cpp +++ b/dbms/src/DataStreams/PrettyCompactBlockOutputStream.cpp @@ -29,7 +29,7 @@ void PrettyCompactBlockOutputStream::writeHeader( writeCString("─", ostr); if (!no_escapes) - writeCString("\033[1;37m", ostr); + writeCString("\033[1m", ostr); writeEscapedString(col.name, ostr); if (!no_escapes) writeCString("\033[0m", ostr); @@ -37,7 +37,7 @@ void PrettyCompactBlockOutputStream::writeHeader( else { if (!no_escapes) - writeCString("\033[1;37m", ostr); + writeCString("\033[1m", ostr); writeEscapedString(col.name, ostr); if (!no_escapes) writeCString("\033[0m", ostr); @@ -75,7 +75,7 @@ void PrettyCompactBlockOutputStream::writeRow( const Widths_t & name_widths) { size_t columns = max_widths.size(); - + writeCString("│ ", ostr); for (size_t j = 0; j < columns; ++j) @@ -90,7 +90,7 @@ void PrettyCompactBlockOutputStream::writeRow( size_t width = get((*block.getByPosition(columns + j).column)[row_id]); for (size_t k = 0; k < max_widths[j] - width; ++k) writeChar(' ', ostr); - + col.type->serializeTextEscaped((*col.column)[row_id], ostr); } else @@ -113,16 +113,16 @@ void PrettyCompactBlockOutputStream::write(const Block & block_) total_rows += block_.rows(); return; } - + /// Будем вставлять сюда столбцы с вычисленными значениями видимых длин. Block block = block_; - + size_t rows = block.rows(); Widths_t max_widths; Widths_t name_widths; calculateWidths(block, max_widths, name_widths); - + writeHeader(block, max_widths, name_widths); for (size_t i = 0; i < rows && total_rows + i < max_rows; ++i) diff --git a/dbms/src/DataStreams/PrettySpaceBlockOutputStream.cpp b/dbms/src/DataStreams/PrettySpaceBlockOutputStream.cpp index a26b0e95e39..8c92367eebd 100644 --- a/dbms/src/DataStreams/PrettySpaceBlockOutputStream.cpp +++ b/dbms/src/DataStreams/PrettySpaceBlockOutputStream.cpp @@ -17,10 +17,10 @@ void PrettySpaceBlockOutputStream::write(const Block & block_) total_rows += block_.rows(); return; } - + /// Будем вставлять суда столбцы с вычисленными значениями видимых длин. Block block = block_; - + size_t rows = block.rows(); size_t columns = block.columns(); @@ -48,7 +48,7 @@ void PrettySpaceBlockOutputStream::write(const Block & block_) writeChar(' ', ostr); if (!no_escapes) - writeCString("\033[1;37m", ostr); + writeCString("\033[1m", ostr); writeEscapedString(col.name, ostr); if (!no_escapes) writeCString("\033[0m", ostr); @@ -56,7 +56,7 @@ void PrettySpaceBlockOutputStream::write(const Block & block_) else { if (!no_escapes) - writeCString("\033[1;37m", ostr); + writeCString("\033[1m", ostr); writeEscapedString(col.name, ostr); if (!no_escapes) writeCString("\033[0m", ostr); @@ -81,7 +81,7 @@ void PrettySpaceBlockOutputStream::write(const Block & block_) size_t width = get((*block.getByPosition(columns + j).column)[i]); for (ssize_t k = 0; k < std::max(0L, static_cast(max_widths[j] - width)); ++k) writeChar(' ', ostr); - + col.type->serializeTextEscaped((*col.column)[i], ostr); } else diff --git a/dbms/src/Parsers/formatAST.cpp b/dbms/src/Parsers/formatAST.cpp index 050bff00caf..7b696b49510 100644 --- a/dbms/src/Parsers/formatAST.cpp +++ b/dbms/src/Parsers/formatAST.cpp @@ -19,7 +19,7 @@ namespace DB { -static const char * hilite_keyword = "\033[1;37m"; +static const char * hilite_keyword = "\033[1m"; static const char * hilite_identifier = "\033[0;36m"; static const char * hilite_function = "\033[0;33m"; static const char * hilite_operator = "\033[1;33m";