#include "utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for ipv4-ipv6 platform-specific stuff #include #include #include #include #include #include #include #include "clickhouse/types/types.h" #include "absl/numeric/int128.h" namespace { using namespace clickhouse; std::ostream & printColumnValue(const ColumnRef& c, const size_t row, std::ostream & ostr); struct DateTimeValue { explicit DateTimeValue(const time_t & v) : value(v) {} template explicit DateTimeValue(const T & v) : value(v) {} const time_t value; }; std::ostream& operator<<(std::ostream & ostr, const DateTimeValue & time) { const auto t = std::gmtime(&time.value); char buffer[] = "2015-05-18 07:40:12\0\0"; std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", t); return ostr << buffer; } template ().At(0)) > bool doPrintValue(const ColumnRef & c, const size_t row, std::ostream & ostr) { if (const auto & casted_c = c->As()) { if constexpr (is_container_v> && !std::is_same_v && !std::is_same_v) { ostr << PrintContainer{static_cast(casted_c->At(row))}; } else { ostr << static_cast(casted_c->At(row)); } return true; } return false; } template bool doPrintEnumValue(const ColumnRef & c, const size_t row, std::ostream & ostr) { if (const auto & casted_c = c->As()) { // via temporary stream to preserve fill and alignment of the ostr std::stringstream sstr; sstr << casted_c->NameAt(row) << " (" << static_cast(casted_c->At(row)) << ")"; ostr << sstr.str(); return true; } return false; } template <> bool doPrintValue(const ColumnRef & c, const size_t row, std::ostream & ostr) { return doPrintEnumValue(c, row, ostr); } template <> bool doPrintValue(const ColumnRef & c, const size_t row, std::ostream & ostr) { return doPrintEnumValue(c, row, ostr); } template <> bool doPrintValue(const ColumnRef & c, const size_t row, std::ostream & ostr) { // via temporary stream to preserve fill and alignment of the ostr std::stringstream sstr; if (const auto & array_col = c->As()) { const auto & row_values = array_col->GetAsColumn(row); sstr << "["; for (size_t i = 0; i < row_values->Size(); ++i) { printColumnValue(row_values, i, sstr); if (i < row_values->Size() - 1) sstr << ", "; } sstr << "]"; ostr << sstr.str(); return true; } return false; } template <> bool doPrintValue(const ColumnRef & c, const size_t row, std::ostream & ostr) { if (const auto & tupple_col = c->As()) { ostr << "("; for (size_t i = 0; i < tupple_col->TupleSize(); ++i) { const auto & nested_col = (*tupple_col)[i]; printColumnValue(nested_col, row, ostr); if (i < tupple_col->TupleSize() - 1) ostr << ", "; } ostr << ")"; return true; } return false; } template <> bool doPrintValue(const ColumnRef & c, const size_t row, std::ostream & ostr) { if (const auto & uuid_col = c->As()) { ostr << ToString(uuid_col->At(row)); return true; } return false; } template <> bool doPrintValue(const ColumnRef & c, const size_t row, std::ostream & ostr) { // via temporary stream to preserve fill and alignment of the ostr std::stringstream sstr; if (const auto & map_col = c->As()) { sstr << "{"; const auto tuples = map_col->GetAsColumn(row); for (size_t i = 0; i < tuples->Size(); ++i) { printColumnValue(tuples, i, sstr); if (i < tuples->Size() - 1) sstr << ", "; } sstr << "}"; ostr << sstr.str(); return true; } return false; } std::ostream & printColumnValue(const ColumnRef& c, const size_t row, std::ostream & ostr) { const auto r = false || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr) || doPrintValue(c, row, ostr); if (!r) ostr << "Unable to print value of type " << c->GetType().GetName(); return ostr; } struct ColumnValue { const ColumnRef& c; size_t row; }; std::ostream & operator<<(std::ostream & ostr, const ColumnValue& v) { return printColumnValue(v.c, v.row, ostr); } } std::ostream& operator<<(std::ostream & ostr, const PrettyPrintBlock & pretty_print_block) { // Pretty-print block: // - names of each column // - types of each column // - values of column row-by-row const auto & block = pretty_print_block.block; if (block.GetRowCount() == 0 || block.GetColumnCount() == 0) return ostr; std::vector column_width(block.GetColumnCount()); const auto horizontal_bar = '|'; const auto cross = '+'; const auto vertical_bar = '-'; std::stringstream sstr; for (auto i = block.begin(); i != block.end(); ++i) { auto width = column_width[i.ColumnIndex()] = std::max(i.Type()->GetName().size(), i.Name().size()); sstr << cross << std::setw(width + 2) << std::setfill(vertical_bar) << vertical_bar; } sstr << cross; const std::string split_line(sstr.str()); ostr << split_line << std::endl; // column name for (auto i = block.begin(); i != block.end(); ++i) { auto width = column_width[i.ColumnIndex()]; ostr << horizontal_bar << ' ' << std::setw(width) << i.Name() << ' '; } ostr << horizontal_bar << std::endl;; ostr << split_line << std::endl; // column type for (auto i = block.begin(); i != block.end(); ++i) { auto width = column_width[i.ColumnIndex()]; ostr << horizontal_bar << ' ' << std::setw(width) << i.Type()->GetName() << ' '; } ostr << horizontal_bar << std::endl;; ostr << split_line << std::endl; // values for (size_t row_index = 0; row_index < block.GetRowCount(); ++row_index) { for (auto i = block.begin(); i != block.end(); ++i) { auto width = column_width[i.ColumnIndex()]; ostr << horizontal_bar << ' ' << std::setw(width) << ColumnValue{i.Column(), row_index} << ' '; } ostr << horizontal_bar << std::endl; } ostr << split_line << std::endl; return ostr; } std::ostream& operator<<(std::ostream& ostr, const in_addr& addr) { char buf[INET_ADDRSTRLEN]; const char* ip_str = inet_ntop(AF_INET, &addr, buf, sizeof(buf)); if (!ip_str) return ostr << ""; return ostr << ip_str; } std::ostream& operator<<(std::ostream& ostr, const in6_addr& addr) { char buf[INET6_ADDRSTRLEN]; const char* ip_str = inet_ntop(AF_INET6, &addr, buf, sizeof(buf)); if (!ip_str) return ostr << ""; return ostr << ip_str; } namespace clickhouse { std::ostream& operator<<(std::ostream & ostr, const Block & block) { if (block.GetRowCount() == 0 || block.GetColumnCount() == 0) return ostr; for (size_t col = 0; col < block.GetColumnCount(); ++col) { const auto & c = block[col]; ostr << c->GetType().GetName() << " ["; for (size_t row = 0; row < block.GetRowCount(); ++row) { printColumnValue(c, row, ostr); if (row != block.GetRowCount() - 1) ostr << ", "; } ostr << "]"; if (col != block.GetColumnCount() - 1) ostr << "\n"; } return ostr; } std::ostream& operator<<(std::ostream & ostr, const Type & type) { return ostr << type.GetName(); } std::ostream & operator<<(std::ostream & ostr, const ServerInfo & server_info) { return ostr << server_info.name << "/" << server_info.display_name << " ver " << server_info.version_major << "." << server_info.version_minor << "." << server_info.version_patch << " (" << server_info.revision << ")"; } std::ostream & operator<<(std::ostream & ostr, const Profile & profile) { return ostr << "rows : " << profile.rows << " blocks : " << profile.blocks << " bytes : " << profile.bytes << " rows_before_limit : " << profile.rows_before_limit << " applied_limit : " << profile.applied_limit << " calculated_rows_before_limit : " << profile.calculated_rows_before_limit; } std::ostream & operator<<(std::ostream & ostr, const Progress & progress) { return ostr << "rows : " << progress.rows << " bytes : " << progress.bytes << " total_rows : " << progress.total_rows << " written_rows : " << progress.written_rows << " written_bytes : " << progress.written_bytes; } std::ostream& operator<<(std::ostream& ostr, const ItemView& item_view) { ostr << "ItemView {" << clickhouse::Type::TypeName(item_view.type) << " : "; switch (item_view.type) { case Type::Void: ostr << "--void--"; break; case Type::Int8: ostr << static_cast(item_view.get()); break; case Type::Int16: ostr << static_cast(item_view.get()); break; case Type::Int32: ostr << static_cast(item_view.get()); break; case Type::Int64: ostr << item_view.get(); break; case Type::UInt8: ostr << static_cast(item_view.get()); break; case Type::UInt16: ostr << static_cast(item_view.get()); break; case Type::UInt32: ostr << static_cast(item_view.get()); break; case Type::UInt64: ostr << item_view.get(); break; case Type::Float32: ostr << static_cast(item_view.get()); break; case Type::Float64: ostr << static_cast(item_view.get()); break; case Type::String: case Type::FixedString: ostr << "\"" << item_view.data << "\" (" << item_view.data.size() << " bytes)"; break; case Type::Date: ostr << DateTimeValue(item_view.get() * 86400); break; case Type::Date32: ostr << DateTimeValue(item_view.get()); break; case Type::DateTime: ostr << DateTimeValue(item_view.get()); break; case Type::DateTime64: { if (item_view.data.size() == sizeof(int32_t)) { ostr << DateTimeValue(item_view.get()); } else if (item_view.data.size() == sizeof(int64_t)) { ostr << DateTimeValue(item_view.get()); } else if (item_view.data.size() == sizeof(Int128)) { ostr << DateTimeValue(item_view.get()); } else { throw std::runtime_error("Invalid data size of ItemView of type DateTime64"); } break; } case Type::Enum8: ostr << static_cast(item_view.get()); break; case Type::Enum16: ostr << static_cast(item_view.get()); break; case Type::UUID: { const auto & uuid_vals = reinterpret_cast(item_view.data.data()); ostr << ToString(clickhouse::UUID{uuid_vals[0], uuid_vals[1]}); break; } case Type::IPv4: { in_addr addr; addr.s_addr = ntohl(item_view.get()); ostr << addr; break; } case Type::IPv6: ostr << *reinterpret_cast(item_view.AsBinaryData().data()); break; case Type::Int128: ostr << item_view.get(); break; case Type::UInt128: ostr << item_view.get(); break; case Type::Decimal: { if (item_view.data.size() == sizeof(int32_t)) { ostr << item_view.get(); } else if (item_view.data.size() == sizeof(int64_t)) { ostr << item_view.get(); } else if (item_view.data.size() == sizeof(Int128)) { ostr << item_view.get(); } else { throw std::runtime_error("Invalid data size of ItemView of type Decimal"); } } break; case Type::Decimal32: ostr << DateTimeValue(item_view.get()); break; case Type::Decimal64: ostr << DateTimeValue(item_view.get()); break; case Type::Decimal128: ostr << DateTimeValue(item_view.get()); break; // Unsupported types. i.e. there shouldn't be `ItemView`s of those types in practice. // either because GetItem() is not implemented for corresponding column type // OR this type code is never used, for `ItemView`s (but type code of wrapped column is). case Type::LowCardinality: case Type::Array: case Type::Nullable: case Type::Tuple: case Type::Map: case Type::Point: case Type::Ring: case Type::Polygon: case Type::MultiPolygon: { throw std::runtime_error("Invalid data size of ItemView of type " + std::string(Type::TypeName(item_view.type))); } }; return ostr << "}"; } } uint64_t versionNumber(const ServerInfo & server_info) { return versionNumber(server_info.version_major, server_info.version_minor, server_info.version_patch, server_info.revision); } std::string ToString(const clickhouse::UUID& v) { std::string result(36, 0); // ffff ff ff ss ssssss const int count = std::snprintf(result.data(), result.size() + 1, "%.8" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.12" PRIx64, v.first >> 32, (v.first >> 16) & 0xffff, v.first & 0xffff, v.second >> 48, v.second & 0xffffffffffff); if (count != 36) { throw std::runtime_error("Error while converting UUID to string"); } return result; }