From 40f7ec71d3164336982346bc0358d786a10fbc45 Mon Sep 17 00:00:00 2001 From: Maxim Sabyanin Date: Fri, 10 Jul 2020 00:02:37 +0300 Subject: [PATCH] add setting output_format_pretty_grid_charset This setting allows to chose charset for printing grids (either UTF-8 or ASCII). --- src/Core/Settings.h | 1 + src/DataTypes/DataTypeNullable.cpp | 7 +- src/Formats/FormatFactory.cpp | 3 + src/Formats/FormatFactory.h | 1 + src/Formats/FormatSettings.h | 8 ++ .../Formats/Impl/PrettyBlockOutputFormat.cpp | 114 ++++++++++++++---- .../Impl/PrettyCompactBlockOutputFormat.cpp | 76 ++++++++++-- .../00405_pretty_formats.reference | 93 ++++++++++++++ .../0_stateless/00405_pretty_formats.sql | 11 ++ 9 files changed, 277 insertions(+), 37 deletions(-) diff --git a/src/Core/Settings.h b/src/Core/Settings.h index c5d697f3170..44fcc9b1c1a 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -439,6 +439,7 @@ struct Settings : public SettingsCollection M(SettingUInt64, output_format_pretty_max_column_pad_width, 250, "Maximum width to pad all values in a column in Pretty formats.", 0) \ M(SettingUInt64, output_format_pretty_max_value_width, 10000, "Maximum width of value to display in Pretty formats. If greater - it will be cut.", 0) \ M(SettingBool, output_format_pretty_color, true, "Use ANSI escape sequences to paint colors in Pretty formats", 0) \ + M(SettingString, output_format_pretty_grid_charset, "UTF-8", "Charset for printing grid borders. Available charsets: ASCII, UTF-8 (default one).", 0) \ M(SettingUInt64, output_format_parquet_row_group_size, 1000000, "Row group size in rows.", 0) \ M(SettingString, output_format_avro_codec, "", "Compression codec used for output. Possible values: 'null', 'deflate', 'snappy'.", 0) \ M(SettingUInt64, output_format_avro_sync_interval, 16 * 1024, "Sync interval in bytes.", 0) \ diff --git a/src/DataTypes/DataTypeNullable.cpp b/src/DataTypes/DataTypeNullable.cpp index b5ffae9588c..d418b296ae3 100644 --- a/src/DataTypes/DataTypeNullable.cpp +++ b/src/DataTypes/DataTypeNullable.cpp @@ -419,7 +419,12 @@ void DataTypeNullable::serializeText(const IColumn & column, size_t row_num, Wri /// This assumes UTF-8 and proper font support. This is Ok, because Pretty formats are "presentational", not for data exchange. if (col.isNullAt(row_num)) - writeCString("ᴺᵁᴸᴸ", ostr); + { + if (settings.pretty.charset == FormatSettings::Pretty::Charset::UTF8) + writeCString("ᴺᵁᴸᴸ", ostr); + else + writeCString("null", ostr); + } else nested_data_type->serializeAsText(col.getNestedColumn(), row_num, ostr, settings); } diff --git a/src/Formats/FormatFactory.cpp b/src/Formats/FormatFactory.cpp index f09a13d51ba..c126e5696aa 100644 --- a/src/Formats/FormatFactory.cpp +++ b/src/Formats/FormatFactory.cpp @@ -105,6 +105,9 @@ static FormatSettings getOutputFormatSetting(const Settings & settings, const Co format_settings.pretty.max_column_pad_width = settings.output_format_pretty_max_column_pad_width; format_settings.pretty.max_value_width = settings.output_format_pretty_max_value_width; format_settings.pretty.color = settings.output_format_pretty_color; + format_settings.pretty.charset = settings.output_format_pretty_grid_charset.toString() == "ASCII" ? + FormatSettings::Pretty::Charset::ASCII : + FormatSettings::Pretty::Charset::UTF8; format_settings.template_settings.resultset_format = settings.format_template_resultset; format_settings.template_settings.row_format = settings.format_template_row; format_settings.template_settings.row_between_delimiter = settings.format_template_rows_between_delimiter; diff --git a/src/Formats/FormatFactory.h b/src/Formats/FormatFactory.h index d8a1ab55bd1..ebd1934ef60 100644 --- a/src/Formats/FormatFactory.h +++ b/src/Formats/FormatFactory.h @@ -194,6 +194,7 @@ void registerOutputFormatNull(FormatFactory & factory); void registerOutputFormatProcessorPretty(FormatFactory & factory); void registerOutputFormatProcessorPrettyCompact(FormatFactory & factory); void registerOutputFormatProcessorPrettySpace(FormatFactory & factory); +void registerOutputFormatProcessorPrettyASCII(FormatFactory & factory); void registerOutputFormatProcessorVertical(FormatFactory & factory); void registerOutputFormatProcessorJSON(FormatFactory & factory); void registerOutputFormatProcessorJSONCompact(FormatFactory & factory); diff --git a/src/Formats/FormatSettings.h b/src/Formats/FormatSettings.h index e66b5ec59e4..299ec353f03 100644 --- a/src/Formats/FormatSettings.h +++ b/src/Formats/FormatSettings.h @@ -44,6 +44,14 @@ struct FormatSettings UInt64 max_column_pad_width = 250; UInt64 max_value_width = 10000; bool color = true; + + enum class Charset + { + UTF8, + ASCII, + }; + + Charset charset = Charset::UTF8; }; Pretty pretty; diff --git a/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp b/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp index 59d3814adea..3cb67a2f04a 100644 --- a/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp @@ -85,6 +85,54 @@ void PrettyBlockOutputFormat::calculateWidths( } } +namespace +{ + +/// Grid symbols are used for printing grid borders in a terminal. +/// Defaults values are UTF-8. +struct GridSymbols +{ + const char * bold_left_top_corner = "┏"; + const char * bold_right_top_corner = "┓"; + const char * left_bottom_corner = "└"; + const char * right_bottom_corner = "┘"; + const char * bold_left_separator = "┡"; + const char * left_separator = "├"; + const char * bold_right_separator = "┩"; + const char * right_separator = "┤"; + const char * bold_top_separator = "┳"; + const char * bold_middle_separator = "╇"; + const char * middle_separator = "┼"; + const char * bottom_separator = "┴"; + const char * bold_dash = "━"; + const char * dash = "─"; + const char * bold_bar = "┃"; + const char * bar = "│"; +}; + +GridSymbols utf8_grid_symbols; + +GridSymbols ascii_grid_symbols { + "+", + "+", + "+", + "+", + "+", + "+", + "+", + "+", + "+", + "+", + "+", + "+", + "-", + "-", + "|", + "|" +}; + +} + void PrettyBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind) { @@ -106,38 +154,42 @@ void PrettyBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind) Widths name_widths; calculateWidths(header, chunk, widths, max_widths, name_widths); + const GridSymbols & grid_symbols = format_settings.pretty.charset == FormatSettings::Pretty::Charset::UTF8 ? + utf8_grid_symbols : + ascii_grid_symbols; + /// Create separators std::stringstream top_separator; std::stringstream middle_names_separator; std::stringstream middle_values_separator; std::stringstream bottom_separator; - top_separator << "┏"; - middle_names_separator << "┡"; - middle_values_separator << "├"; - bottom_separator << "└"; + top_separator << grid_symbols.bold_left_top_corner; + middle_names_separator << grid_symbols.bold_left_separator; + middle_values_separator << grid_symbols.left_separator; + bottom_separator << grid_symbols.left_bottom_corner; for (size_t i = 0; i < num_columns; ++i) { if (i != 0) { - top_separator << "┳"; - middle_names_separator << "╇"; - middle_values_separator << "┼"; - bottom_separator << "┴"; + top_separator << grid_symbols.bold_top_separator; + middle_names_separator << grid_symbols.bold_middle_separator; + middle_values_separator << grid_symbols.middle_separator; + bottom_separator << grid_symbols.bottom_separator; } for (size_t j = 0; j < max_widths[i] + 2; ++j) { - top_separator << "━"; - middle_names_separator << "━"; - middle_values_separator << "─"; - bottom_separator << "─"; + top_separator << grid_symbols.bold_dash; + middle_names_separator << grid_symbols.bold_dash; + middle_values_separator << grid_symbols.dash; + bottom_separator << grid_symbols.dash; } } - top_separator << "┓\n"; - middle_names_separator << "┩\n"; - middle_values_separator << "┤\n"; - bottom_separator << "┘\n"; + top_separator << grid_symbols.bold_right_top_corner << "\n"; + middle_names_separator << grid_symbols.bold_right_separator << "\n"; + middle_values_separator << grid_symbols.right_separator << "\n"; + bottom_separator << grid_symbols.right_bottom_corner << "\n"; std::string top_separator_s = top_separator.str(); std::string middle_names_separator_s = middle_names_separator.str(); @@ -148,11 +200,16 @@ void PrettyBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind) writeString(top_separator_s, out); /// Names - writeCString("┃ ", out); + writeCString(grid_symbols.bold_bar, out); + writeCString(" ", out); for (size_t i = 0; i < num_columns; ++i) { if (i != 0) - writeCString(" ┃ ", out); + { + writeCString(" ", out); + writeCString(grid_symbols.bold_bar, out); + writeCString(" ", out); + } const auto & col = header.getByPosition(i); @@ -177,7 +234,9 @@ void PrettyBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind) if (format_settings.pretty.color) writeCString("\033[0m", out); } - writeCString(" ┃\n", out); + writeCString(" ", out); + writeCString(grid_symbols.bold_bar, out); + writeCString("\n", out); writeString(middle_names_separator_s, out); @@ -186,12 +245,12 @@ void PrettyBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind) if (i != 0) writeString(middle_values_separator_s, out); - writeCString("│", out); + writeCString(grid_symbols.bar, out); for (size_t j = 0; j < num_columns; ++j) { if (j != 0) - writeCString("│", out); + writeCString(grid_symbols.bar, out); const auto & type = *header.getByPosition(j).type; writeValueWithPadding(*columns[j], type, i, @@ -199,7 +258,8 @@ void PrettyBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind) max_widths[j]); } - writeCString("│\n", out); + writeCString(grid_symbols.bar, out); + writeCString("\n", out); } writeString(bottom_separator_s, out); @@ -222,10 +282,16 @@ void PrettyBlockOutputFormat::writeValueWithPadding( serialized_value.resize(UTF8::computeBytesBeforeWidth( reinterpret_cast(serialized_value.data()), serialized_value.size(), 0, 1 + format_settings.pretty.max_value_width)); + const char *ellipsis = format_settings.pretty.charset == FormatSettings::Pretty::Charset::UTF8 ? + "⋯" : "..."; if (format_settings.pretty.color) - serialized_value += "\033[31;1m⋯\033[0m"; + { + serialized_value += "\033[31;1m"; + serialized_value += ellipsis; + serialized_value += "\033[0m"; + } else - serialized_value += "⋯"; + serialized_value += ellipsis; value_width = format_settings.pretty.max_value_width; } diff --git a/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp b/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp index e9040f672b4..72f95a50ea4 100644 --- a/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp @@ -13,6 +13,39 @@ namespace ErrorCodes { +} + + +namespace +{ + +/// Grid symbols are used for printing grid borders in a terminal. +/// Defaults values are UTF-8. +struct GridSymbols +{ + const char * left_top_corner = "┌"; + const char * right_top_corner = "┐"; + const char * left_bottom_corner = "└"; + const char * right_bottom_corner = "┘"; + const char * top_separator = "┬"; + const char * bottom_separator = "┴"; + const char * dash = "─"; + const char * bar = "│"; +}; + +GridSymbols utf8_grid_symbols; + +GridSymbols ascii_grid_symbols { + "+", + "+", + "+", + "+", + "+", + "+", + "-", + "|" +}; + } void PrettyCompactBlockOutputFormat::writeHeader( @@ -20,19 +53,28 @@ void PrettyCompactBlockOutputFormat::writeHeader( const Widths & max_widths, const Widths & name_widths) { + const GridSymbols & gridSymbols = format_settings.pretty.charset == FormatSettings::Pretty::Charset::UTF8 ? + utf8_grid_symbols : + ascii_grid_symbols; + /// Names - writeCString("┌─", out); + writeCString(gridSymbols.left_top_corner, out); + writeCString(gridSymbols.dash, out); for (size_t i = 0; i < max_widths.size(); ++i) { if (i != 0) - writeCString("─┬─", out); + { + writeCString(gridSymbols.dash, out); + writeCString(gridSymbols.top_separator, out); + writeCString(gridSymbols.dash, out); + } const ColumnWithTypeAndName & col = block.getByPosition(i); if (col.type->shouldAlignRightInPrettyFormats()) { for (size_t k = 0; k < max_widths[i] - name_widths[i]; ++k) - writeCString("─", out); + writeCString(gridSymbols.dash, out); if (format_settings.pretty.color) writeCString("\033[1m", out); @@ -49,27 +91,32 @@ void PrettyCompactBlockOutputFormat::writeHeader( writeCString("\033[0m", out); for (size_t k = 0; k < max_widths[i] - name_widths[i]; ++k) - writeCString("─", out); + writeCString(gridSymbols.dash, out); } } - writeCString("─┐\n", out); + writeCString(gridSymbols.dash, out); + writeCString(gridSymbols.right_top_corner, out); + writeCString("\n", out); } void PrettyCompactBlockOutputFormat::writeBottom(const Widths & max_widths) { + const GridSymbols & gridSymbols = format_settings.pretty.charset == FormatSettings::Pretty::Charset::UTF8 ? + utf8_grid_symbols : + ascii_grid_symbols; /// Create delimiters std::stringstream bottom_separator; - bottom_separator << "└"; + bottom_separator << gridSymbols.left_bottom_corner; for (size_t i = 0; i < max_widths.size(); ++i) { if (i != 0) - bottom_separator << "┴"; + bottom_separator << gridSymbols.bottom_separator; for (size_t j = 0; j < max_widths[i] + 2; ++j) - bottom_separator << "─"; + bottom_separator << gridSymbols.dash; } - bottom_separator << "┘\n"; + bottom_separator << gridSymbols.right_bottom_corner << "\n"; writeString(bottom_separator.str(), out); } @@ -81,21 +128,26 @@ void PrettyCompactBlockOutputFormat::writeRow( const WidthsPerColumn & widths, const Widths & max_widths) { + const GridSymbols & grid_symbols = format_settings.pretty.charset == FormatSettings::Pretty::Charset::UTF8 ? + utf8_grid_symbols : + ascii_grid_symbols; + size_t num_columns = max_widths.size(); - writeCString("│", out); + writeCString(grid_symbols.bar, out); for (size_t j = 0; j < num_columns; ++j) { if (j != 0) - writeCString("│", out); + writeCString(grid_symbols.bar, out); const auto & type = *header.getByPosition(j).type; const auto & cur_widths = widths[j].empty() ? max_widths[j] : widths[j][row_num]; writeValueWithPadding(*columns[j], type, row_num, cur_widths, max_widths[j]); } - writeCString("│\n", out); + writeCString(grid_symbols.bar, out); + writeCString("\n", out); } void PrettyCompactBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind) diff --git a/tests/queries/0_stateless/00405_pretty_formats.reference b/tests/queries/0_stateless/00405_pretty_formats.reference index ef3184f2837..896ce48fe8f 100644 --- a/tests/queries/0_stateless/00405_pretty_formats.reference +++ b/tests/queries/0_stateless/00405_pretty_formats.reference @@ -211,3 +211,96 @@ Showed first 6. 5 5 (5,'5') 2 Showed first 6. ++-------+-------+---------+-----------------+ +| hello | world | tuple  | sometimes_nulls | ++-------+-------+---------+-----------------+ +| 0 | 0 | (0,'0') | null | ++-------+-------+---------+-----------------+ +| 1 | 1 | (1,'1') | 1 | ++-------+-------+---------+-----------------+ +| 2 | 2 | (2,'2') | 2 | ++-------+-------+---------+-----------------+ +| 3 | 3 | (3,'3') | null | ++-------+-------+---------+-----------------+ +| 4 | 4 | (4,'4') | 1 | ++-------+-------+---------+-----------------+ ++-------+-------+---------+-----------------+ +| hello | world | tuple  | sometimes_nulls | ++-------+-------+---------+-----------------+ +| 5 | 5 | (5,'5') | 2 | ++-------+-------+---------+-----------------+ + Showed first 6. ++-hello-+-world-+-tuple---+-sometimes_nulls-+ +| 0 | 0 | (0,'0') | null | +| 1 | 1 | (1,'1') | 1 | +| 2 | 2 | (2,'2') | 2 | +| 3 | 3 | (3,'3') | null | +| 4 | 4 | (4,'4') | 1 | ++-------+-------+---------+-----------------+ ++-hello-+-world-+-tuple---+-sometimes_nulls-+ +| 5 | 5 | (5,'5') | 2 | ++-------+-------+---------+-----------------+ + Showed first 6. + hello world tuple sometimes_nulls + + 0 0 (0,'0') null + 1 1 (1,'1') 1 + 2 2 (2,'2') 2 + 3 3 (3,'3') null + 4 4 (4,'4') 1 + hello world tuple sometimes_nulls + + 5 5 (5,'5') 2 + +Showed first 6. ++-hello-+-world-+-tuple---+-sometimes_nulls-+ +| 0 | 0 | (0,'0') | null | +| 1 | 1 | (1,'1') | 1 | +| 2 | 2 | (2,'2') | 2 | +| 3 | 3 | (3,'3') | null | +| 4 | 4 | (4,'4') | 1 | +| 5 | 5 | (5,'5') | 2 | ++-------+-------+---------+-----------------+ + Showed first 6. ++-------+-------+---------+-----------------+ +| hello | world | tuple | sometimes_nulls | ++-------+-------+---------+-----------------+ +| 0 | 0 | (0,'0') | null | ++-------+-------+---------+-----------------+ +| 1 | 1 | (1,'1') | 1 | ++-------+-------+---------+-----------------+ +| 2 | 2 | (2,'2') | 2 | ++-------+-------+---------+-----------------+ +| 3 | 3 | (3,'3') | null | ++-------+-------+---------+-----------------+ +| 4 | 4 | (4,'4') | 1 | ++-------+-------+---------+-----------------+ ++-------+-------+---------+-----------------+ +| hello | world | tuple | sometimes_nulls | ++-------+-------+---------+-----------------+ +| 5 | 5 | (5,'5') | 2 | ++-------+-------+---------+-----------------+ + Showed first 6. ++-hello-+-world-+-tuple---+-sometimes_nulls-+ +| 0 | 0 | (0,'0') | null | +| 1 | 1 | (1,'1') | 1 | +| 2 | 2 | (2,'2') | 2 | +| 3 | 3 | (3,'3') | null | +| 4 | 4 | (4,'4') | 1 | ++-------+-------+---------+-----------------+ ++-hello-+-world-+-tuple---+-sometimes_nulls-+ +| 5 | 5 | (5,'5') | 2 | ++-------+-------+---------+-----------------+ + Showed first 6. + hello world tuple sometimes_nulls + + 0 0 (0,'0') null + 1 1 (1,'1') 1 + 2 2 (2,'2') 2 + 3 3 (3,'3') null + 4 4 (4,'4') 1 + hello world tuple sometimes_nulls + + 5 5 (5,'5') 2 + +Showed first 6. diff --git a/tests/queries/0_stateless/00405_pretty_formats.sql b/tests/queries/0_stateless/00405_pretty_formats.sql index dd733f9ecde..3c8af776278 100644 --- a/tests/queries/0_stateless/00405_pretty_formats.sql +++ b/tests/queries/0_stateless/00405_pretty_formats.sql @@ -15,3 +15,14 @@ SELECT number AS hello, toString(number) AS world, (hello, world) AS tuple, null SELECT number AS hello, toString(number) AS world, (hello, world) AS tuple, nullIf(hello % 3, 0) AS sometimes_nulls FROM system.numbers LIMIT 10 SETTINGS max_block_size = 5 FORMAT PrettyNoEscapes; SELECT number AS hello, toString(number) AS world, (hello, world) AS tuple, nullIf(hello % 3, 0) AS sometimes_nulls FROM system.numbers LIMIT 10 SETTINGS max_block_size = 5 FORMAT PrettyCompactNoEscapes; SELECT number AS hello, toString(number) AS world, (hello, world) AS tuple, nullIf(hello % 3, 0) AS sometimes_nulls FROM system.numbers LIMIT 10 SETTINGS max_block_size = 5 FORMAT PrettySpaceNoEscapes; + + +SET output_format_pretty_grid_charset = 'ASCII'; + +SELECT number AS hello, toString(number) AS world, (hello, world) AS tuple, nullIf(hello % 3, 0) AS sometimes_nulls FROM system.numbers LIMIT 10 SETTINGS max_block_size = 5 FORMAT Pretty; +SELECT number AS hello, toString(number) AS world, (hello, world) AS tuple, nullIf(hello % 3, 0) AS sometimes_nulls FROM system.numbers LIMIT 10 SETTINGS max_block_size = 5 FORMAT PrettyCompact; +SELECT number AS hello, toString(number) AS world, (hello, world) AS tuple, nullIf(hello % 3, 0) AS sometimes_nulls FROM system.numbers LIMIT 10 SETTINGS max_block_size = 5 FORMAT PrettySpace; +SELECT number AS hello, toString(number) AS world, (hello, world) AS tuple, nullIf(hello % 3, 0) AS sometimes_nulls FROM system.numbers LIMIT 10 SETTINGS max_block_size = 5 FORMAT PrettyCompactMonoBlock; +SELECT number AS hello, toString(number) AS world, (hello, world) AS tuple, nullIf(hello % 3, 0) AS sometimes_nulls FROM system.numbers LIMIT 10 SETTINGS max_block_size = 5 FORMAT PrettyNoEscapes; +SELECT number AS hello, toString(number) AS world, (hello, world) AS tuple, nullIf(hello % 3, 0) AS sometimes_nulls FROM system.numbers LIMIT 10 SETTINGS max_block_size = 5 FORMAT PrettyCompactNoEscapes; +SELECT number AS hello, toString(number) AS world, (hello, world) AS tuple, nullIf(hello % 3, 0) AS sometimes_nulls FROM system.numbers LIMIT 10 SETTINGS max_block_size = 5 FORMAT PrettySpaceNoEscapes;