Autonomy Software C++ 24.5.1
Welcome to the Autonomy Software repository of the Mars Rover Design Team (MRDT) at Missouri University of Science and Technology (Missouri S&T)! API reference contains the source code and other resources for the development of the autonomy software for our Mars rover. The Autonomy Software project aims to compete in the University Rover Challenge (URC) by demonstrating advanced autonomous capabilities and robust navigation algorithms.
Loading...
Searching...
No Matches
duckdb::BoxRendererImplementation Struct Reference
Inheritance diagram for duckdb::BoxRendererImplementation:
Collaboration diagram for duckdb::BoxRendererImplementation:

Public Member Functions

 BoxRendererImplementation (BoxRendererConfig config, ClientContext &context, const vector< string > &names, const ColumnDataCollection &result)
 
idx_t TotalRenderWidth () override
 
void Render (BaseResultRenderer &ss) override
 

Private Member Functions

void Initialize ()
 
void RenderValue (BaseResultRenderer &ss, const string &value, idx_t column_width, ResultRenderType render_mode, const vector< HighlightingAnnotation > &annotations, ValueRenderAlignment alignment=ValueRenderAlignment::MIDDLE, optional_idx render_width=optional_idx(), const char *vertical=nullptr)
 
string RenderType (const LogicalType &type)
 
ValueRenderAlignment TypeAlignment (const LogicalType &type)
 
void ConvertRenderVector (Vector &vector, Vector &render_lengths, idx_t count, const LogicalType &original_type, idx_t null_render_length)
 
void FetchTopCollection (RenderDataCollection &top_collection, const ColumnDataCollection &result, idx_t chunk_idx, idx_t row_idx, idx_t top_rows, idx_t bottom_rows)
 
void FetchBottomCollection (RenderDataCollection &bottom_collection, const ColumnDataCollection &result, idx_t bottom_rows)
 
vector< RenderDataCollectionFetchRenderCollections (const ColumnDataCollection &result, idx_t top_rows, idx_t bottom_rows)
 
vector< RenderDataCollectionPivotCollections (vector< RenderDataCollection > input, idx_t row_count)
 
void ComputeRenderWidths (vector< RenderDataCollection > &collections, idx_t min_width, idx_t max_width)
 
void RenderHeader (BaseResultRenderer &ss)
 
void RenderValues (BaseResultRenderer &ss, vector< RenderDataCollection > &collections)
 
void RenderRow (BaseResultRenderer &ss, BoxRenderRow &row)
 
void RenderDivider (BaseResultRenderer &ss, const BoxRenderRow &prev_row, const BoxRenderRow &next_row)
 
void PotentiallyExpandRow (BoxRenderRow &row, vector< BoxRenderRow > &rows, idx_t max_rows_per_row, bool is_first_row)
 
void UpdateColumnCountFooter (idx_t column_count, const unordered_set< idx_t > &pruned_columns)
 
string TruncateValue (const string &value, idx_t column_width, idx_t &pos, idx_t &current_render_width)
 
void ComputeRowFooter (idx_t row_count, idx_t rendered_rows)
 
void RenderFooter (BaseResultRenderer &ss, idx_t row_count, idx_t column_count)
 
string FormatNumber (const string &input)
 
string ConvertRenderValue (const string &input, const LogicalType &type)
 
string ConvertRenderValue (const string &input)
 
void RenderLayoutLine (BaseResultRenderer &ss, const char *layout, const char *boundary, const char *left_corner, const char *right_corner)
 
string TryFormatLargeNumber (const string &numeric)
 Try to format a large number in a readable way (e.g. 1234567 -> 1.23 million)
 
bool CanPrettyPrint (const BoxRenderValue &render_value)
 
bool CanHighlight (const BoxRenderValue &render_value)
 
void PrettyPrintValue (BoxRenderValue &render_value, idx_t max_rows, idx_t max_width)
 
void HighlightValue (BoxRenderValue &render_value)
 

Private Attributes

BoxRendererConfig config
 
ClientContextcontext
 
vector< string > column_names
 
vector< LogicalTyperesult_types
 
const ColumnDataCollectionresult
 
vector< idx_tcolumn_widths
 
vector< idx_tcolumn_boundary_positions
 
idx_t total_render_length = 0
 
vector< BoxRenderRowheader_rows
 
BoxRendererFooter footer
 
unordered_set< idx_tpruned_columns
 
vector< optional_idxcolumn_map
 
bool expand_rows = false
 
bool is_first_row = true
 
idx_t max_rows_per_row = 1
 
vector< RenderDataCollectionrender_collections
 
idx_t top_rows = 0
 
idx_t bottom_rows = 0
 
optional_idx current_chunk_idx
 
optional_idx current_row_idx
 

Constructor & Destructor Documentation

◆ BoxRendererImplementation()

duckdb::BoxRendererImplementation::BoxRendererImplementation ( BoxRendererConfig  config,
ClientContext context,
const vector< string > &  names,
const ColumnDataCollection result 
)
47525 : config(std::move(config_p)), context(context), column_names(names), result(result) {
47526 result_types = result.Types();
47527 Initialize();
47528}

Member Function Documentation

◆ TotalRenderWidth()

idx_t duckdb::BoxRendererImplementation::TotalRenderWidth ( )
inlineoverridevirtual

Implements duckdb::BoxRendererState.

47450 {
47451 return total_render_length;
47452 }

◆ Render()

void duckdb::BoxRendererImplementation::Render ( BaseResultRenderer ss)
overridevirtual

Implements duckdb::BoxRendererState.

47635 {
47636 ss.SetResultTypes(result.Types());
47637
47638 RenderHeader(ss);
47639 while (true) {
47640 // render the values
47641 RenderValues(ss, render_collections);
47642
47643 if (!current_chunk_idx.IsValid()) {
47644 // we are done - render the footer
47645 break;
47646 }
47647 // we have more data to fetch
47648 render_collections.clear();
47649 auto column_count = result.ColumnCount();
47650 render_collections.emplace_back(context, column_count);
47651 render_collections.emplace_back(context, column_count);
47652 FetchTopCollection(render_collections[0], result, current_chunk_idx.GetIndex(), current_row_idx.GetIndex(),
47653 top_rows, bottom_rows);
47654 }
47655
47656 // render the row count and column count
47657 idx_t column_count = result_types.size();
47658 RenderFooter(ss, result.Count(), column_count);
47659}

◆ Initialize()

void duckdb::BoxRendererImplementation::Initialize ( )
private
47578 {
47579 if (result.ColumnCount() != column_names.size()) {
47580 throw InternalException("Error in BoxRenderer::Render - unaligned columns and names");
47581 }
47582 auto max_width = config.max_width;
47583 if (max_width == 0) {
47584 if (Printer::IsTerminal(OutputStream::STREAM_STDOUT)) {
47585 max_width = Printer::TerminalWidth();
47586 } else {
47587 max_width = 120;
47588 }
47589 }
47590 // we do not support max widths under 80
47591 max_width = MaxValue<idx_t>(80, max_width);
47592
47593 // figure out how many/which rows to render
47594 idx_t row_count = result.Count();
47595 idx_t rows_to_render = MinValue<idx_t>(row_count, config.max_rows);
47596 if (row_count <= config.max_rows + 3) {
47597 // hiding rows adds 3 extra rows
47598 // so hiding rows makes no sense if we are only slightly over the limit
47599 // if we are 1 row over the limit hiding rows will actually increase the number of lines we display!
47600 // in this case render all the rows
47601 rows_to_render = row_count;
47602 }
47603 if (rows_to_render == row_count) {
47604 top_rows = row_count;
47605 bottom_rows = 0;
47606 } else {
47607 top_rows = rows_to_render / 2 + (rows_to_render % 2 != 0 ? 1 : 0);
47608 bottom_rows = rows_to_render - top_rows;
47609 }
47610 ComputeRowFooter(row_count, top_rows + bottom_rows);
47611
47612 // fetch the top and bottom render collections from the result
47613 render_collections = FetchRenderCollections(result, top_rows, bottom_rows);
47614 if (config.render_mode == RenderMode::COLUMNS && rows_to_render > 0) {
47615 render_collections = PivotCollections(std::move(render_collections), row_count);
47616 }
47617
47618 // for each column, figure out the width
47619 // start off by figuring out the name of the header by looking at the column name and column type
47620 idx_t min_width = footer.must_show_footer ? footer.render_length : 0;
47621 ComputeRenderWidths(render_collections, min_width, max_width);
47622
47623 // render boundaries for the individual columns
47624 for (idx_t c = 0; c < column_widths.size(); c++) {
47625 idx_t render_boundary;
47626 if (c == 0) {
47627 render_boundary = column_widths[c] + 2;
47628 } else {
47629 render_boundary = column_boundary_positions[c - 1] + column_widths[c] + 3;
47630 }
47631 column_boundary_positions.push_back(render_boundary);
47632 }
47633}
static DUCKDB_API bool IsTerminal(OutputStream stream)
Whether or not we are printing to a terminal.
static DUCKDB_API idx_t TerminalWidth()
The terminal width.
RenderMode render_mode
Whether or not to render row-wise or column-wise.
Definition duckdb.cpp:35682

