1#include <sys/ioctl.h>
2#include <port/unistd.h>
3#include <Processors/Formats/Impl/PrettyBlockOutputFormat.h>
4#include <Formats/FormatFactory.h>
5#include <IO/WriteBuffer.h>
6#include <IO/WriteHelpers.h>
7#include <IO/WriteBufferFromString.h>
8#include <Common/PODArray.h>
9#include <Common/UTF8Helpers.h>
10
11namespace DB
12{
13
14namespace ErrorCodes
15{
16 extern const int ILLEGAL_COLUMN;
17}
18
19
20PrettyBlockOutputFormat::PrettyBlockOutputFormat(
21 WriteBuffer & out_, const Block & header_, const FormatSettings & format_settings_)
22 : IOutputFormat(header_, out_), format_settings(format_settings_)
23{
24 struct winsize w;
25 if (0 == ioctl(STDOUT_FILENO, TIOCGWINSZ, &w))
26 terminal_width = w.ws_col;
27}
28
29
30/// Evaluate the visible width of the values and column names.
31/// Note that number of code points is just a rough approximation of visible string width.
32void PrettyBlockOutputFormat::calculateWidths(
33 const Block & header, const Chunk & chunk,
34 WidthsPerColumn & widths, Widths & max_widths, Widths & name_widths)
35{
36 size_t num_rows = chunk.getNumRows();
37 size_t num_columns = chunk.getNumColumns();
38 auto & columns = chunk.getColumns();
39
40 widths.resize(num_columns);
41 max_widths.resize_fill(num_columns);
42 name_widths.resize(num_columns);
43
44 /// Calculate widths of all values.
45 String serialized_value;
46 size_t prefix = 2; // Tab character adjustment
47 for (size_t i = 0; i < num_columns; ++i)
48 {
49 auto & elem = header.getByPosition(i);
50 auto & column = columns[i];
51
52 widths[i].resize(num_rows);
53
54 for (size_t j = 0; j < num_rows; ++j)
55 {
56 {
57 WriteBufferFromString out_(serialized_value);
58 elem.type->serializeAsText(*column, j, out_, format_settings);
59 }
60
61 widths[i][j] = std::min<UInt64>(format_settings.pretty.max_column_pad_width,
62 UTF8::computeWidth(reinterpret_cast<const UInt8 *>(serialized_value.data()), serialized_value.size(), prefix));
63 max_widths[i] = std::max(max_widths[i], widths[i][j]);
64 }
65
66 /// And also calculate widths for names of columns.
67 {
68 // name string doesn't contain Tab, no need to pass `prefix`
69 name_widths[i] = std::min<UInt64>(format_settings.pretty.max_column_pad_width,
70 UTF8::computeWidth(reinterpret_cast<const UInt8 *>(elem.name.data()), elem.name.size()));
71 max_widths[i] = std::max(max_widths[i], name_widths[i]);
72 }
73 prefix += max_widths[i] + 3;
74 }
75}
76
77
78void PrettyBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind)
79{
80 UInt64 max_rows = format_settings.pretty.max_rows;
81
82 if (total_rows >= max_rows)
83 {
84 total_rows += chunk.getNumRows();
85 return;
86 }
87
88 auto num_rows = chunk.getNumRows();
89 auto num_columns = chunk.getNumColumns();
90 auto & columns = chunk.getColumns();
91 auto & header = getPort(port_kind).getHeader();
92
93 WidthsPerColumn widths;
94 Widths max_widths;
95 Widths name_widths;
96 calculateWidths(header, chunk, widths, max_widths, name_widths);
97
98 /// Create separators
99 std::stringstream top_separator;
100 std::stringstream middle_names_separator;
101 std::stringstream middle_values_separator;
102 std::stringstream bottom_separator;
103
104 top_separator << "┏";
105 middle_names_separator << "┡";
106 middle_values_separator << "├";
107 bottom_separator << "└";
108 for (size_t i = 0; i < num_columns; ++i)
109 {
110 if (i != 0)
111 {
112 top_separator << "┳";
113 middle_names_separator << "╇";
114 middle_values_separator << "┼";
115 bottom_separator << "┴";
116 }
117
118 for (size_t j = 0; j < max_widths[i] + 2; ++j)
119 {
120 top_separator << "━";
121 middle_names_separator << "━";
122 middle_values_separator << "─";
123 bottom_separator << "─";
124 }
125 }
126 top_separator << "┓\n";
127 middle_names_separator << "┩\n";
128 middle_values_separator << "┤\n";
129 bottom_separator << "┘\n";
130
131 std::string top_separator_s = top_separator.str();
132 std::string middle_names_separator_s = middle_names_separator.str();
133 std::string middle_values_separator_s = middle_values_separator.str();
134 std::string bottom_separator_s = bottom_separator.str();
135
136 /// Output the block
137 writeString(top_separator_s, out);
138
139 /// Names
140 writeCString("┃ ", out);
141 for (size_t i = 0; i < num_columns; ++i)
142 {
143 if (i != 0)
144 writeCString(" ┃ ", out);
145
146 auto & col = header.getByPosition(i);
147
148 if (format_settings.pretty.color)
149 writeCString("\033[1m", out);
150
151 if (col.type->shouldAlignRightInPrettyFormats())
152 {
153 for (size_t k = 0; k < max_widths[i] - name_widths[i]; ++k)
154 writeChar(' ', out);
155
156 writeString(col.name, out);
157 }
158 else
159 {
160 writeString(col.name, out);
161
162 for (size_t k = 0; k < max_widths[i] - name_widths[i]; ++k)
163 writeChar(' ', out);
164 }
165
166 if (format_settings.pretty.color)
167 writeCString("\033[0m", out);
168 }
169 writeCString(" ┃\n", out);
170
171 writeString(middle_names_separator_s, out);
172
173 for (size_t i = 0; i < num_rows && total_rows + i < max_rows; ++i)
174 {
175 if (i != 0)
176 writeString(middle_values_separator_s, out);
177
178 writeCString("│ ", out);
179
180 for (size_t j = 0; j < num_columns; ++j)
181 {
182 if (j != 0)
183 writeCString(" │ ", out);
184
185 auto & type = *header.getByPosition(j).type;
186 writeValueWithPadding(*columns[j], type, i, widths[j].empty() ? max_widths[j] : widths[j][i], max_widths[j]);
187 }
188
189 writeCString(" │\n", out);
190 }
191
192 writeString(bottom_separator_s, out);
193
194 total_rows += num_rows;
195}
196
197
198void PrettyBlockOutputFormat::writeValueWithPadding(
199 const IColumn & column, const IDataType & type, size_t row_num, size_t value_width, size_t pad_to_width)
200{
201 auto writePadding = [&]()
202 {
203 for (size_t k = 0; k < pad_to_width - value_width; ++k)
204 writeChar(' ', out);
205 };
206
207 if (type.shouldAlignRightInPrettyFormats())
208 {
209 writePadding();
210 type.serializeAsText(column, row_num, out, format_settings);
211 }
212 else
213 {
214 type.serializeAsText(column, row_num, out, format_settings);
215 writePadding();
216 }
217}
218
219
220void PrettyBlockOutputFormat::consume(Chunk chunk)
221{
222 write(chunk, PortKind::Main);
223}
224
225void PrettyBlockOutputFormat::consumeTotals(Chunk chunk)
226{
227 total_rows = 0;
228 writeSuffixIfNot();
229 writeCString("\nExtremes:\n", out);
230 write(chunk, PortKind::Totals);
231}
232
233void PrettyBlockOutputFormat::consumeExtremes(Chunk chunk)
234{
235 total_rows = 0;
236 writeSuffixIfNot();
237 writeCString("\nTotals:\n", out);
238 write(chunk, PortKind::Extremes);
239}
240
241
242void PrettyBlockOutputFormat::writeSuffix()
243{
244 if (total_rows >= format_settings.pretty.max_rows)
245 {
246 writeCString(" Showed first ", out);
247 writeIntText(format_settings.pretty.max_rows, out);
248 writeCString(".\n", out);
249 }
250}
251
252void PrettyBlockOutputFormat::finalize()
253{
254 writeSuffixIfNot();
255}
256
257
258void registerOutputFormatProcessorPretty(FormatFactory & factory)
259{
260 factory.registerOutputFormatProcessor("Pretty", [](
261 WriteBuffer & buf,
262 const Block & sample,
263 FormatFactory::WriteCallback,
264 const FormatSettings & format_settings)
265 {
266 return std::make_shared<PrettyBlockOutputFormat>(buf, sample, format_settings);
267 });
268
269 factory.registerOutputFormatProcessor("PrettyNoEscapes", [](
270 WriteBuffer & buf,
271 const Block & sample,
272 FormatFactory::WriteCallback,
273 const FormatSettings & format_settings)
274 {
275 FormatSettings changed_settings = format_settings;
276 changed_settings.pretty.color = false;
277 return std::make_shared<PrettyBlockOutputFormat>(buf, sample, changed_settings);
278 });
279}
280
281}
282