diff --git a/dbms/include/DB/DataStreams/PrettyBlockOutputStream.h b/dbms/include/DB/DataStreams/PrettyBlockOutputStream.h index a30aee52635..f9a5d5a7cbc 100644 --- a/dbms/include/DB/DataStreams/PrettyBlockOutputStream.h +++ b/dbms/include/DB/DataStreams/PrettyBlockOutputStream.h @@ -18,7 +18,12 @@ public: void writeSuffix(); BlockOutputStreamPtr clone() { return new PrettyBlockOutputStream(ostr); } -private: +protected: + typedef std::vector Widths_t; + + /// Вычислить видимую (при выводе на консоль с кодировкой UTF-8) ширину значений и имён столбцов. + void calculateWidths(Block & block, Widths_t & max_widths, Widths_t & name_widths); + WriteBuffer & ostr; size_t max_rows; size_t total_rows; diff --git a/dbms/include/DB/DataStreams/PrettyCompactBlockOutputStream.h b/dbms/include/DB/DataStreams/PrettyCompactBlockOutputStream.h new file mode 100644 index 00000000000..7ab60aabb89 --- /dev/null +++ b/dbms/include/DB/DataStreams/PrettyCompactBlockOutputStream.h @@ -0,0 +1,21 @@ +#pragma once + +#include + + +namespace DB +{ + +/** Выводит результат в виде красивых таблиц, но с меньшим количеством строк-разделителей. + */ +class PrettyCompactBlockOutputStream : public PrettyBlockOutputStream +{ +public: + PrettyCompactBlockOutputStream(WriteBuffer & ostr_, size_t max_rows_ = PRETTY_FORMAT_DEFAULT_MAX_ROWS) + : PrettyBlockOutputStream(ostr_, max_rows_) {} + + void write(const Block & block); + BlockOutputStreamPtr clone() { return new PrettyCompactBlockOutputStream(ostr); } +}; + +} diff --git a/dbms/include/DB/DataStreams/PrettySpaceBlockOutputStream.h b/dbms/include/DB/DataStreams/PrettySpaceBlockOutputStream.h new file mode 100644 index 00000000000..b73df73ece2 --- /dev/null +++ b/dbms/include/DB/DataStreams/PrettySpaceBlockOutputStream.h @@ -0,0 +1,22 @@ +#pragma once + +#include + + +namespace DB +{ + +/** Выводит результат, выравнивая пробелами. + */ +class PrettySpaceBlockOutputStream : public PrettyBlockOutputStream +{ +public: + PrettySpaceBlockOutputStream(WriteBuffer & ostr_, size_t max_rows_ = PRETTY_FORMAT_DEFAULT_MAX_ROWS) + : PrettyBlockOutputStream(ostr_, max_rows_) {} + + void write(const Block & block); + void writeSuffix(); + BlockOutputStreamPtr clone() { return new PrettySpaceBlockOutputStream(ostr); } +}; + +} diff --git a/dbms/include/DB/DataStreams/VerticalRowOutputStream.h b/dbms/include/DB/DataStreams/VerticalRowOutputStream.h new file mode 100644 index 00000000000..910fd0452cd --- /dev/null +++ b/dbms/include/DB/DataStreams/VerticalRowOutputStream.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include +#include +#include +#include + + +namespace DB +{ + +using Poco::SharedPtr; + + +/** Интерфейс потока для вывода данных в формате "каждое значение на своей строке". + */ +class VerticalRowOutputStream : public IRowOutputStream +{ +public: + VerticalRowOutputStream(WriteBuffer & ostr_, const Block & sample_); + + void writeField(const Field & field); + void writeRowStartDelimiter(); + void writeRowBetweenDelimiter(); + + RowOutputStreamPtr clone() { return new VerticalRowOutputStream(ostr, sample); } + +private: + WriteBuffer & ostr; + const Block & sample; + DataTypes data_types; + Names names; + size_t field_number; + size_t row_number; + + typedef std::vector Pads_t; + Pads_t pads; +}; + +} + diff --git a/dbms/src/DataStreams/FormatFactory.cpp b/dbms/src/DataStreams/FormatFactory.cpp index 9d5414db102..3c33540eac4 100644 --- a/dbms/src/DataStreams/FormatFactory.cpp +++ b/dbms/src/DataStreams/FormatFactory.cpp @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -25,9 +28,7 @@ BlockInputStreamPtr FormatFactory::getInput(const String & name, ReadBuffer & bu return new BlockInputStreamFromRowInputStream(new TabSeparatedRowInputStream(buf, sample, true), sample, max_block_size); else if (name == "TabSeparatedWithNamesAndTypes") return new BlockInputStreamFromRowInputStream(new TabSeparatedRowInputStream(buf, sample, true, true), sample, max_block_size); - else if (name == "BlockTabSeparated") - throw Exception("Format " + name + " is not suitable for input", ErrorCodes::FORMAT_IS_NOT_SUITABLE_FOR_INPUT); - else if (name == "Pretty") + else if (name == "BlockTabSeparated" || name == "Pretty" || name == "PrettyCompact" || name == "PrettySpace" || name == "Vertical") throw Exception("Format " + name + " is not suitable for input", ErrorCodes::FORMAT_IS_NOT_SUITABLE_FOR_INPUT); else if (name == "Values") return new BlockInputStreamFromRowInputStream(new ValuesRowInputStream(buf, sample), sample, max_block_size); @@ -51,6 +52,12 @@ BlockOutputStreamPtr FormatFactory::getOutput(const String & name, WriteBuffer & return new TabSeparatedBlockOutputStream(buf); else if (name == "Pretty") return new PrettyBlockOutputStream(buf); + else if (name == "PrettyCompact") + return new PrettyCompactBlockOutputStream(buf); + else if (name == "PrettySpace") + return new PrettySpaceBlockOutputStream(buf); + else if (name == "Vertical") + return new BlockOutputStreamFromRowOutputStream(new VerticalRowOutputStream(buf, sample)); else if (name == "Values") return new BlockOutputStreamFromRowOutputStream(new ValuesRowOutputStream(buf, sample)); else diff --git a/dbms/src/DataStreams/PrettyBlockOutputStream.cpp b/dbms/src/DataStreams/PrettyBlockOutputStream.cpp index 650afa7a370..60c34026a67 100644 --- a/dbms/src/DataStreams/PrettyBlockOutputStream.cpp +++ b/dbms/src/DataStreams/PrettyBlockOutputStream.cpp @@ -13,30 +13,23 @@ PrettyBlockOutputStream::PrettyBlockOutputStream(WriteBuffer & ostr_, size_t max : ostr(ostr_), max_rows(max_rows_), total_rows(0), terminal_width(0) { struct winsize w; - if (0 != ioctl(STDOUT_FILENO, TIOCGWINSZ, &w)) + if (0 == ioctl(STDOUT_FILENO, TIOCGWINSZ, &w)) terminal_width = w.ws_col; } -void PrettyBlockOutputStream::write(const Block & block_) +void PrettyBlockOutputStream::calculateWidths(Block & block, Widths_t & max_widths, Widths_t & name_widths) { - if (total_rows >= max_rows) - return; - - /// Будем вставлять суда столбцы с вычисленными значениями видимых длин. - Block block = block_; - size_t rows = block.rows(); size_t columns = block.columns(); + + max_widths.resize(columns); + name_widths.resize(columns); - /// Вычислим ширину всех значений FunctionVisibleWidth visible_width_func; DataTypePtr visible_width_type = new DataTypeUInt64; - typedef std::vector Widths_t; - Widths_t max_widths(columns); - Widths_t name_widths(columns); - + /// Вычислим ширину всех значений for (size_t i = 0; i < columns; ++i) { ColumnWithNameAndType column; @@ -66,7 +59,7 @@ void PrettyBlockOutputStream::write(const Block & block_) max_widths[i] = res; } else - throw Exception("Illegal column " + column.column->getName() + throw Exception("Illegal column " + column.column->getName() + " of result of function " + visible_width_func.getName(), ErrorCodes::ILLEGAL_COLUMN); @@ -75,6 +68,23 @@ void PrettyBlockOutputStream::write(const Block & block_) if (name_widths[i] > max_widths[i]) max_widths[i] = name_widths[i]; } +} + + +void PrettyBlockOutputStream::write(const Block & block_) +{ + if (total_rows >= max_rows) + return; + + /// Будем вставлять суда столбцы с вычисленными значениями видимых длин. + Block block = block_; + + size_t rows = block.rows(); + size_t columns = block.columns(); + + Widths_t max_widths; + Widths_t name_widths; + calculateWidths(block, max_widths, name_widths); /// Создадим разделители std::stringstream top_separator; diff --git a/dbms/src/DataStreams/PrettyCompactBlockOutputStream.cpp b/dbms/src/DataStreams/PrettyCompactBlockOutputStream.cpp new file mode 100644 index 00000000000..a3ee487d320 --- /dev/null +++ b/dbms/src/DataStreams/PrettyCompactBlockOutputStream.cpp @@ -0,0 +1,111 @@ +#include +#include + +#include + +#include + + +namespace DB +{ + + +void PrettyCompactBlockOutputStream::write(const Block & block_) +{ + if (total_rows >= max_rows) + return; + + /// Будем вставлять суда столбцы с вычисленными значениями видимых длин. + Block block = block_; + + size_t rows = block.rows(); + size_t columns = block.columns(); + + Widths_t max_widths; + Widths_t name_widths; + calculateWidths(block, max_widths, name_widths); + + /// Создадим разделители + std::stringstream bottom_separator; + + bottom_separator << "└"; + for (size_t i = 0; i < columns; ++i) + { + if (i != 0) + bottom_separator << "┴"; + + for (size_t j = 0; j < max_widths[i] + 2; ++j) + bottom_separator << "─"; + } + bottom_separator << "┘\n"; + + std::string bottom_separator_s = bottom_separator.str(); + + /// Имена + writeString("┌─", ostr); + for (size_t i = 0; i < columns; ++i) + { + if (i != 0) + writeString("─┬─", ostr); + + const ColumnWithNameAndType & col = block.getByPosition(i); + + if (col.type->isNumeric()) + { + for (size_t k = 0; k < max_widths[i] - name_widths[i]; ++k) + writeString("─", ostr); + + writeString("\033[1;37m", ostr); + writeEscapedString(col.name, ostr); + writeString("\033[0m", ostr); + } + else + { + writeString("\033[1;37m", ostr); + writeEscapedString(col.name, ostr); + writeString("\033[0m", ostr); + + for (size_t k = 0; k < max_widths[i] - name_widths[i]; ++k) + writeString("─", ostr); + } + } + writeString("─┐\n", ostr); + + for (size_t i = 0; i < rows && total_rows + i < max_rows; ++i) + { + writeString("│ ", ostr); + + for (size_t j = 0; j < columns; ++j) + { + if (j != 0) + writeString(" │ ", ostr); + + const ColumnWithNameAndType & col = block.getByPosition(j); + + if (col.type->isNumeric()) + { + size_t width = boost::get((*block.getByPosition(columns + j).column)[i]); + for (size_t k = 0; k < max_widths[j] - width; ++k) + writeChar(' ', ostr); + + col.type->serializeTextEscaped((*col.column)[i], ostr); + } + else + { + col.type->serializeTextEscaped((*col.column)[i], ostr); + + size_t width = boost::get((*block.getByPosition(columns + j).column)[i]); + for (size_t k = 0; k < max_widths[j] - width; ++k) + writeChar(' ', ostr); + } + } + + writeString(" │\n", ostr); + } + + writeString(bottom_separator_s, ostr); + + total_rows += rows; +} + +} diff --git a/dbms/src/DataStreams/PrettySpaceBlockOutputStream.cpp b/dbms/src/DataStreams/PrettySpaceBlockOutputStream.cpp new file mode 100644 index 00000000000..80aa8da3e1f --- /dev/null +++ b/dbms/src/DataStreams/PrettySpaceBlockOutputStream.cpp @@ -0,0 +1,112 @@ +#include +#include + +#include + +#include + + +namespace DB +{ + + +void PrettySpaceBlockOutputStream::write(const Block & block_) +{ + if (total_rows >= max_rows) + return; + + /// Будем вставлять суда столбцы с вычисленными значениями видимых длин. + Block block = block_; + + size_t rows = block.rows(); + size_t columns = block.columns(); + + Widths_t max_widths; + Widths_t name_widths; + calculateWidths(block, max_widths, name_widths); + + /// Не будем выравнивать по слишком длинным значениям. + if (terminal_width > 80) + for (size_t i = 0; i < columns; ++i) + if (max_widths[i] > terminal_width / 2) + max_widths[i] = terminal_width / 2; + + /// Имена + for (size_t i = 0; i < columns; ++i) + { + if (i != 0) + writeString(" ", ostr); + + const ColumnWithNameAndType & col = block.getByPosition(i); + + if (col.type->isNumeric()) + { + for (size_t k = 0; k < std::max(0L, static_cast(max_widths[i] - name_widths[i])); ++k) + writeChar(' ', ostr); + + writeString("\033[1;37m", ostr); + writeEscapedString(col.name, ostr); + writeString("\033[0m", ostr); + } + else + { + writeString("\033[1;37m", ostr); + writeEscapedString(col.name, ostr); + writeString("\033[0m", ostr); + + for (size_t k = 0; k < std::max(0L, static_cast(max_widths[i] - name_widths[i])); ++k) + writeChar(' ', ostr); + } + } + writeString("\n\n", ostr); + + for (size_t i = 0; i < rows && total_rows + i < max_rows; ++i) + { + for (size_t j = 0; j < columns; ++j) + { + if (j != 0) + writeString(" ", ostr); + + const ColumnWithNameAndType & col = block.getByPosition(j); + + if (col.type->isNumeric()) + { + size_t width = boost::get((*block.getByPosition(columns + j).column)[i]); + for (size_t k = 0; k < std::max(0L, static_cast(max_widths[j] - width)); ++k) + writeChar(' ', ostr); + + col.type->serializeTextEscaped((*col.column)[i], ostr); + } + else + { + col.type->serializeTextEscaped((*col.column)[i], ostr); + + size_t width = boost::get((*block.getByPosition(columns + j).column)[i]); + for (size_t k = 0; k < std::max(0L, static_cast(max_widths[j] - width)); ++k) + writeChar(' ', ostr); + } + } + + writeChar('\n', ostr); + } + + total_rows += rows; +} + + +void PrettySpaceBlockOutputStream::writeSuffix() +{ + writeChar('\n', ostr); + writeIntText(total_rows, ostr); + writeString(" rows in set.", ostr); + + if (total_rows >= max_rows) + { + writeString(" Showed first ", ostr); + writeIntText(max_rows, ostr); + writeString(".", ostr); + } + writeChar('\n', ostr); +} + +} diff --git a/dbms/src/DataStreams/VerticalRowOutputStream.cpp b/dbms/src/DataStreams/VerticalRowOutputStream.cpp new file mode 100644 index 00000000000..22bb6769636 --- /dev/null +++ b/dbms/src/DataStreams/VerticalRowOutputStream.cpp @@ -0,0 +1,73 @@ +#include +#include + +#include + + +namespace DB +{ + +using Poco::SharedPtr; + + +VerticalRowOutputStream::VerticalRowOutputStream(WriteBuffer & ostr_, const Block & sample_) + : ostr(ostr_), sample(sample_), field_number(0), row_number(0) +{ + size_t columns = sample.columns(); + data_types.resize(columns); + names.resize(columns); + + typedef std::vector Widths_t; + Widths_t name_widths(columns); + size_t max_name_width = 0; + + for (size_t i = 0; i < columns; ++i) + { + data_types[i] = sample.getByPosition(i).type; + names[i] = sample.getByPosition(i).name; + stringWidthConstant(names[i], name_widths[i]); + if (name_widths[i] > max_name_width) + max_name_width = name_widths[i]; + } + + pads.resize(columns); + for (size_t i = 0; i < columns; ++i) + pads[i] = String(max_name_width - name_widths[i], ' '); +} + + +void VerticalRowOutputStream::writeField(const Field & field) +{ + writeEscapedString(names[field_number], ostr); + writeString(": ", ostr); + writeString(pads[field_number], ostr); + + data_types[field_number]->serializeTextEscaped(field, ostr); + + writeChar('\n', ostr); + ++field_number; +} + + +void VerticalRowOutputStream::writeRowStartDelimiter() +{ + ++row_number; + writeString("Row ", ostr); + writeIntText(row_number, ostr); + writeString(":\n", ostr); + + size_t width = log10(row_number + 1) + 1 + strlen("Row :"); + for (size_t i = 0; i < width; ++i) + writeString("─", ostr); + writeChar('\n', ostr); +} + + +void VerticalRowOutputStream::writeRowBetweenDelimiter() +{ + writeString("\n", ostr); + field_number = 0; +} + + +}