◆ RenderValue()

void duckdb::BoxRendererImplementation::RenderValue ( BaseResultRenderer ss,
const string &  value,
idx_t  column_width,
ResultRenderType  render_mode,
const vector< HighlightingAnnotation > &  annotations,
ValueRenderAlignment  alignment = ValueRenderAlignment::MIDDLE,
optional_idx  render_width = optional_idx(),
const char vertical = nullptr 
)
private
47692 {
47693 idx_t render_width;
47694 if (render_width_input.IsValid()) {
47695 render_width = render_width_input.GetIndex();
47696 if (render_width != Utf8Proc::RenderWidth(value)) {
47697 throw InternalException("Misaligned render width provided for string \"%s\"", value);
47698 }
47699 } else {
47700 render_width = Utf8Proc::RenderWidth(value);
47701 }
47702
47703 const_reference<string> render_value(value);
47704 string small_value;
47705 idx_t max_render_pos = value.size();
47706 if (render_width > column_width) {
47707 // the string is too large to fit in this column!
47708 // the size of this column must have been reduced
47709 // figure out how much of this value we can render
47710 idx_t pos = 0;
47711 idx_t current_render_width = config.DOTDOTDOT_LENGTH;
47712 small_value = TruncateValue(value, column_width, pos, current_render_width);
47713 max_render_pos = small_value.size();
47714 small_value += config.DOTDOTDOT;
47715 render_width = current_render_width;
47716 render_value = const_reference<string>(small_value);
47717 }
47718 auto padding_count = (column_width - render_width) + 2;
47719 idx_t lpadding;
47720 idx_t rpadding;
47721 switch (alignment) {
47722 case ValueRenderAlignment::LEFT:
47723 lpadding = 1;
47724 rpadding = padding_count - 1;
47725 break;
47726 case ValueRenderAlignment::MIDDLE:
47727 lpadding = padding_count / 2;
47728 rpadding = padding_count - lpadding;
47729 break;
47730 case ValueRenderAlignment::RIGHT:
47731 lpadding = padding_count - 1;
47732 rpadding = 1;
47733 break;
47734 default:
47735 throw InternalException("Unrecognized value renderer alignment");
47736 }
47737 ss << (vertical ? vertical : config.VERTICAL);
47738 ss << string(lpadding, ' ');
47739 if (!annotations.empty()) {
47740 // if we have annotations split up the rendering between annotations
47741 idx_t pos = 0;
47742 ResultRenderType active_render_mode = render_mode;
47743 for (auto &annotation : annotations) {
47744 if (annotation.start >= max_render_pos) {
47745 break;
47746 }
47747 auto render_end = MinValue<idx_t>(max_render_pos, annotation.start);
47748 ss.Render(active_render_mode, render_value.get().substr(pos, render_end - pos));
47749 active_render_mode = annotation.render_mode;
47750 pos = render_end;
47751 }
47752 if (pos < render_value.get().size()) {
47753 ss.Render(active_render_mode, render_value.get().substr(pos, render_value.get().size() - pos));
47754 }
47755 } else {
47756 ss.Render(render_mode, render_value.get());
47757 }
47758 ss << string(rpadding, ' ');
47759}
static size_t RenderWidth(const char *s, size_t len, size_t pos)
Returns the render width of a single character in a string.

◆ RenderType()

string duckdb::BoxRendererImplementation::RenderType ( const LogicalType type)
private
47761 {
47762 if (type.HasAlias()) {
47763 return StringUtil::Lower(type.ToString());
47764 }
47765 switch (type.id()) {
47766 case LogicalTypeId::TINYINT:
47767 return "int8";
47768 case LogicalTypeId::SMALLINT:
47769 return "int16";
47770 case LogicalTypeId::INTEGER:
47771 return "int32";
47772 case LogicalTypeId::BIGINT:
47773 return "int64";
47774 case LogicalTypeId::HUGEINT:
47775 return "int128";
47776 case LogicalTypeId::UTINYINT:
47777 return "uint8";
47778 case LogicalTypeId::USMALLINT:
47779 return "uint16";
47780 case LogicalTypeId::UINTEGER:
47781 return "uint32";
47782 case LogicalTypeId::UBIGINT:
47783 return "uint64";
47784 case LogicalTypeId::UHUGEINT:
47785 return "uint128";
47786 case LogicalTypeId::LIST: {
47787 auto child = RenderType(ListType::GetChildType(type));
47788 return child + "[]";
47789 }
47790 default:
47791 return StringUtil::Lower(type.ToString());
47792 }
47793}
static DUCKDB_API string Lower(const string &str)
Convert a string to lowercase.

◆ TypeAlignment()

ValueRenderAlignment duckdb::BoxRendererImplementation::TypeAlignment ( const LogicalType type)
private
47795 {
47796 switch (type.id()) {
47797 case LogicalTypeId::TINYINT:
47798 case LogicalTypeId::SMALLINT:
47799 case LogicalTypeId::INTEGER:
47800 case LogicalTypeId::BIGINT:
47801 case LogicalTypeId::HUGEINT:
47802 case LogicalTypeId::UTINYINT:
47803 case LogicalTypeId::USMALLINT:
47804 case LogicalTypeId::UINTEGER:
47805 case LogicalTypeId::UBIGINT:
47806 case LogicalTypeId::UHUGEINT:
47807 case LogicalTypeId::DECIMAL:
47808 case LogicalTypeId::FLOAT:
47809 case LogicalTypeId::DOUBLE:
47810 return ValueRenderAlignment::RIGHT;
47811 default:
47812 return ValueRenderAlignment::LEFT;
47813 }
47814}

◆ ConvertRenderVector()

void duckdb::BoxRendererImplementation::ConvertRenderVector ( Vector vector,
Vector render_lengths,
idx_t  count,
const LogicalType original_type,
idx_t  null_render_length 
)
private
47889 {
47890 vector.Flatten(count);
47891 auto data = FlatVector::GetData<string_t>(vector);
47892 auto &validity = FlatVector::Validity(vector);
47893 auto render_length_data = FlatVector::GetData<uint64_t>(render_lengths);
47894 for (idx_t r = 0; r < count; r++) {
47895 if (!validity.RowIsValid(r)) {
47896 // null - no need to convert
47897 // set render length to render length of NULL
47898 render_length_data[r] = null_render_length;
47899 continue;
47900 }
47901 // non-null - convert value
47902 auto result_str = ConvertRenderValue(data[r].GetString(), original_type);
47903 render_length_data[r] = Utf8Proc::RenderWidth(result_str);
47904 data[r] = StringVector::AddString(vector, result_str);
47905 }
47906}
static DUCKDB_API string_t AddString(Vector &vector, const char *data, idx_t len)
Add a string to the string heap of the vector (auxiliary data)

◆ FetchTopCollection()

