Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Underscore digit groups in Pretty formats #61802

Merged
merged 7 commits into from Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Core/Settings.h
Expand Up @@ -1139,6 +1139,7 @@ class IColumn;
M(Bool, output_format_enable_streaming, false, "Enable streaming in output formats that support it.", 0) \
M(Bool, output_format_write_statistics, true, "Write statistics about read rows, bytes, time elapsed in suitable output formats.", 0) \
M(Bool, output_format_pretty_row_numbers, false, "Add row numbers before each row for pretty output format", 0) \
M(Bool, output_format_pretty_highlight_digit_groups, true, "If enabled and if output is a terminal, highlight every digit corresponding to the number of thousands, millions, etc. with underline.", 0) \
M(UInt64, output_format_pretty_single_large_number_tip_threshold, 1'000'000, "Print a readable number tip on the right side of the table if the block consists of a single number which exceeds this value (except 0)", 0) \
M(Bool, insert_distributed_one_random_shard, false, "If setting is enabled, inserting into distributed table will choose a random shard to write when there is no sharding key", 0) \
\
Expand Down
1 change: 1 addition & 0 deletions src/Core/SettingsChangesHistory.h
Expand Up @@ -105,6 +105,7 @@ static std::map<ClickHouseVersion, SettingsChangesHistory::SettingsChanges> sett
{"keeper_retry_max_backoff_ms", 5000, 5000, "Max backoff timeout for general keeper operations"},
{"s3queue_allow_experimental_sharded_mode", false, false, "Enable experimental sharded mode of S3Queue table engine. It is experimental because it will be rewritten"},
{"merge_tree_read_split_ranges_into_intersecting_and_non_intersecting_injection_probability", 0.0, 0.0, "For testing of `PartsSplitter` - split read ranges into intersecting and non intersecting every time you read from MergeTree with the specified probability."},
{"output_format_pretty_highlight_digit_groups", false, true, "If enabled and if output is a terminal, highlight every digit corresponding to the number of thousands, millions, etc. with underline."},
}},
{"24.2", {{"allow_suspicious_variant_types", true, false, "Don't allow creating Variant type with suspicious variants by default"},
{"validate_experimental_and_suspicious_types_inside_nested_types", false, true, "Validate usage of experimental and suspicious types inside nested types"},
Expand Down
1 change: 1 addition & 0 deletions src/Formats/FormatFactory.cpp
Expand Up @@ -167,6 +167,7 @@ FormatSettings getFormatSettings(const ContextPtr & context, const Settings & se
format_settings.pretty.max_column_pad_width = settings.output_format_pretty_max_column_pad_width;
format_settings.pretty.max_rows = settings.output_format_pretty_max_rows;
format_settings.pretty.max_value_width = settings.output_format_pretty_max_value_width;
format_settings.pretty.highlight_digit_groups = settings.output_format_pretty_highlight_digit_groups;
format_settings.pretty.output_format_pretty_row_numbers = settings.output_format_pretty_row_numbers;
format_settings.pretty.output_format_pretty_single_large_number_tip_threshold = settings.output_format_pretty_single_large_number_tip_threshold;
format_settings.protobuf.input_flatten_google_wrappers = settings.input_format_protobuf_flatten_google_wrappers;
Expand Down
1 change: 1 addition & 0 deletions src/Formats/FormatSettings.h
Expand Up @@ -275,6 +275,7 @@ struct FormatSettings
UInt64 max_rows = 10000;
UInt64 max_column_pad_width = 250;
UInt64 max_value_width = 10000;
bool highlight_digit_groups = true;
SettingFieldUInt64Auto color{"auto"};

bool output_format_pretty_row_numbers = false;
Expand Down
74 changes: 72 additions & 2 deletions src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp
Expand Up @@ -303,7 +303,7 @@ void PrettyBlockOutputFormat::writeChunk(const Chunk & chunk, PortKind port_kind
const auto & type = *header.getByPosition(j).type;
writeValueWithPadding(*columns[j], *serializations[j], i,
widths[j].empty() ? max_widths[j] : widths[j][i],
max_widths[j], type.shouldAlignRightInPrettyFormats());
max_widths[j], type.shouldAlignRightInPrettyFormats(), isNumber(type));
}

writeCString(grid_symbols.bar, out);
Expand All @@ -322,9 +322,75 @@ void PrettyBlockOutputFormat::writeChunk(const Chunk & chunk, PortKind port_kind
}


static String highlightDigitGroups(String source)
{
if (source.size() <= 4)
return source;

bool is_regular_number = true;
size_t num_digits_before_decimal = 0;
for (auto c : source)
{
if (c == '-' || c == ' ')
continue;
if (c == '.')
break;
if (c >= '0' && c <= '9')
{
++num_digits_before_decimal;
}
else
{
is_regular_number = false;
break;
}
}

if (!is_regular_number || num_digits_before_decimal <= 4)
return source;

String result;
size_t size = source.size();
result.reserve(2 * size);

bool before_decimal = true;
size_t digit_num = 0;
for (size_t i = 0; i < size; ++i)
{
auto c = source[i];
if (before_decimal && c >= '0' && c <= '9')
{
++digit_num;
size_t offset = num_digits_before_decimal - digit_num;
if (offset && offset % 3 == 0)
{
result += "\033[4m";
result += c;
result += "\033[0m";
}
else
{
result += c;
}
}
else if (c == '.')
{
before_decimal = false;
result += c;
}
else
{
result += c;
}
}

return result;
}


void PrettyBlockOutputFormat::writeValueWithPadding(
const IColumn & column, const ISerialization & serialization, size_t row_num,
size_t value_width, size_t pad_to_width, bool align_right)
size_t value_width, size_t pad_to_width, bool align_right, bool is_number)
{
String serialized_value = " ";
{
Expand Down Expand Up @@ -359,6 +425,10 @@ void PrettyBlockOutputFormat::writeValueWithPadding(
writeChar(' ', out);
};

/// Highlight groups of thousands.
if (color && is_number && format_settings.pretty.highlight_digit_groups)
serialized_value = highlightDigitGroups(serialized_value);

if (align_right)
{
write_padding();
Expand Down
2 changes: 1 addition & 1 deletion src/Processors/Formats/Impl/PrettyBlockOutputFormat.h
Expand Up @@ -48,7 +48,7 @@ class PrettyBlockOutputFormat : public IOutputFormat

void writeValueWithPadding(
const IColumn & column, const ISerialization & serialization, size_t row_num,
size_t value_width, size_t pad_to_width, bool align_right);
size_t value_width, size_t pad_to_width, bool align_right, bool is_number);

void resetFormatterImpl() override
{
Expand Down
Expand Up @@ -169,7 +169,7 @@ void PrettyCompactBlockOutputFormat::writeRow(

const auto & type = *header.getByPosition(j).type;
const auto & cur_widths = widths[j].empty() ? max_widths[j] : widths[j][row_num];
writeValueWithPadding(*columns[j], *serializations[j], row_num, cur_widths, max_widths[j], type.shouldAlignRightInPrettyFormats());
writeValueWithPadding(*columns[j], *serializations[j], row_num, cur_widths, max_widths[j], type.shouldAlignRightInPrettyFormats(), isNumber(type));
}

writeCString(grid_symbols.bar, out);
Expand Down
Expand Up @@ -84,7 +84,7 @@ void PrettySpaceBlockOutputFormat::writeChunk(const Chunk & chunk, PortKind port
const auto & type = *header.getByPosition(column).type;
auto & cur_width = widths[column].empty() ? max_widths[column] : widths[column][row];
writeValueWithPadding(
*columns[column], *serializations[column], row, cur_width, max_widths[column], type.shouldAlignRightInPrettyFormats());
*columns[column], *serializations[column], row, cur_width, max_widths[column], type.shouldAlignRightInPrettyFormats(), isNumber(type));
}

writeReadableNumberTip(chunk);
Expand Down
@@ -1,4 +1,4 @@
SET output_format_pretty_color=1;
SET output_format_pretty_color=1, output_format_pretty_highlight_digit_groups=0;
SELECT toUInt64(round(exp10(number))) AS x, toString(x) AS s FROM system.numbers LIMIT 10 FORMAT Pretty;
SELECT toUInt64(round(exp10(number))) AS x, toString(x) AS s FROM system.numbers LIMIT 10 FORMAT PrettyCompact;
SELECT toUInt64(round(exp10(number))) AS x, toString(x) AS s FROM system.numbers LIMIT 10 FORMAT PrettySpace;
Expand Down
68 changes: 68 additions & 0 deletions tests/queries/0_stateless/03022_highlight_digit_groups.reference
@@ -0,0 +1,68 @@
multiply(exp10(number), if(modulo(number, 2), 1, -1))

-1
10
-100
1000
-10000
100000
-1000000
10000000
-100000000
1000000000
-10000000000
100000000000
-1000000000000
10000000000000
-100000000000000
1000000000000000
-10000000000000000
100000000000000000
-1000000000000000000
10000000000000000000
-100000000000000000000
1e21
-1e22
1e23
-1e24
1e25
-1e26
1e27
-1e28
1e29
exp10(number)

1
10
100
1000
10000
100000
1000000
10000000
100000000
1000000000
exp10(number)

1
10
100
1000
10000
100000
1000000
10000000
100000000
1000000000
plus(exp10(number), exp10(negate(number)))

2
10.1
100.01
1000.001
10000.0001
100000.00001
1000000.000001
10000000.0000001
100000000.00000001
1000000000
6 changes: 6 additions & 0 deletions tests/queries/0_stateless/03022_highlight_digit_groups.sql
@@ -0,0 +1,6 @@
SELECT exp10(number) * (number % 2 ? 1 : -1) FROM numbers(30) FORMAT PrettySpace SETTINGS output_format_pretty_color = 1;

SELECT exp10(number) FROM numbers(10) FORMAT PrettySpace SETTINGS output_format_pretty_color = 1, output_format_pretty_highlight_digit_groups = 0;
SELECT exp10(number) FROM numbers(10) FORMAT PrettySpace;

SELECT exp10(number) + exp10(-number) FROM numbers(10) FORMAT PrettySpace SETTINGS output_format_pretty_color = 1;