dbms: Client: don't leave progress indicator in the middle of data; colors almost compatible with white backgrounds [#METR-11125].

This commit is contained in:
Alexey Milovidov 2014-08-15 04:08:15 +04:00
parent 65cf115313
commit 047e5e30df
5 changed files with 66 additions and 55 deletions

View File

@ -49,6 +49,16 @@
#include <DB/Common/ExternalTable.h> #include <DB/Common/ExternalTable.h>
/// 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. /** Клиент командной строки СУБД ClickHouse.
*/ */
@ -61,11 +71,7 @@ using Poco::SharedPtr;
class Client : public Poco::Util::Application class Client : public Poco::Util::Application
{ {
public: public:
Client() : is_interactive(true), stdin_is_not_tty(false), Client() {}
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)
{
}
private: private:
typedef std::unordered_set<String> StringSet; typedef std::unordered_set<String> StringSet;
@ -77,24 +83,24 @@ private:
"q", "й", "\\q", "\\Q", "\\й", "\\Й", ":q", "Жй" "q", "й", "\\q", "\\Q", "\\й", "\\Й", ":q", "Жй"
}; };
bool is_interactive; /// Использовать readline интерфейс или batch режим. bool is_interactive = true; /// Использовать readline интерфейс или batch режим.
bool stdin_is_not_tty; /// stdin - не терминал. bool stdin_is_not_tty = false; /// stdin - не терминал.
SharedPtr<Connection> connection; /// Соединение с БД. SharedPtr<Connection> connection; /// Соединение с БД.
String query; /// Текущий запрос. String query; /// Текущий запрос.
String format; /// Формат вывода результата в консоль. String format; /// Формат вывода результата в консоль.
size_t format_max_block_size; /// Максимальный размер блока при выводе в консоль. size_t format_max_block_size = 0; /// Максимальный размер блока при выводе в консоль.
String insert_format; /// Формат данных для INSERT-а при чтении их из stdin в batch режиме 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; Context context;
/// Чтение из stdin для batch режима /// Чтение из stdin для batch режима
ReadBufferFromFileDescriptor std_in; ReadBufferFromFileDescriptor std_in {STDIN_FILENO};
/// Вывод в консоль /// Вывод в консоль
WriteBufferFromFileDescriptor std_out; WriteBufferFromFileDescriptor std_out {STDOUT_FILENO};
BlockOutputStreamPtr block_std_out; BlockOutputStreamPtr block_std_out;
String home_path; String home_path;
@ -105,7 +111,7 @@ private:
String history_file; String history_file;
/// Строк прочитано или записано. /// Строк прочитано или записано.
size_t processed_rows; size_t processed_rows = 0;
/// Распарсенный запрос. Оттуда берутся некоторые настройки (формат). /// Распарсенный запрос. Оттуда берутся некоторые настройки (формат).
ASTPtr parsed_query; ASTPtr parsed_query;
@ -115,10 +121,10 @@ private:
Stopwatch watch; Stopwatch watch;
size_t rows_read_on_server; size_t rows_read_on_server = 0;
size_t bytes_read_on_server; size_t bytes_read_on_server = 0;
size_t written_progress_chars; size_t written_progress_chars = 0;
bool written_first_block; bool written_first_block = false;
/// Информация о внешних таблицах /// Информация о внешних таблицах
std::list<ExternalTable> external_tables; std::list<ExternalTable> external_tables;
@ -755,12 +761,7 @@ private:
void onData(Block & block) void onData(Block & block)
{ {
if (written_progress_chars) if (written_progress_chars)
{ clearProgress();
for (size_t i = 0; i < written_progress_chars; ++i)
std::cerr << "\b \b";
written_progress_chars = 0;
}
if (!block) if (!block)
return; return;
@ -813,8 +814,18 @@ private:
} }
void clearProgress()
{
std::cerr << RESTORE_CURSOR_POSITION CLEAR_TO_END_OF_LINE;
written_progress_chars = 0;
}
void writeProgress() void writeProgress()
{ {
if (!is_interactive)
return;
static size_t increment = 0; static size_t increment = 0;
static const char * indicators[8] = static const char * indicators[8] =
{ {
@ -825,12 +836,13 @@ private:
"\033[1;34m←\033[0m", "\033[1;34m←\033[0m",
"\033[1;35m↖\033[0m", "\033[1;35m↖\033[0m",
"\033[1;36m↑\033[0m", "\033[1;36m↑\033[0m",
"\033[1;37m↗\033[0m", "\033[1m↗\033[0m",
}; };
if (is_interactive) if (written_progress_chars)
{ clearProgress();
std::cerr << std::string(written_progress_chars, '\b'); else
std::cerr << SAVE_CURSOR_POSITION;
std::stringstream message; std::stringstream message;
message << indicators[increment % 8] message << indicators[increment % 8]
@ -846,10 +858,9 @@ private:
message << ". "; message << ". ";
written_progress_chars = message.str().size() - 13; written_progress_chars = message.str().size() - 13;
std::cerr << message.rdbuf(); std::cerr << DISABLE_LINE_WRAPPING << message.rdbuf() << ENABLE_LINE_WRAPPING;
++increment; ++increment;
} }
}
void writeFinalProgress() void writeFinalProgress()