void duckdb::BoxRendererImplementation::FetchTopCollection ( RenderDataCollection top_collection,
const ColumnDataCollection result,
idx_t  chunk_idx,
idx_t  row_idx,
idx_t  top_rows,
idx_t  bottom_rows 
)
private
47923 {
47924 auto column_count = result.ColumnCount();
47925
47926 DataChunk fetch_result;
47927 fetch_result.Initialize(context, result.Types());
47928
47929 DataChunk insert_result;
47930 top_collection.InitializeChunk(insert_result);
47931
47932 idx_t null_render_length = Utf8Proc::RenderWidth(config.null_value);
47933
47934 current_chunk_idx = optional_idx();
47935 current_row_idx = optional_idx();
47936 while (row_idx < top_rows) {
47937 if (context.IsInterrupted()) {
47938 break;
47939 }
47940 fetch_result.Reset();
47941 insert_result.Reset();
47942 // fetch the next chunk
47943 result.FetchChunk(chunk_idx, fetch_result);
47944 idx_t insert_count = MinValue<idx_t>(fetch_result.size(), top_rows - row_idx);
47945
47946 // cast all columns to varchar
47947 for (idx_t c = 0; c < column_count; c++) {
47948 auto &source_vector = fetch_result.data[c];
47949 auto &target_vector = top_collection.Values(insert_result, c);
47950 auto &render_lengths = top_collection.RenderLengths(insert_result, c);
47951 VectorOperations::Cast(context, source_vector, target_vector, insert_count);
47952 ConvertRenderVector(target_vector, render_lengths, insert_count, source_vector.GetType(),
47953 null_render_length);
47954 }
47955 insert_result.SetCardinality(insert_count);
47956
47957 // construct the render collection
47958 top_collection.render_values->Append(insert_result);
47959
47960 // if we have are constructing a footer
47961 if (config.large_number_rendering == LargeNumberRendering::FOOTER) {
47962 D_ASSERT(insert_count == 1);
47963 vector<string> readable_numbers;
47964 readable_numbers.resize(column_count);
47965 bool all_readable = true;
47966 for (idx_t c = 0; c < column_count; c++) {
47967 if (!result.Types()[c].IsNumeric()) {
47968 // not a numeric type - cannot summarize
47969 all_readable = false;
47970 break;
47971 }
47972 // add a readable rendering of the value (i.e. "1234567" becomes "1.23 million")
47973 // we only add the rendering if the string is big
47974 auto &values = top_collection.Values(insert_result, c);
47975 auto numeric_val = values.GetValue(0).ToString();
47976 readable_numbers[c] = TryFormatLargeNumber(numeric_val);
47977 if (readable_numbers[c].empty()) {
47978 all_readable = false;
47979 break;
47980 }
47981 readable_numbers[c] = "(" + readable_numbers[c] + ")";
47982 }
47983 insert_result.Reset();
47984 if (all_readable) {
47985 for (idx_t c = 0; c < column_count; c++) {
47986 auto &values = top_collection.Values(insert_result, c);
47987 auto &render_widths = top_collection.RenderLengths(insert_result, c);
47988 values.SetValue(0, Value(readable_numbers[c]));
47989 render_widths.SetValue(0, Value::UBIGINT(Utf8Proc::RenderWidth(readable_numbers[c])));
47990 }
47991 insert_result.SetCardinality(1);
47992 top_collection.render_values->Append(insert_result);
47993 } else {
47994 config.large_number_rendering = LargeNumberRendering::NONE;
47995 }
47996 }
47997
47998 chunk_idx++;
47999 row_idx += fetch_result.size();
48000 if (bottom_rows == 0 && row_idx >= config.max_analyze_rows && config.render_mode == RenderMode::ROWS) {
48001 // stop fetching for now - store current position
48002 current_chunk_idx = chunk_idx;
48003 current_row_idx = row_idx;
48004 break;
48005 }
48006 }
48007}
static DUCKDB_API Value UBIGINT(uint64_t value)
Create an unsigned bigint Value from a specified value.
LargeNumberRendering large_number_rendering
How to render large numbers.
Definition duckdb.cpp:35684
string null_value
how to render NULL values
Definition duckdb.cpp:35676
idx_t max_analyze_rows
The maximum number of rows to analyze in order to determine column widths.
Definition duckdb.cpp:35668
string TryFormatLargeNumber(const string &numeric)
Try to format a large number in a readable way (e.g. 1234567 -> 1.23 million)
Definition duckdb.cpp:47884
static DUCKDB_API void Cast(ClientContext &context, Vector &source, Vector &result, idx_t count, bool strict=false)
Cast the data from the source type to the target type. Throws an exception if the cast fails.

◆ FetchBottomCollection()

void duckdb::BoxRendererImplementation::FetchBottomCollection ( RenderDataCollection bottom_collection,
const ColumnDataCollection result,
idx_t  bottom_rows 
)
private
48010 {
48011 if (bottom_rows == 0) {
48012 return;
48013 }
48014 auto column_count = result.ColumnCount();
48015
48016 DataChunk fetch_result;
48017 fetch_result.Initialize(context, result.Types());
48018
48019 DataChunk insert_result;
48020 bottom_collection.InitializeChunk(insert_result);
48021
48022 idx_t null_render_length = Utf8Proc::RenderWidth(config.null_value);
48023 // fetch the bottom rows from the ColumnDataCollection
48024 // first fetch all required chunks
48025 idx_t fetched_row_count = 0;
48026 vector<unique_ptr<DataChunk>> chunks;
48027 idx_t chunk_idx = result.ChunkCount() - 1;
48028 while (fetched_row_count < bottom_rows) {
48029 // fetch the current chunk
48030 auto fetch_chunk = make_uniq<DataChunk>();
48031 fetch_chunk->Initialize(context, result.Types());
48032 result.FetchChunk(chunk_idx, *fetch_chunk);
48033
48034 fetched_row_count += fetch_chunk->size();
48035 chunks.push_back(std::move(fetch_chunk));
48036 if (fetched_row_count >= bottom_rows) {
48037 // fetched all required rows - break
48038 break;
48039 }
48040 // fetch another chunk
48041 if (chunk_idx == 0) {
48042 throw InternalException("Failed to fetch enough rows");
48043 }
48044 chunk_idx--;
48045 }
48046 // invert the chunks and start converting
48047 std::reverse(chunks.begin(), chunks.end());
48048
48049 for (idx_t i = 0; i < chunks.size(); i++) {
48050 // skip over any extra rows
48051 auto &chunk = *chunks[i];
48052 idx_t offset = i == 0 ? fetched_row_count - bottom_rows : 0;
48053 idx_t insert_count = chunk.size() - offset;
48054
48055 if (offset > 0) {
48056 // invert the rows
48057 SelectionVector slice_sel(insert_count);
48058 for (idx_t r = 0; r < insert_count; r++) {
48059 slice_sel.set_index(r, offset + r);
48060 }
48061 chunk.Slice(slice_sel, insert_count);
48062 chunk.Flatten();
48063 }
48064
48065 for (idx_t c = 0; c < column_count; c++) {
48066 auto &source_vector = chunk.data[c];
48067 auto &target_vector = bottom_collection.Values(insert_result, c);
48068 auto &render_lengths = bottom_collection.RenderLengths(insert_result, c);
48069 VectorOperations::Cast(context, source_vector, target_vector, insert_count);
48070 ConvertRenderVector(target_vector, render_lengths, insert_count, source_vector.GetType(),
48071 null_render_length);
48072 }
48073 insert_result.SetCardinality(insert_count);
48074 // construct the render collection
48075 bottom_collection.render_values->Append(insert_result);
48076 }
48077}

◆ FetchRenderCollections()

vector< RenderDataCollection > duckdb::BoxRendererImplementation::FetchRenderCollections ( const ColumnDataCollection result,
idx_t  top_rows,
idx_t  bottom_rows 
)
private
48080 {
48081 auto column_count = result.ColumnCount();
48082 vector<RenderDataCollection> collections;
48083 collections.emplace_back(context, column_count);
48084 collections.emplace_back(context, column_count);
48085
48086 auto &top_collection = collections.front();
48087 auto &bottom_collection = collections.back();
48088
48089 if (config.large_number_rendering == LargeNumberRendering::FOOTER) {
48090 if (config.render_mode != RenderMode::ROWS || result.Count() != 1) {
48091 // large number footer can only be constructed (1) if we have a single row, and (2) in ROWS mode
48092 config.large_number_rendering = LargeNumberRendering::NONE;
48093 }
48094 }
48095
48096 // fetch the top rows from the ColumnDataCollection
48097 FetchTopCollection(top_collection, result, 0, 0, top_rows, bottom_rows);
48098 // fetch the bottom rows (if any)
48099 FetchBottomCollection(bottom_collection, result, bottom_rows);
48100 return collections;
48101}

◆ PivotCollections()

