1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing,
12 // software distributed under the License is distributed on an
13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, either express or implied. See the License for the
15 // specific language governing permissions and limitations
16 // under the License.
17
18 #include <algorithm>
19 #include <chrono>
20 #include <cstddef>
21 #include <cstdint>
22 #include <iostream>
23 #include <memory>
24 #include <sstream> // IWYU pragma: keep
25 #include <string>
26 #include <type_traits>
27 #include <vector>
28
29 #include "arrow/array.h"
30 #include "arrow/pretty_print.h"
31 #include "arrow/record_batch.h"
32 #include "arrow/status.h"
33 #include "arrow/table.h"
34 #include "arrow/type.h"
35 #include "arrow/type_traits.h"
36 #include "arrow/util/checked_cast.h"
37 #include "arrow/util/int_util.h"
38 #include "arrow/util/key_value_metadata.h"
39 #include "arrow/util/string.h"
40 #include "arrow/vendored/datetime.h"
41 #include "arrow/visitor_inline.h"
42
43 namespace arrow {
44
45 using internal::checked_cast;
46
47 class PrettyPrinter {
48 public:
PrettyPrinter(const PrettyPrintOptions & options,std::ostream * sink)49 PrettyPrinter(const PrettyPrintOptions& options, std::ostream* sink)
50 : options_(options), indent_(options.indent), sink_(sink) {}
51
52 void Write(const char* data);
53 void Write(const std::string& data);
54 void WriteIndented(const char* data);
55 void WriteIndented(const std::string& data);
56 void Newline();
57 void Indent();
58 void OpenArray(const Array& array);
59 void CloseArray(const Array& array);
60
Flush()61 void Flush() { (*sink_) << std::flush; }
62
63 protected:
64 const PrettyPrintOptions& options_;
65 int indent_;
66 std::ostream* sink_;
67 };
68
OpenArray(const Array & array)69 void PrettyPrinter::OpenArray(const Array& array) {
70 Indent();
71 (*sink_) << "[";
72 if (array.length() > 0) {
73 (*sink_) << "\n";
74 indent_ += options_.indent_size;
75 }
76 }
77
CloseArray(const Array & array)78 void PrettyPrinter::CloseArray(const Array& array) {
79 if (array.length() > 0) {
80 indent_ -= options_.indent_size;
81 Indent();
82 }
83 (*sink_) << "]";
84 }
85
Write(const char * data)86 void PrettyPrinter::Write(const char* data) { (*sink_) << data; }
Write(const std::string & data)87 void PrettyPrinter::Write(const std::string& data) { (*sink_) << data; }
88
WriteIndented(const char * data)89 void PrettyPrinter::WriteIndented(const char* data) {
90 Indent();
91 Write(data);
92 }
93
WriteIndented(const std::string & data)94 void PrettyPrinter::WriteIndented(const std::string& data) {
95 Indent();
96 Write(data);
97 }
98
Newline()99 void PrettyPrinter::Newline() {
100 if (options_.skip_new_lines) {
101 return;
102 }
103 (*sink_) << "\n";
104 Indent();
105 }
106
Indent()107 void PrettyPrinter::Indent() {
108 for (int i = 0; i < indent_; ++i) {
109 (*sink_) << " ";
110 }
111 }
112
113 class ArrayPrinter : public PrettyPrinter {
114 public:
ArrayPrinter(const PrettyPrintOptions & options,std::ostream * sink)115 ArrayPrinter(const PrettyPrintOptions& options, std::ostream* sink)
116 : PrettyPrinter(options, sink) {}
117
118 template <typename FormatFunction>
WriteValues(const Array & array,FormatFunction && func)119 void WriteValues(const Array& array, FormatFunction&& func) {
120 bool skip_comma = true;
121 for (int64_t i = 0; i < array.length(); ++i) {
122 if (skip_comma) {
123 skip_comma = false;
124 } else {
125 (*sink_) << ",\n";
126 }
127 Indent();
128 if ((i >= options_.window) && (i < (array.length() - options_.window))) {
129 (*sink_) << "...\n";
130 i = array.length() - options_.window - 1;
131 skip_comma = true;
132 } else if (array.IsNull(i)) {
133 (*sink_) << options_.null_rep;
134 } else {
135 func(i);
136 }
137 }
138 (*sink_) << "\n";
139 }
140
WriteDataValues(const BooleanArray & array)141 Status WriteDataValues(const BooleanArray& array) {
142 WriteValues(array, [&](int64_t i) { Write(array.Value(i) ? "true" : "false"); });
143 return Status::OK();
144 }
145
146 template <typename T>
WriteDataValues(const T & array)147 enable_if_integer<typename T::TypeClass, Status> WriteDataValues(const T& array) {
148 const auto data = array.raw_values();
149 // Need to upcast integers to avoid selecting operator<<(char)
150 WriteValues(array, [&](int64_t i) { (*sink_) << internal::UpcastInt(data[i]); });
151 return Status::OK();
152 }
153
154 template <typename T>
WriteDataValues(const T & array)155 enable_if_floating_point<typename T::TypeClass, Status> WriteDataValues(
156 const T& array) {
157 const auto data = array.raw_values();
158 WriteValues(array, [&](int64_t i) { (*sink_) << data[i]; });
159 return Status::OK();
160 }
161
162 template <typename T>
WriteDataValues(const T & array)163 enable_if_date<typename T::TypeClass, Status> WriteDataValues(const T& array) {
164 const auto data = array.raw_values();
165 using unit = typename std::conditional<std::is_same<T, Date32Array>::value,
166 arrow_vendored::date::days,
167 std::chrono::milliseconds>::type;
168 WriteValues(array, [&](int64_t i) { FormatDateTime<unit>("%F", data[i], true); });
169 return Status::OK();
170 }
171
172 template <typename T>
WriteDataValues(const T & array)173 enable_if_time<typename T::TypeClass, Status> WriteDataValues(const T& array) {
174 const auto data = array.raw_values();
175 const auto type = static_cast<const TimeType*>(array.type().get());
176 WriteValues(array,
177 [&](int64_t i) { FormatDateTime(type->unit(), "%T", data[i], false); });
178 return Status::OK();
179 }
180
WriteDataValues(const TimestampArray & array)181 Status WriteDataValues(const TimestampArray& array) {
182 const int64_t* data = array.raw_values();
183 const auto type = static_cast<const TimestampType*>(array.type().get());
184 WriteValues(array,
185 [&](int64_t i) { FormatDateTime(type->unit(), "%F %T", data[i], true); });
186 return Status::OK();
187 }
188
189 template <typename T>
WriteDataValues(const T & array)190 enable_if_duration<typename T::TypeClass, Status> WriteDataValues(const T& array) {
191 const auto data = array.raw_values();
192 WriteValues(array, [&](int64_t i) { (*sink_) << data[i]; });
193 return Status::OK();
194 }
195
WriteDataValues(const DayTimeIntervalArray & array)196 Status WriteDataValues(const DayTimeIntervalArray& array) {
197 WriteValues(array, [&](int64_t i) {
198 auto day_millis = array.GetValue(i);
199 (*sink_) << day_millis.days << "d" << day_millis.milliseconds << "ms";
200 });
201 return Status::OK();
202 }
203
WriteDataValues(const MonthIntervalArray & array)204 Status WriteDataValues(const MonthIntervalArray& array) {
205 const auto data = array.raw_values();
206 WriteValues(array, [&](int64_t i) { (*sink_) << data[i]; });
207 return Status::OK();
208 }
209
210 template <typename T>
WriteDataValues(const T & array)211 enable_if_string_like<typename T::TypeClass, Status> WriteDataValues(const T& array) {
212 WriteValues(array, [&](int64_t i) { (*sink_) << "\"" << array.GetView(i) << "\""; });
213 return Status::OK();
214 }
215
216 // Binary
217 template <typename T>
WriteDataValues(const T & array)218 enable_if_binary_like<typename T::TypeClass, Status> WriteDataValues(const T& array) {
219 WriteValues(array, [&](int64_t i) { (*sink_) << HexEncode(array.GetView(i)); });
220 return Status::OK();
221 }
222
WriteDataValues(const Decimal128Array & array)223 Status WriteDataValues(const Decimal128Array& array) {
224 WriteValues(array, [&](int64_t i) { (*sink_) << array.FormatValue(i); });
225 return Status::OK();
226 }
227
228 template <typename T>
WriteDataValues(const T & array)229 enable_if_list_like<typename T::TypeClass, Status> WriteDataValues(const T& array) {
230 bool skip_comma = true;
231 for (int64_t i = 0; i < array.length(); ++i) {
232 if (skip_comma) {
233 skip_comma = false;
234 } else {
235 (*sink_) << ",\n";
236 }
237 if ((i >= options_.window) && (i < (array.length() - options_.window))) {
238 Indent();
239 (*sink_) << "...\n";
240 i = array.length() - options_.window - 1;
241 skip_comma = true;
242 } else if (array.IsNull(i)) {
243 Indent();
244 (*sink_) << options_.null_rep;
245 } else {
246 std::shared_ptr<Array> slice =
247 array.values()->Slice(array.value_offset(i), array.value_length(i));
248 RETURN_NOT_OK(PrettyPrint(*slice, {indent_, options_.window}, sink_));
249 }
250 }
251 (*sink_) << "\n";
252 return Status::OK();
253 }
254
WriteDataValues(const MapArray & array)255 Status WriteDataValues(const MapArray& array) {
256 bool skip_comma = true;
257 for (int64_t i = 0; i < array.length(); ++i) {
258 if (skip_comma) {
259 skip_comma = false;
260 } else {
261 (*sink_) << ",\n";
262 }
263 if ((i >= options_.window) && (i < (array.length() - options_.window))) {
264 Indent();
265 (*sink_) << "...\n";
266 i = array.length() - options_.window - 1;
267 skip_comma = true;
268 } else if (array.IsNull(i)) {
269 Indent();
270 (*sink_) << options_.null_rep;
271 } else {
272 Indent();
273 (*sink_) << "keys:\n";
274 auto keys_slice =
275 array.keys()->Slice(array.value_offset(i), array.value_length(i));
276 RETURN_NOT_OK(PrettyPrint(*keys_slice, {indent_, options_.window}, sink_));
277 (*sink_) << "\n";
278 Indent();
279 (*sink_) << "values:\n";
280 auto values_slice =
281 array.items()->Slice(array.value_offset(i), array.value_length(i));
282 RETURN_NOT_OK(PrettyPrint(*values_slice, {indent_, options_.window}, sink_));
283 }
284 }
285 (*sink_) << "\n";
286 return Status::OK();
287 }
288
Visit(const NullArray & array)289 Status Visit(const NullArray& array) {
290 (*sink_) << array.length() << " nulls";
291 return Status::OK();
292 }
293
294 template <typename T>
295 enable_if_t<std::is_base_of<PrimitiveArray, T>::value ||
296 std::is_base_of<FixedSizeBinaryArray, T>::value ||
297 std::is_base_of<BinaryArray, T>::value ||
298 std::is_base_of<LargeBinaryArray, T>::value ||
299 std::is_base_of<ListArray, T>::value ||
300 std::is_base_of<LargeListArray, T>::value ||
301 std::is_base_of<MapArray, T>::value ||
302 std::is_base_of<FixedSizeListArray, T>::value,
303 Status>
Visit(const T & array)304 Visit(const T& array) {
305 OpenArray(array);
306 if (array.length() > 0) {
307 RETURN_NOT_OK(WriteDataValues(array));
308 }
309 CloseArray(array);
310 return Status::OK();
311 }
312
Visit(const ExtensionArray & array)313 Status Visit(const ExtensionArray& array) { return Print(*array.storage()); }
314
315 Status WriteValidityBitmap(const Array& array);
316
PrintChildren(const std::vector<std::shared_ptr<Array>> & fields,int64_t offset,int64_t length)317 Status PrintChildren(const std::vector<std::shared_ptr<Array>>& fields, int64_t offset,
318 int64_t length) {
319 for (size_t i = 0; i < fields.size(); ++i) {
320 Newline();
321 std::stringstream ss;
322 ss << "-- child " << i << " type: " << fields[i]->type()->ToString() << "\n";
323 Write(ss.str());
324
325 std::shared_ptr<Array> field = fields[i];
326 if (offset != 0) {
327 field = field->Slice(offset, length);
328 }
329
330 RETURN_NOT_OK(PrettyPrint(*field, indent_ + options_.indent_size, sink_));
331 }
332 return Status::OK();
333 }
334
Visit(const StructArray & array)335 Status Visit(const StructArray& array) {
336 RETURN_NOT_OK(WriteValidityBitmap(array));
337 std::vector<std::shared_ptr<Array>> children;
338 children.reserve(array.num_fields());
339 for (int i = 0; i < array.num_fields(); ++i) {
340 children.emplace_back(array.field(i));
341 }
342 return PrintChildren(children, 0, array.length());
343 }
344
Visit(const UnionArray & array)345 Status Visit(const UnionArray& array) {
346 RETURN_NOT_OK(WriteValidityBitmap(array));
347
348 Newline();
349 Write("-- type_ids: ");
350 UInt8Array type_codes(array.length(), array.type_codes(), nullptr, 0, array.offset());
351 RETURN_NOT_OK(PrettyPrint(type_codes, indent_ + options_.indent_size, sink_));
352
353 if (array.mode() == UnionMode::DENSE) {
354 Newline();
355 Write("-- value_offsets: ");
356 Int32Array value_offsets(array.length(), array.value_offsets(), nullptr, 0,
357 array.offset());
358 RETURN_NOT_OK(PrettyPrint(value_offsets, indent_ + options_.indent_size, sink_));
359 }
360
361 // Print the children without any offset, because the type ids are absolute
362 std::vector<std::shared_ptr<Array>> children;
363 children.reserve(array.num_fields());
364 for (int i = 0; i < array.num_fields(); ++i) {
365 children.emplace_back(array.field(i));
366 }
367 return PrintChildren(children, 0, array.length() + array.offset());
368 }
369
Visit(const DictionaryArray & array)370 Status Visit(const DictionaryArray& array) {
371 Newline();
372 Write("-- dictionary:\n");
373 RETURN_NOT_OK(
374 PrettyPrint(*array.dictionary(), indent_ + options_.indent_size, sink_));
375
376 Newline();
377 Write("-- indices:\n");
378 return PrettyPrint(*array.indices(), indent_ + options_.indent_size, sink_);
379 }
380
Print(const Array & array)381 Status Print(const Array& array) {
382 RETURN_NOT_OK(VisitArrayInline(array, this));
383 Flush();
384 return Status::OK();
385 }
386
387 private:
388 template <typename Unit>
FormatDateTime(const char * fmt,int64_t value,bool add_epoch)389 void FormatDateTime(const char* fmt, int64_t value, bool add_epoch) {
390 if (add_epoch) {
391 (*sink_) << arrow_vendored::date::format(fmt, epoch_ + Unit{value});
392 } else {
393 (*sink_) << arrow_vendored::date::format(fmt, Unit{value});
394 }
395 }
396
FormatDateTime(TimeUnit::type unit,const char * fmt,int64_t value,bool add_epoch)397 void FormatDateTime(TimeUnit::type unit, const char* fmt, int64_t value,
398 bool add_epoch) {
399 switch (unit) {
400 case TimeUnit::NANO:
401 FormatDateTime<std::chrono::nanoseconds>(fmt, value, add_epoch);
402 break;
403 case TimeUnit::MICRO:
404 FormatDateTime<std::chrono::microseconds>(fmt, value, add_epoch);
405 break;
406 case TimeUnit::MILLI:
407 FormatDateTime<std::chrono::milliseconds>(fmt, value, add_epoch);
408 break;
409 case TimeUnit::SECOND:
410 FormatDateTime<std::chrono::seconds>(fmt, value, add_epoch);
411 break;
412 }
413 }
414
415 static arrow_vendored::date::sys_days epoch_;
416 };
417
418 arrow_vendored::date::sys_days ArrayPrinter::epoch_ =
419 arrow_vendored::date::sys_days{arrow_vendored::date::jan / 1 / 1970};
420
WriteValidityBitmap(const Array & array)421 Status ArrayPrinter::WriteValidityBitmap(const Array& array) {
422 Indent();
423 Write("-- is_valid:");
424
425 if (array.null_count() > 0) {
426 Newline();
427 BooleanArray is_valid(array.length(), array.null_bitmap(), nullptr, 0,
428 array.offset());
429 return PrettyPrint(is_valid, indent_ + options_.indent_size, sink_);
430 } else {
431 Write(" all not null");
432 return Status::OK();
433 }
434 }
435
PrettyPrint(const Array & arr,int indent,std::ostream * sink)436 Status PrettyPrint(const Array& arr, int indent, std::ostream* sink) {
437 PrettyPrintOptions options;
438 options.indent = indent;
439 ArrayPrinter printer(options, sink);
440 return printer.Print(arr);
441 }
442
PrettyPrint(const Array & arr,const PrettyPrintOptions & options,std::ostream * sink)443 Status PrettyPrint(const Array& arr, const PrettyPrintOptions& options,
444 std::ostream* sink) {
445 ArrayPrinter printer(options, sink);
446 return printer.Print(arr);
447 }
448
PrettyPrint(const Array & arr,const PrettyPrintOptions & options,std::string * result)449 Status PrettyPrint(const Array& arr, const PrettyPrintOptions& options,
450 std::string* result) {
451 std::ostringstream sink;
452 RETURN_NOT_OK(PrettyPrint(arr, options, &sink));
453 *result = sink.str();
454 return Status::OK();
455 }
456
PrettyPrint(const ChunkedArray & chunked_arr,const PrettyPrintOptions & options,std::ostream * sink)457 Status PrettyPrint(const ChunkedArray& chunked_arr, const PrettyPrintOptions& options,
458 std::ostream* sink) {
459 int num_chunks = chunked_arr.num_chunks();
460 int indent = options.indent;
461 int window = options.window;
462
463 for (int i = 0; i < indent; ++i) {
464 (*sink) << " ";
465 }
466 (*sink) << "[\n";
467 bool skip_comma = true;
468 for (int i = 0; i < num_chunks; ++i) {
469 if (skip_comma) {
470 skip_comma = false;
471 } else {
472 (*sink) << ",\n";
473 }
474 if ((i >= window) && (i < (num_chunks - window))) {
475 for (int i = 0; i < indent; ++i) {
476 (*sink) << " ";
477 }
478 (*sink) << "...\n";
479 i = num_chunks - window - 1;
480 skip_comma = true;
481 } else {
482 PrettyPrintOptions chunk_options = options;
483 chunk_options.indent += options.indent_size;
484 ArrayPrinter printer(chunk_options, sink);
485 RETURN_NOT_OK(printer.Print(*chunked_arr.chunk(i)));
486 }
487 }
488 (*sink) << "\n";
489
490 for (int i = 0; i < indent; ++i) {
491 (*sink) << " ";
492 }
493 (*sink) << "]";
494
495 return Status::OK();
496 }
497
PrettyPrint(const ChunkedArray & chunked_arr,const PrettyPrintOptions & options,std::string * result)498 Status PrettyPrint(const ChunkedArray& chunked_arr, const PrettyPrintOptions& options,
499 std::string* result) {
500 std::ostringstream sink;
501 RETURN_NOT_OK(PrettyPrint(chunked_arr, options, &sink));
502 *result = sink.str();
503 return Status::OK();
504 }
505
PrettyPrint(const RecordBatch & batch,int indent,std::ostream * sink)506 Status PrettyPrint(const RecordBatch& batch, int indent, std::ostream* sink) {
507 for (int i = 0; i < batch.num_columns(); ++i) {
508 const std::string& name = batch.column_name(i);
509 (*sink) << name << ": ";
510 RETURN_NOT_OK(PrettyPrint(*batch.column(i), indent + 2, sink));
511 (*sink) << "\n";
512 }
513 (*sink) << std::flush;
514 return Status::OK();
515 }
516
PrettyPrint(const RecordBatch & batch,const PrettyPrintOptions & options,std::ostream * sink)517 Status PrettyPrint(const RecordBatch& batch, const PrettyPrintOptions& options,
518 std::ostream* sink) {
519 for (int i = 0; i < batch.num_columns(); ++i) {
520 const std::string& name = batch.column_name(i);
521 PrettyPrintOptions column_options = options;
522 column_options.indent += 2;
523
524 (*sink) << name << ": ";
525 RETURN_NOT_OK(PrettyPrint(*batch.column(i), column_options, sink));
526 (*sink) << "\n";
527 }
528 (*sink) << std::flush;
529 return Status::OK();
530 }
531
PrettyPrint(const Table & table,const PrettyPrintOptions & options,std::ostream * sink)532 Status PrettyPrint(const Table& table, const PrettyPrintOptions& options,
533 std::ostream* sink) {
534 RETURN_NOT_OK(PrettyPrint(*table.schema(), options, sink));
535 (*sink) << "\n";
536 (*sink) << "----\n";
537
538 PrettyPrintOptions column_options = options;
539 column_options.indent += 2;
540 for (int i = 0; i < table.num_columns(); ++i) {
541 for (int j = 0; j < options.indent; ++j) {
542 (*sink) << " ";
543 }
544 (*sink) << table.schema()->field(i)->name() << ":\n";
545 RETURN_NOT_OK(PrettyPrint(*table.column(i), column_options, sink));
546 (*sink) << "\n";
547 }
548 (*sink) << std::flush;
549 return Status::OK();
550 }
551
DebugPrint(const Array & arr,int indent)552 Status DebugPrint(const Array& arr, int indent) {
553 return PrettyPrint(arr, indent, &std::cout);
554 }
555
556 class SchemaPrinter : public PrettyPrinter {
557 public:
SchemaPrinter(const Schema & schema,const PrettyPrintOptions & options,std::ostream * sink)558 SchemaPrinter(const Schema& schema, const PrettyPrintOptions& options,
559 std::ostream* sink)
560 : PrettyPrinter(options, sink), schema_(schema) {}
561
562 Status PrintType(const DataType& type, bool nullable);
563 Status PrintField(const Field& field);
564
PrintVerboseMetadata(const KeyValueMetadata & metadata)565 void PrintVerboseMetadata(const KeyValueMetadata& metadata) {
566 for (int64_t i = 0; i < metadata.size(); ++i) {
567 Newline();
568 Write(metadata.key(i) + ": '" + metadata.value(i) + "'");
569 }
570 }
571
PrintTruncatedMetadata(const KeyValueMetadata & metadata)572 void PrintTruncatedMetadata(const KeyValueMetadata& metadata) {
573 for (int64_t i = 0; i < metadata.size(); ++i) {
574 Newline();
575 size_t size = metadata.value(i).size();
576 size_t truncated_size = std::max<size_t>(10, 70 - metadata.key(i).size() - indent_);
577 if (size <= truncated_size) {
578 Write(metadata.key(i) + ": '" + metadata.value(i) + "'");
579 continue;
580 }
581
582 Write(metadata.key(i) + ": '" + metadata.value(i).substr(0, truncated_size) +
583 "' + " + std::to_string(size - truncated_size));
584 }
585 }
586
PrintMetadata(const std::string & metadata_type,const KeyValueMetadata & metadata)587 void PrintMetadata(const std::string& metadata_type, const KeyValueMetadata& metadata) {
588 if (metadata.size() > 0) {
589 Newline();
590 Write(metadata_type);
591 if (options_.truncate_metadata) {
592 PrintTruncatedMetadata(metadata);
593 } else {
594 PrintVerboseMetadata(metadata);
595 }
596 }
597 }
598
Print()599 Status Print() {
600 for (int i = 0; i < schema_.num_fields(); ++i) {
601 if (i > 0) {
602 Newline();
603 } else {
604 Indent();
605 }
606 RETURN_NOT_OK(PrintField(*schema_.field(i)));
607 }
608
609 if (options_.show_schema_metadata && schema_.metadata() != nullptr) {
610 PrintMetadata("-- schema metadata --", *schema_.metadata());
611 }
612 Flush();
613 return Status::OK();
614 }
615
616 private:
617 const Schema& schema_;
618 };
619
PrintType(const DataType & type,bool nullable)620 Status SchemaPrinter::PrintType(const DataType& type, bool nullable) {
621 Write(type.ToString());
622 if (!nullable) {
623 Write(" not null");
624 }
625 for (int i = 0; i < type.num_fields(); ++i) {
626 Newline();
627
628 std::stringstream ss;
629 ss << "child " << i << ", ";
630
631 indent_ += options_.indent_size;
632 WriteIndented(ss.str());
633 RETURN_NOT_OK(PrintField(*type.field(i)));
634 indent_ -= options_.indent_size;
635 }
636 return Status::OK();
637 }
638
PrintField(const Field & field)639 Status SchemaPrinter::PrintField(const Field& field) {
640 Write(field.name());
641 Write(": ");
642 RETURN_NOT_OK(PrintType(*field.type(), field.nullable()));
643
644 if (options_.show_field_metadata && field.metadata() != nullptr) {
645 indent_ += options_.indent_size;
646 PrintMetadata("-- field metadata --", *field.metadata());
647 indent_ -= options_.indent_size;
648 }
649 return Status::OK();
650 }
651
PrettyPrint(const Schema & schema,const PrettyPrintOptions & options,std::ostream * sink)652 Status PrettyPrint(const Schema& schema, const PrettyPrintOptions& options,
653 std::ostream* sink) {
654 SchemaPrinter printer(schema, options, sink);
655 return printer.Print();
656 }
657
PrettyPrint(const Schema & schema,const PrettyPrintOptions & options,std::string * result)658 Status PrettyPrint(const Schema& schema, const PrettyPrintOptions& options,
659 std::string* result) {
660 std::ostringstream sink;
661 RETURN_NOT_OK(PrettyPrint(schema, options, &sink));
662 *result = sink.str();
663 return Status::OK();
664 }
665
666 } // namespace arrow
667