View File

@ -140,7 +140,7 @@ void PrettyBlockOutputStream::write(const Block & block_)
const ColumnWithNameAndType & col = block.getByPosition(i); const ColumnWithNameAndType & col = block.getByPosition(i);
if (!no_escapes) if (!no_escapes)
writeCString("\033[1;37m", ostr); writeCString("\033[1m", ostr);
if (col.type->isNumeric()) if (col.type->isNumeric())
{ {

View File

@ -29,7 +29,7 @@ void PrettyCompactBlockOutputStream::writeHeader(
writeCString("", ostr); writeCString("", ostr);
if (!no_escapes) if (!no_escapes)
writeCString("\033[1;37m", ostr); writeCString("\033[1m", ostr);
writeEscapedString(col.name, ostr); writeEscapedString(col.name, ostr);
if (!no_escapes) if (!no_escapes)
writeCString("\033[0m", ostr); writeCString("\033[0m", ostr);
@ -37,7 +37,7 @@ void PrettyCompactBlockOutputStream::writeHeader(
else else
{ {
if (!no_escapes) if (!no_escapes)
writeCString("\033[1;37m", ostr); writeCString("\033[1m", ostr);
writeEscapedString(col.name, ostr); writeEscapedString(col.name, ostr);
if (!no_escapes) if (!no_escapes)
writeCString("\033[0m", ostr); writeCString("\033[0m", ostr);

View File

@ -48,7 +48,7 @@ void PrettySpaceBlockOutputStream::write(const Block & block_)
writeChar(' ', ostr); writeChar(' ', ostr);
if (!no_escapes) if (!no_escapes)
writeCString("\033[1;37m", ostr); writeCString("\033[1m", ostr);
writeEscapedString(col.name, ostr); writeEscapedString(col.name, ostr);
if (!no_escapes) if (!no_escapes)
writeCString("\033[0m", ostr); writeCString("\033[0m", ostr);
@ -56,7 +56,7 @@ void PrettySpaceBlockOutputStream::write(const Block & block_)
else else
{ {
if (!no_escapes) if (!no_escapes)
writeCString("\033[1;37m", ostr); writeCString("\033[1m", ostr);
writeEscapedString(col.name, ostr); writeEscapedString(col.name, ostr);
if (!no_escapes) if (!no_escapes)
writeCString("\033[0m", ostr); writeCString("\033[0m", ostr);

View File

@ -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_identifier = "\033[0;36m";
static const char * hilite_function = "\033[0;33m"; static const char * hilite_function = "\033[0;33m";
static const char * hilite_operator = "\033[1;33m"; static const char * hilite_operator = "\033[1;33m";