vector< RenderDataCollection > duckdb::BoxRendererImplementation::PivotCollections ( vector< RenderDataCollection input,
idx_t  row_count 
)
private
48104 {
48105 auto &top = input.front();
48106 auto &bottom = input.back();
48107
48108 vector<LogicalType> new_types;
48109 vector<string> new_names;
48110 new_names.emplace_back("Column");
48111 new_names.emplace_back("Type");
48112 new_types.emplace_back(LogicalType::VARCHAR);
48113 new_types.emplace_back(LogicalType::VARCHAR);
48114 for (idx_t r = 0; r < top.render_values->Count(); r++) {
48115 new_names.emplace_back("Row " + to_string(r + 1));
48116 new_types.emplace_back(LogicalType::VARCHAR);
48117 }
48118 for (idx_t r = 0; r < bottom.render_values->Count(); r++) {
48119 auto row_index = row_count - bottom.render_values->Count() + r + 1;
48120 new_names.emplace_back("Row " + to_string(row_index));
48121 new_types.emplace_back(LogicalType::VARCHAR);
48122 }
48123 vector<RenderDataCollection> result;
48124 result.emplace_back(context, new_names.size());
48125 DataChunk row_chunk;
48126 result.front().InitializeChunk(row_chunk);
48127 auto &res_coll = *result.front().render_values;
48128 ColumnDataAppendState append_state;
48129 res_coll.InitializeAppend(append_state);
48130 for (idx_t c = 0; c < column_names.size(); c++) {
48131 vector<column_t> column_ids {c * 2, c * 2 + 1};
48132 auto row_index = row_chunk.size();
48133 idx_t current_index = 0;
48134 auto &column_name = column_names[c];
48135 auto type_name = RenderType(result_types[c]);
48136 row_chunk.SetValue(current_index++, row_index, column_name);
48137 row_chunk.SetValue(current_index++, row_index, Value::UBIGINT(Utf8Proc::RenderWidth(column_name)));
48138 row_chunk.SetValue(current_index++, row_index, type_name);
48139 row_chunk.SetValue(current_index++, row_index, Value::UBIGINT(Utf8Proc::RenderWidth(type_name)));
48140 for (auto &collection : input) {
48141 for (auto &chunk : collection.render_values->Chunks(column_ids)) {
48142 if (context.IsInterrupted()) {
48143 break;
48144 }
48145 for (idx_t r = 0; r < chunk.size(); r++) {
48146 auto val = chunk.GetValue(0, r);
48147 auto length = chunk.GetValue(1, r);
48148 row_chunk.SetValue(current_index++, row_index, val);
48149 row_chunk.SetValue(current_index++, row_index, length);
48150 }
48151 }
48152 }
48153 row_chunk.SetCardinality(row_chunk.size() + 1);
48154 if (row_chunk.size() == STANDARD_VECTOR_SIZE || c + 1 == column_names.size()) {
48155 res_coll.Append(append_state, row_chunk);
48156 row_chunk.Reset();
48157 }
48158 }
48159 column_names = std::move(new_names);
48160 result_types = std::move(new_types);
48161 return result;
48162}

◆ ComputeRenderWidths()

void duckdb::BoxRendererImplementation::ComputeRenderWidths ( vector< RenderDataCollection > &  collections,
idx_t  min_width,
idx_t  max_width 
)
private
48950 {
48951 auto column_count = result_types.size();
48952
48953 // prepare the header / type for rendering
48954 // header / type
48955 BoxRenderRow header_row;
48956 BoxRenderRow type_row;
48957 for (idx_t c = 0; c < column_count; c++) {
48958 auto column_name = ConvertRenderValue(column_names[c]);
48959 idx_t column_name_width = Utf8Proc::RenderWidth(column_name);
48960 idx_t column_type_width = 0;
48961
48962 header_row.values.emplace_back(column_name, ResultRenderType::COLUMN_NAME, ValueRenderAlignment::MIDDLE,
48963 optional_idx(), column_name_width);
48964 if (config.render_mode == RenderMode::ROWS) {
48965 auto column_type = RenderType(result_types[c]);
48966 column_type_width = Utf8Proc::RenderWidth(RenderType(result_types[c]));
48967 type_row.values.emplace_back(column_type, ResultRenderType::COLUMN_TYPE, ValueRenderAlignment::MIDDLE,
48968 optional_idx(), column_type_width);
48969 }
48970 column_widths.push_back(MaxValue<idx_t>(column_name_width, column_type_width));
48971 }
48972 header_rows.push_back(std::move(header_row));
48973 if (config.render_mode == RenderMode::ROWS) {
48974 header_rows.push_back(std::move(type_row));
48975 }
48976
48977 // scan the render widths in the collection to figure out the
48978 vector<column_t> column_ids;
48979 for (idx_t c = 0; c < column_count; c++) {
48980 column_ids.push_back(c * 2 + 1);
48981 }
48982 for (auto &collection : collections) {
48983 for (auto &chunk : collection.render_values->Chunks(column_ids)) {
48984 for (idx_t c = 0; c < column_count; c++) {
48985 auto render_widths = FlatVector::GetData<uint64_t>(chunk.data[c]);
48986 for (idx_t r = 0; r < chunk.size(); r++) {
48987 if (render_widths[r] > column_widths[c]) {
48988 column_widths[c] = render_widths[r];
48989 }
48990 }
48991 }
48992 }
48993 }
48994
48995 bool shortened_columns = false;
48996 // figure out the total length
48997 // we start off with a pipe (|)
48998 total_render_length = 1;
48999 for (idx_t c = 0; c < column_widths.size(); c++) {
49000 // each column has a space at the beginning, and a space plus a pipe (|) at the end
49001 // hence + 3
49002 total_render_length += column_widths[c] + 3;
49003 }
49004 if (total_render_length < min_width) {
49005 // if there are hidden rows we should always display that
49006 // stretch up the first column until we have space to show the row count
49007 column_widths[0] += min_width - total_render_length;
49008 total_render_length = min_width;
49009 }
49010 // now we need to constrain the length
49011 if (total_render_length > max_width) {
49012 auto original_widths = column_widths;
49013 // before we remove columns, check if we can just reduce the size of columns
49014 vector<idx_t> max_shorten_amount;
49015 idx_t total_max_shorten_amount = 0;
49016 for (auto &w : column_widths) {
49017 if (w <= config.max_col_width) {
49018 max_shorten_amount.push_back(0);
49019 continue;
49020 }
49021 auto max_diff = w - config.max_col_width;
49022 max_shorten_amount.push_back(max_diff);
49023 total_max_shorten_amount += max_diff;
49024 }
49025 idx_t shorten_amount_required = total_render_length - max_width;
49026 if (total_max_shorten_amount >= shorten_amount_required) {
49027 // we can get below the max width by shortening
49028 // try to shorten everything to the same size
49029 // i.e. if we have one long column and one small column, we would prefer to shorten only the long column
49030
49031 // map of "shorten amount required -> column index"
49032 map<idx_t, vector<idx_t>> shorten_amount_required_map;
49033 for (idx_t col_idx = 0; col_idx < max_shorten_amount.size(); col_idx++) {
49034 shorten_amount_required_map[max_shorten_amount[col_idx]].push_back(col_idx);
49035 }
49036 vector<idx_t> actual_shorten_amounts;
49037 actual_shorten_amounts.resize(max_shorten_amount.size());
49038
49039 while (shorten_amount_required > 0) {
49040 // find the columns with the longest width
49041 auto entry = shorten_amount_required_map.rbegin();
49042 auto largest_width = entry->first;
49043 auto &column_list = entry->second;
49044 // shorten these columns to the next-shortest width
49045 // move to the second-largest entry - this is the target entry
49046 entry++;
49047 auto second_largest_width = entry == shorten_amount_required_map.rend() ? 0 : entry->first;
49048 auto max_shorten_width = largest_width - second_largest_width;
49049 D_ASSERT(max_shorten_width > 0);
49050
49051 auto total_potential_shorten_width = max_shorten_width * column_list.size();
49052 if (total_potential_shorten_width >= shorten_amount_required) {
49053 // we can reach the shorten amount required just by shortening this set of columns
49054 // shorten the columns equally
49055 idx_t shorten_amount_per_column = shorten_amount_required / column_list.size();
49056 for (auto &column_idx : column_list) {
49057 actual_shorten_amounts[column_idx] += shorten_amount_per_column;
49058 shorten_amount_required -= shorten_amount_per_column;
49059 }
49060
49061 // because of truncation, we might still need to shorten columns by a single unit
49062 for (idx_t i = column_list.size(); i > 0 && shorten_amount_required > 0; i--) {
49063 actual_shorten_amounts[column_list[i - 1]]++;
49064 shorten_amount_required--;
49065 }
49066 if (shorten_amount_required != 0) {
49067 throw InternalException("Shorten amount required has tob e zero now");
49068 }
49069
49070 // we are now done
49071 break;
49072 }
49073 if (entry == shorten_amount_required_map.rend()) {
49074 throw InternalException(
49075 "ColumnRenderer - we could not reach the shorten amount required but we ran out of columns?");
49076 }
49077 // we need to shorten all columns to the width of the next-largest column
49078 for (auto &column_idx : column_list) {
49079 actual_shorten_amounts[column_idx] += max_shorten_width;
49080 }
49081 // add all columns to the second-largest list of columns
49082 auto &second_largest_column_list = entry->second;
49083 second_largest_column_list.insert(second_largest_column_list.end(), column_list.begin(),
49084 column_list.end());
49085 // delete this entry from the shorten map and continue
49086 shorten_amount_required_map.erase(largest_width);
49087 shorten_amount_required -= total_potential_shorten_width;
49088 }
49089
49090 // now perform the shortening
49091 for (idx_t c = 0; c < actual_shorten_amounts.size(); c++) {
49092 if (actual_shorten_amounts[c] == 0) {
49093 continue;
49094 }
49095 D_ASSERT(actual_shorten_amounts[c] < column_widths[c]);
49096 column_widths[c] -= actual_shorten_amounts[c];
49097 total_render_length -= actual_shorten_amounts[c];
49098 shortened_columns = true;
49099 }
49100 } else {
49101 // we cannot get below the max width by shortening
49102 // set everything that is wider than the col width to the max col width
49103 // afterwards - we need to prune columns
49104 for (auto &w : column_widths) {
49105 if (w <= config.max_col_width) {
49106 continue;
49107 }
49108 total_render_length -= w - config.max_col_width;
49109 w = config.max_col_width;
49110 shortened_columns = true;
49111 }
49112 D_ASSERT(total_render_length > max_width);
49113 }
49114
49115 if (total_render_length > max_width) {
49116 // the total length is still too large
49117 // we need to remove columns!
49118 // first, we add 6 characters to the total length
49119 // this is what we need to add the "..." in the middle
49120 total_render_length += 3 + config.DOTDOTDOT_LENGTH;
49121 // now select columns to prune
49122 // we select columns in zig-zag order starting from the middle
49123 // e.g. if we have 10 columns, we remove #5, then #4, then #6, then #3, then #7, etc
49124 int64_t offset = 0;
49125 while (total_render_length > max_width) {
49126 auto c = NumericCast<idx_t>(NumericCast<int64_t>(column_count) / 2 + offset);
49127 total_render_length -= column_widths[c] + 3;
49128 pruned_columns.insert(c);
49129 if (offset >= 0) {
49130 offset = -offset - 1;
49131 } else {
49132 offset = -offset;
49133 }
49134 }
49135
49136 // if we have any space left after truncating columns we can try to increase the size of columns again
49137 idx_t space_left = max_width - total_render_length;
49138 for (idx_t c = 0; c < column_widths.size() && space_left > 0; c++) {
49139 if (pruned_columns.find(c) != pruned_columns.end()) {
49140 // only increase size of visible columns
49141 continue;
49142 }
49143 if (column_widths[c] >= original_widths[c]) {
49144 continue;
49145 }
49146 idx_t increase_amount = MinValue<idx_t>(space_left, original_widths[c] - column_widths[c]);
49147 column_widths[c] += increase_amount;
49148 space_left -= increase_amount;
49149 total_render_length += increase_amount;
49150 }
49151 }
49152 }
49153
49154 // update the footer with the column counts
49155 UpdateColumnCountFooter(column_count, pruned_columns);
49156
49157 bool added_split_column = false;
49158 vector<idx_t> new_widths;
49159 for (idx_t c = 0; c < column_count; c++) {
49160 if (pruned_columns.find(c) == pruned_columns.end()) {
49161 column_map.push_back(c);
49162 new_widths.push_back(column_widths[c]);
49163 } else if (!added_split_column) {
49164 // "..."
49165 column_map.push_back(optional_idx());
49166 new_widths.push_back(config.DOTDOTDOT_LENGTH);
49167 added_split_column = true;
49168 }
49169 }
49170 column_widths = std::move(new_widths);
49171 column_count = column_widths.size();
49172
49173 // prune columns from the header rows
49174 for (auto &header_row : header_rows) {
49175 vector<BoxRenderValue> new_rows;
49176 for (auto &c : column_map) {
49177 if (!c.IsValid()) {
49178 // split column - skip
49179 continue;
49180 }
49181 new_rows.push_back(std::move(header_row.values[c.GetIndex()]));
49182 }
49183 header_row.values = std::move(new_rows);
49184 }
49185
49186 idx_t row_count = 0;
49187 for (auto &collection : collections) {
49188 row_count += collection.render_values->Count();
49189 }
49190
49191 // check if we shortened any columns that would be rendered and if we can expand them
49192 // we only expand columns in the ".mode rows", and only if we haven't hidden any columns
49193 if (shortened_columns && config.render_mode == RenderMode::ROWS && row_count > 0 &&
49194 row_count + 5 < config.max_rows && pruned_columns.empty()) {
49195 max_rows_per_row = MaxValue<idx_t>(1, config.max_rows <= 5 ? 0 : (config.max_rows - 5) / row_count);
49196 if (max_rows_per_row > 1) {
49197 // we can expand rows - check if we should expand any rows
49198 expand_rows = true;
49199 }
49200 }
49201}
::int64_t int64_t

