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>
/// 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<String> 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> 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<ExternalTable> 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;
}

View File

@ -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())
{

View File

@ -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<UInt64>((*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)

View File

@ -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<UInt64>((*block.getByPosition(columns + j).column)[i]);
for (ssize_t k = 0; k < std::max(0L, static_cast<ssize_t>(max_widths[j] - width)); ++k)
writeChar(' ', ostr);
col.type->serializeTextEscaped((*col.column)[i], ostr);
}
else

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