mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 07:31:57 +00:00
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:
parent
65cf115313
commit
047e5e30df
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
Loading…
Reference in New Issue
Block a user