◆ RenderHeader()

void duckdb::BoxRendererImplementation::RenderHeader ( BaseResultRenderer ss)
private
49312 {
49313 // render the header
49314 RenderLayoutLine(ss, config.HORIZONTAL, config.TMIDDLE, config.LTCORNER, config.RTCORNER);
49315
49316 vector<BoxRenderRow> rows_to_render;
49317 for (auto &row : header_rows) {
49318 if (expand_rows) {
49319 PotentiallyExpandRow(row, rows_to_render, max_rows_per_row, is_first_row);
49320 } else {
49321 rows_to_render.push_back(std::move(row));
49322 }
49323 }
49324 for (auto &row : rows_to_render) {
49325 if (context.IsInterrupted()) {
49326 return;
49327 }
49328 RenderRow(ss, row);
49329 }
49330 if (result.Count() > 0) {
49331 BoxRenderRow separator(RenderRowType::SEPARATOR);
49332 RenderRow(ss, separator);
49333 }
49334}

◆ RenderValues()

void duckdb::BoxRendererImplementation::RenderValues ( BaseResultRenderer ss,
vector< RenderDataCollection > &  collections 
)
private
49336 {
49337 // render the values
49338 vector<column_t> columns_to_scan;
49339 vector<column_t> scan_indexes;
49340 for (idx_t c = 0; c < column_map.size(); c++) {
49341 auto column_idx = column_map[c];
49342 if (column_idx.IsValid()) {
49343 auto scan_column_idx = column_idx.GetIndex();
49344 columns_to_scan.push_back(scan_column_idx);
49345 scan_indexes.push_back(scan_column_idx * 2);
49346 scan_indexes.push_back(scan_column_idx * 2 + 1);
49347 }
49348 }
49349
49350 // prepare the values for rendering
49351 bool render_divider = false;
49352 vector<BoxRenderRow> last_rendered_rows;
49353 idx_t row_idx = 0;
49354 bool is_first_collection = true;
49355 for (auto &render_collection : collections) {
49356 auto &collection = *render_collection.render_values;
49357 if (collection.Count() == 0) {
49358 continue;
49359 }
49360 if (!is_first_collection) {
49361 render_divider = true;
49362 }
49363 is_first_collection = false;
49364
49365 for (auto &chunk : collection.Chunks(scan_indexes)) {
49366 vector<BoxRenderRow> chunk_rows;
49367 chunk_rows.resize(chunk.size());
49368 for (idx_t c = 0; c < columns_to_scan.size(); c++) {
49369 auto &string_vector = render_collection.Values(chunk, c);
49370 auto &render_lengths_vector = render_collection.RenderLengths(chunk, c);
49371
49372 auto string_data = FlatVector::GetData<string_t>(string_vector);
49373 auto render_length_data = FlatVector::GetData<uint64_t>(render_lengths_vector);
49374 for (idx_t r = 0; r < chunk.size(); r++) {
49375 if (context.IsInterrupted()) {
49376 return;
49377 }
49378 string render_value;
49379 ResultRenderType render_type;
49380 ValueRenderAlignment alignment;
49381 optional_idx column_idx;
49382 if (FlatVector::IsNull(string_vector, r)) {
49383 render_value = config.null_value;
49384 render_type = ResultRenderType::NULL_VALUE;
49385 } else {
49386 render_value = string_data[r].GetString();
49387 render_type = ResultRenderType::VALUE;
49388 }
49389 if (config.render_mode == RenderMode::ROWS) {
49390 // in rows mode we select alignment for each column based on the type
49391 column_idx = columns_to_scan[c];
49392 alignment = TypeAlignment(result_types[column_idx.GetIndex()]);
49393 } else {
49394 // in columns mode we left-align the header rows, and right-align the values
49395 switch (c) {
49396 case 0:
49397 render_type = ResultRenderType::COLUMN_NAME;
49398 alignment = ValueRenderAlignment::LEFT;
49399 break;
49400 case 1:
49401 render_type = ResultRenderType::COLUMN_TYPE;
49402 alignment = ValueRenderAlignment::LEFT;
49403 break;
49404 default:
49405 render_type = ResultRenderType::VALUE;
49406 alignment = ValueRenderAlignment::RIGHT;
49407 // for columns rendering mode - the type for this value is determined by the row index
49408 column_idx = row_idx + r;
49409 break;
49410 }
49411 }
49412 if (config.large_number_rendering == LargeNumberRendering::FOOTER) {
49413 // when rendering the large number footer we align to the middle
49414 alignment = ValueRenderAlignment::MIDDLE;
49415 if (row_idx + r == 1) {
49416 // large number footers should be rendered as NULL values
49417 render_type = ResultRenderType::NULL_VALUE;
49418 }
49419 }
49420 auto render_length = render_length_data[r];
49421 chunk_rows[r].values.emplace_back(std::move(render_value), render_type, alignment, column_idx,
49422 render_length);
49423 }
49424 }
49425 row_idx += chunk.size();
49426 vector<BoxRenderRow> rows_to_render;
49427 for (auto &row : chunk_rows) {
49428 if (context.IsInterrupted()) {
49429 return;
49430 }
49431 if (render_divider) {
49432 RenderDivider(ss, last_rendered_rows.back(), row);
49433 render_divider = false;
49434 }
49435 if (expand_rows) {
49436 PotentiallyExpandRow(row, rows_to_render, max_rows_per_row, is_first_row);
49437 } else {
49438 rows_to_render.push_back(std::move(row));
49439 }
49440 is_first_row = false;
49441 }
49442 for (auto &row : rows_to_render) {
49443 if (context.IsInterrupted()) {
49444 return;
49445 }
49446 RenderRow(ss, row);
49447 }
49448 last_rendered_rows = std::move(rows_to_render);
49449 }
49450 }
49451}

◆ RenderRow()

void duckdb::BoxRendererImplementation::RenderRow ( BaseResultRenderer ss,
BoxRenderRow row 
)
private
49281 {
49282 auto column_count = column_widths.size();
49283 if (row.row_type == RenderRowType::SEPARATOR) {
49284 // render separator
49285 RenderLayoutLine(ss, config.HORIZONTAL, config.MIDDLE, config.LMIDDLE, config.RMIDDLE);
49286 return;
49287 }
49288 if (row.row_type == RenderRowType::DIVIDER) {
49289 throw InternalException("Divider should be rendered before");
49290 }
49291 // render row values
49292 idx_t value_idx = 0;
49293 BoxRenderValue split_value(config.DOTDOTDOT, ResultRenderType::LAYOUT, ValueRenderAlignment::MIDDLE, optional_idx(),
49294 1);
49295 for (idx_t column_idx = 0; column_idx < column_count; column_idx++) {
49296 auto &render_value = column_map[column_idx].IsValid() ? row.values[value_idx++] : split_value;
49297 auto render_mode = render_value.render_mode;
49298 auto alignment = render_value.alignment;
49299 if (render_mode == ResultRenderType::NULL_VALUE || render_mode == ResultRenderType::VALUE) {
49300 ss.SetValueColumn(render_value.column_idx);
49301 if (!render_value.decomposed && CanHighlight(render_value)) {
49302 HighlightValue(render_value);
49303 }
49304 }
49305 RenderValue(ss, render_value.text, column_widths[column_idx], render_mode, render_value.annotations, alignment,
49306 render_value.render_width);
49307 }
49308 ss << config.VERTICAL;
49309 ss << '\n';
49310}

◆ RenderDivider()

void duckdb::BoxRendererImplementation::RenderDivider ( BaseResultRenderer ss,
const BoxRenderRow prev_row,
const BoxRenderRow next_row 
)
private
49221 {
49222 // generate three new rows
49223 const idx_t divider_row_count = 3;
49224 vector<BoxRenderRow> divider_rows;
49225 for (idx_t d = 0; d < divider_row_count; d++) {
49226 divider_rows.emplace_back(RenderRowType::ROW_VALUES);
49227 }
49228
49229 // now generate the dividers for each of the columns
49230 for (idx_t c = 0; c < prev_row.values.size(); c++) {
49231 string str;
49232 auto &prev_value = prev_row.values[c];
49233 auto &next_value = next_row.values[c];
49234 ValueRenderAlignment alignment = prev_value.alignment;
49235 if (alignment == ValueRenderAlignment::MIDDLE) {
49236 // for middle alignment we don't have to do anything - just push a dot
49237 str = config.DOT;
49238 } else {
49239 // for left / right alignment we want to be in the middle of the prev / next value
49240 auto top_length = MinValue<idx_t>(column_widths[c], Utf8Proc::RenderWidth(prev_value.text));
49241 auto bottom_length = MinValue<idx_t>(column_widths[c], Utf8Proc::RenderWidth(next_value.text));
49242 auto dot_length = MinValue<idx_t>(top_length, bottom_length);
49243 if (top_length == 0) {
49244 dot_length = bottom_length;
49245 } else if (bottom_length == 0) {
49246 dot_length = top_length;
49247 }
49248 if (dot_length > 1) {
49249 auto padding = dot_length - 1;
49250 idx_t left_padding, right_padding;
49251 switch (alignment) {
49252 case ValueRenderAlignment::LEFT:
49253 left_padding = padding / 2;
49254 right_padding = padding - left_padding;
49255 break;
49256 case ValueRenderAlignment::RIGHT:
49257 right_padding = padding / 2;
49258 left_padding = padding - right_padding;
49259 break;
49260 default:
49261 throw InternalException("Unrecognized value renderer alignment");
49262 }
49263 str = string(left_padding, ' ') + config.DOT + string(right_padding, ' ');
49264 } else {
49265 if (dot_length == 0) {
49266 // everything is empty
49267 alignment = ValueRenderAlignment::MIDDLE;
49268 }
49269 str = config.DOT;
49270 }
49271 }
49272 for (idx_t d = 0; d < divider_row_count; d++) {
49273 divider_rows[d].values.emplace_back(str, ResultRenderType::LAYOUT, alignment);
49274 }
49275 }
49276 for (auto &divider : divider_rows) {
49277 RenderRow(ss, divider);
49278 }
49279}

◆ PotentiallyExpandRow()

void duckdb::BoxRendererImplementation::PotentiallyExpandRow ( BoxRenderRow row,
vector< BoxRenderRow > &  rows,
idx_t  max_rows_per_row,
bool  is_first_row 
)
private
48858 {
48859 // check if this row has truncated columns
48860 vector<BoxRenderRow> extra_rows;
48861 for (idx_t c = 0; c < row.values.size(); c++) {
48862 if (CanPrettyPrint(row.values[c])) {
48863 PrettyPrintValue(row.values[c], max_rows_per_row, column_widths[c]);
48864 if (CanHighlight(row.values[c])) {
48865 HighlightValue(row.values[c]);
48866 }
48867 // FIXME: hacky
48868 row.values[c].render_width = column_widths[c] + 1;
48869 }
48870 auto render_width = row.values[c].render_width.GetIndex();
48871 if (render_width <= column_widths[c]) {
48872 // not shortened - skip
48873 continue;
48874 }
48875 // this value was shortened! try to stretch it out
48876 // first truncate what appears on the first row
48877 idx_t current_row = 0;
48878 idx_t current_pos = 0;
48879 idx_t current_render_width = 0;
48880 auto full_value = row.values[c].text;
48881 auto annotations = row.values[c].annotations;
48882 idx_t annotation_idx = 0;
48883 ResultRenderType active_render_mode = ResultRenderType::VALUE;
48884 row.values[c].annotations.clear();
48885 row.values[c].text = TruncateValue(full_value, column_widths[c], current_pos, current_render_width);
48886 row.values[c].render_width = current_render_width;
48887 row.values[c].decomposed = true;
48888 // copy over annotations
48889 for (; annotation_idx < annotations.size(); annotation_idx++) {
48890 if (annotations[annotation_idx].start >= current_pos) {
48891 break;
48892 }
48893 row.values[c].annotations.push_back(annotations[annotation_idx]);
48894 }
48895 while (current_pos < full_value.size()) {
48896 if (current_row >= extra_rows.size()) {
48897 if (extra_rows.size() >= max_rows_per_row + 1) {
48898 // we need to add an extra row but there's no space anymore - break
48899 break;
48900 }
48901 // add a new row with empty values
48902 extra_rows.emplace_back();
48903 for (auto &current_val : row.values) {
48904 extra_rows.back().values.emplace_back(string(), current_val.render_mode, current_val.alignment,
48905 current_val.column_idx);
48906 }
48907 }
48908 bool can_add_extra_row = current_row + 1 < extra_rows.size() || extra_rows.size() < max_rows_per_row;
48909 auto &extra_row = extra_rows[current_row++];
48910 idx_t start_pos = current_pos;
48911 // stretch out the remainder on this row
48912 current_render_width = 0;
48913 if (can_add_extra_row) {
48914 // if we can add an extra row after this row truncate it
48915 extra_row.values[c].text =
48916 TruncateValue(full_value, column_widths[c], current_pos, current_render_width);
48917 } else {
48918 // if we cannot add an extra row after this just throw all remaining text on this row
48919 extra_row.values[c].text = full_value.substr(current_pos);
48920 current_render_width = Utf8Proc::RenderWidth(extra_row.values[c].text);
48921 current_pos = full_value.size();
48922 }
48923 extra_row.values[c].render_width = current_render_width;
48924 extra_row.values[c].decomposed = true;
48925 // copy over annotations
48926 if (active_render_mode != ResultRenderType::VALUE) {
48927 extra_row.values[c].annotations.emplace_back(active_render_mode, 0);
48928 }
48929 for (; annotation_idx < annotations.size(); annotation_idx++) {
48930 if (annotations[annotation_idx].start >= current_pos) {
48931 break;
48932 }
48933 annotations[annotation_idx].start -= start_pos;
48934 extra_row.values[c].annotations.push_back(annotations[annotation_idx]);
48935 active_render_mode = annotations[annotation_idx].render_mode;
48936 }
48937 }
48938 }
48939 // add an extra separator for all but the first row
48940 if (!is_first_row) {
48941 rows.emplace_back(RenderRowType::SEPARATOR);
48942 }
48943 rows.push_back(std::move(row));
48944 for (auto &extra_row : extra_rows) {
48945 rows.push_back(std::move(extra_row));
48946 }
48947}
int rows

◆ UpdateColumnCountFooter()

void duckdb::BoxRendererImplementation::UpdateColumnCountFooter ( idx_t  column_count,
const unordered_set< idx_t > &  pruned_columns 
)
private
47561 {
47562 if (pruned_columns.empty()) {
47563 // no pruned columns - no need to update the footer
47564 return;
47565 }
47566 if (config.render_mode == RenderMode::COLUMNS) {
47567 // in columns mode - pruned columns really means pruned rows
47568 footer.has_hidden_rows = true;
47569 idx_t shown_row_count = column_count - pruned_columns.size();
47570 footer.shown_str = to_string(shown_row_count - 2) + " shown";
47571 } else {
47572 footer.has_hidden_columns = true;
47573 idx_t shown_column_count = column_count - pruned_columns.size();
47574 footer.column_count_str += " (" + to_string(shown_column_count) + " shown)";
47575 }
47576}

◆ TruncateValue()

string duckdb::BoxRendererImplementation::TruncateValue ( const string &  value,
idx_t  column_width,
idx_t pos,
idx_t current_render_width 
)
private
47684 {
47685 return BoxRenderer::TruncateValue(value, column_width, pos, current_render_width);
47686}

◆ ComputeRowFooter()

void duckdb::BoxRendererImplementation::ComputeRowFooter ( idx_t  row_count,
idx_t  rendered_rows 
)
private
47530 {
47531 footer.column_count_str = to_string(result.ColumnCount()) + " column";
47532 if (result.ColumnCount() > 1) {
47533 footer.column_count_str += "s";
47534 }
47535 footer.row_count_str = FormatNumber(to_string(row_count)) + " rows";
47536 bool has_limited_rows = config.limit > 0 && row_count == config.limit;
47537 if (has_limited_rows) {
47538 footer.row_count_str = "? rows";
47539 }
47540 if (config.large_number_rendering == LargeNumberRendering::FOOTER && !has_limited_rows) {
47541 string readable_str = TryFormatLargeNumber(to_string(row_count));
47542 if (!readable_str.empty()) {
47543 footer.readable_rows_str = to_string(row_count) + " total";
47544 footer.row_count_str = readable_str + " rows";
47545 }
47546 }
47547 footer.has_hidden_rows = rendered_rows < row_count;
47548 if (footer.has_hidden_rows) {
47549 if (has_limited_rows) {
47550 footer.shown_str += ">" + FormatNumber(to_string(config.limit - 1)) + " rows, ";
47551 }
47552 footer.shown_str += FormatNumber(to_string(rendered_rows)) + " shown";
47553 }
47554 footer.must_show_footer = has_limited_rows || footer.has_hidden_rows || row_count == 0;
47555 footer.render_length = MaxValue<idx_t>(MaxValue<idx_t>(footer.row_count_str.size(), footer.shown_str.size() + 2),
47556 footer.readable_rows_str.size() + 2) +
47557 4;
47558}

◆ RenderFooter()

void duckdb::BoxRendererImplementation::RenderFooter ( BaseResultRenderer ss,
idx_t  row_count,
idx_t  column_count 
)
private
49453 {
49454 auto &row_count_str = footer.row_count_str;
49455 auto &column_count_str = footer.column_count_str;
49456 auto &readable_rows_str = footer.readable_rows_str;
49457 auto &shown_str = footer.shown_str;
49458 auto &has_hidden_columns = footer.has_hidden_columns;
49459 auto &has_hidden_rows = footer.has_hidden_rows;
49460 // check if we can merge the row_count_str, readable_rows_str and the shown_str
49461 auto minimum_length = row_count_str.size() + column_count_str.size() + 6;
49462 bool render_rows_and_columns = total_render_length >= minimum_length &&
49463 ((has_hidden_columns && row_count > 0) || (row_count >= 10 && column_count > 1));
49464 bool render_rows = total_render_length >= footer.render_length && (row_count == 0 || row_count >= 10);
49465 bool render_anything = true;
49466 if (!render_rows && !render_rows_and_columns) {
49467 render_anything = false;
49468 }
49469 // render the bottom of the result values, if there are any
49470 RenderLayoutLine(ss, config.HORIZONTAL, config.DMIDDLE, config.LDCORNER, config.RDCORNER);
49471 if (!render_anything) {
49472 return;
49473 }
49474 idx_t padding = total_render_length - row_count_str.size() - 4;
49475 if (render_rows_and_columns) {
49476 padding -= column_count_str.size();
49477 }
49478 string extra_render_str;
49479 // do we have to space to render the minimum_row_length and the shown string on the same row?
49480 idx_t shown_size = readable_rows_str.size() + shown_str.size() + (readable_rows_str.empty() ? 3 : 5);
49481 if (has_hidden_rows && padding >= shown_size) {
49482 // we have space - render it here
49483 extra_render_str = " (";
49484 extra_render_str += shown_str;
49485 if (!readable_rows_str.empty()) {
49486 extra_render_str += ", " + readable_rows_str;
49487 }
49488 extra_render_str += ")";
49489 D_ASSERT(extra_render_str.size() == shown_size);
49490 padding -= shown_size;
49491 readable_rows_str = string();
49492 shown_str = string();
49493 }
49494
49495 ss << " ";
49496 if (render_rows_and_columns) {
49497 ss.Render(ResultRenderType::FOOTER, row_count_str);
49498 if (!extra_render_str.empty()) {
49499 ss.Render(ResultRenderType::FOOTER, extra_render_str);
49500 }
49501 // can we add the hidden rows hint to this line?
49502 if ((has_hidden_columns || has_hidden_rows) && !config.hidden_rows_hint.empty() &&
49503 padding >= config.hidden_rows_hint.size() + 10) {
49504 // we can
49505 padding -= config.hidden_rows_hint.size();
49506 auto lpadding = padding / 2;
49507 auto rpadding = padding - lpadding;
49508 ss << string(lpadding, ' ');
49509 ss.Render(ResultRenderType::FOOTER, config.hidden_rows_hint);
49510 ss << string(rpadding, ' ');
49511 } else {
49512 // we can't - don't render it
49513 ss << string(padding, ' ');
49514 }
49515 ss.Render(ResultRenderType::FOOTER, column_count_str);
49516 } else if (render_rows) {
49517 idx_t lpadding = padding / 2;
49518 idx_t rpadding = padding - lpadding;
49519 ss << string(lpadding, ' ');
49520 ss.Render(ResultRenderType::FOOTER, row_count_str);
49521 if (!extra_render_str.empty()) {
49522 ss.Render(ResultRenderType::FOOTER, extra_render_str);
49523 }
49524 ss << string(rpadding, ' ');
49525 }
49526 ss << '\n';
49527 if (!readable_rows_str.empty() || !shown_str.empty()) {
49528 // we still need to render the readable rows/shown strings
49529 // check if we can merge the two onto one row
49530 idx_t combined_shown_length = readable_rows_str.size() + shown_str.size() + 4;
49531 if (!readable_rows_str.empty() && !shown_str.empty() && combined_shown_length <= total_render_length) {
49532 // we can! merge them
49533 ss << " ";
49534 ss.Render(ResultRenderType::FOOTER, shown_str);
49535 ss << string(total_render_length - combined_shown_length, ' ');
49536 ss.Render(ResultRenderType::FOOTER, readable_rows_str);
49537 ss << '\n';
49538 readable_rows_str = string();
49539 shown_str = string();
49540 }
49541 ValueRenderAlignment alignment =
49542 render_rows_and_columns ? ValueRenderAlignment::LEFT : ValueRenderAlignment::MIDDLE;
49543 vector<HighlightingAnnotation> annotations;
49544 if (!shown_str.empty()) {
49545 RenderValue(ss, "(" + shown_str + ")", total_render_length - 4, ResultRenderType::FOOTER, annotations,
49546 alignment, optional_idx(), " ");
49547 ss << '\n';
49548 }
49549 if (!readable_rows_str.empty()) {
49550 RenderValue(ss, "(" + readable_rows_str + ")", total_render_length - 4, ResultRenderType::FOOTER,
49551 annotations, alignment, optional_idx(), " ");
49552 ss << '\n';
49553 }
49554 }
49555}
string hidden_rows_hint
Hidden rows hint.
Definition duckdb.cpp:35686

◆ FormatNumber()

string duckdb::BoxRendererImplementation::FormatNumber ( const string &  input)
private
48216 {
48217 if (config.large_number_rendering == LargeNumberRendering::ALL) {
48218 // when large number rendering is set to ALL, we try to format all numbers as large numbers
48219 auto number = TryFormatLargeNumber(input);
48220 if (!number.empty()) {
48221 return number;
48222 }
48223 }
48224 if (config.decimal_separator == '\0' && config.thousand_separator == '\0') {
48225 // no thousand separator
48226 return input;
48227 }
48228 // first check how many digits there are (preceding any decimal point)
48229 idx_t character_count = 0;
48230 for (auto c : input) {
48231 if (!StringUtil::CharacterIsDigit(c)) {
48232 break;
48233 }
48234 character_count++;
48235 }
48236 // find the position of the first thousand separator
48237 idx_t separator_position = character_count % 3 == 0 ? 3 : character_count % 3;
48238 // now add the thousand separators
48239 string result;
48240 for (idx_t c = 0; c < character_count; c++) {
48241 if (c == separator_position && config.thousand_separator != '\0') {
48242 result += config.thousand_separator;
48243 separator_position += 3;
48244 }
48245 result += input[c];
48246 }
48247 // add any remaining characters
48248 for (idx_t c = character_count; c < input.size(); c++) {
48249 if (input[c] == '.' && config.decimal_separator != '\0') {
48250 result += config.decimal_separator;
48251 } else {
48252 result += input[c];
48253 }
48254 }
48255 return result;
48256}
char decimal_separator
Decimal separator (if any)
Definition duckdb.cpp:35678
char thousand_separator
Thousand separator (if any)
Definition duckdb.cpp:35680

◆ ConvertRenderValue() [1/2]

string duckdb::BoxRendererImplementation::ConvertRenderValue ( const string &  input,
const LogicalType type 
)
private
48258 {
48259 switch (type.id()) {
48260 case LogicalTypeId::TINYINT:
48261 case LogicalTypeId::SMALLINT:
48262 case LogicalTypeId::INTEGER:
48263 case LogicalTypeId::BIGINT:
48264 case LogicalTypeId::HUGEINT:
48265 case LogicalTypeId::UTINYINT:
48266 case LogicalTypeId::USMALLINT:
48267 case LogicalTypeId::UINTEGER:
48268 case LogicalTypeId::UBIGINT:
48269 case LogicalTypeId::UHUGEINT:
48270 case LogicalTypeId::DECIMAL:
48271 case LogicalTypeId::FLOAT:
48272 case LogicalTypeId::DOUBLE:
48273 return FormatNumber(input);
48274 default:
48275 return ConvertRenderValue(input);
48276 }
48277}

◆ ConvertRenderValue() [2/2]

string duckdb::BoxRendererImplementation::ConvertRenderValue ( const string &  input)
private
48164 {
48165 string result;
48166 result.reserve(input.size());
48167 for (idx_t c = 0; c < input.size(); c++) {
48168 data_t byte_value = const_data_ptr_cast(input.c_str())[c];
48169 if (byte_value < 32) {
48170 // ASCII control character
48171 result += "\\";
48172 switch (input[c]) {
48173 case 7:
48174 // bell
48175 result += 'a';
48176 break;
48177 case 8:
48178 // backspace
48179 result += 'b';
48180 break;
48181 case 9:
48182 // tab
48183 result += 't';
48184 break;
48185 case 10:
48186 // newline
48187 result += 'n';
48188 break;
48189 case 11:
48190 // vertical tab
48191 result += 'v';
48192 break;
48193 case 12:
48194 // form feed
48195 result += 'f';
48196 break;
48197 case 13:
48198 // cariage return
48199 result += 'r';
48200 break;
48201 case 27:
48202 // escape
48203 result += 'e';
48204 break;
48205 default:
48206 result += to_string(byte_value);
48207 break;
48208 }
48209 } else {
48210 result += input[c];
48211 }
48212 }
48213 return result;
48214}
uint8_t data_t
data pointers
Definition duckdb.hpp:246

◆ RenderLayoutLine()

void duckdb::BoxRendererImplementation::RenderLayoutLine ( BaseResultRenderer ss,
const char layout,
const char boundary,
const char left_corner,
const char right_corner 
)
private
49204 {
49205 // render the top line
49206 ss << left_corner;
49207 idx_t column_index = 0;
49208 for (idx_t k = 0; k < total_render_length - 2; k++) {
49209 if (column_index < column_boundary_positions.size() && k == column_boundary_positions[column_index]) {
49210 ss << boundary;
49211 column_index++;
49212 } else {
49213 ss << layout;
49214 }
49215 }
49216 ss << right_corner;
49217 ss << '\n';
49218}

◆ TryFormatLargeNumber()

string duckdb::BoxRendererImplementation::TryFormatLargeNumber ( const string &  numeric)
private

Try to format a large number in a readable way (e.g. 1234567 -> 1.23 million)

47884 {
47885 return BoxRenderer::TryFormatLargeNumber(numeric, config.decimal_separator);
47886}

◆ CanPrettyPrint()

bool duckdb::BoxRendererImplementation::CanPrettyPrint ( const BoxRenderValue render_value)
private
48826 {
48827 if (!render_value.column_idx.IsValid()) {
48828 return false;
48829 }
48830 auto &type = result.Types()[render_value.column_idx.GetIndex()];
48831 return type.IsJSONType() || type.IsNested();
48832}

◆ CanHighlight()

bool duckdb::BoxRendererImplementation::CanHighlight ( const BoxRenderValue render_value)
private
48834 {
48835 if (!render_value.column_idx.IsValid()) {
48836 return false;
48837 }
48838 auto &type = result.Types()[render_value.column_idx.GetIndex()];
48839 return type.IsJSONType() || type.IsNested();
48840}

◆ PrettyPrintValue()

void duckdb::BoxRendererImplementation::PrettyPrintValue ( BoxRenderValue render_value,
idx_t  max_rows,
idx_t  max_width 
)
private
48842 {
48843 if (!CanPrettyPrint(render_value)) {
48844 return;
48845 }
48846 JSONFormatter::FormatValue(render_value, max_rows, max_width);
48847}

◆ HighlightValue()

void duckdb::BoxRendererImplementation::HighlightValue ( BoxRenderValue render_value)
private
48849 {
48850 if (!CanHighlight(render_value)) {
48851 return;
48852 }
48853 JSONHighlighter highlighter(render_value);
48854 highlighter.Process(render_value.text);
48855}

The documentation for this struct was generated from the following file: