#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wsign-compare" #endif #include #ifdef __clang__ #pragma clang diagnostic pop #endif #include namespace DB { namespace ErrorCodes { extern const int CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER; } /// Helper functions for formatted and binary output. inline void writeChar(char x, WriteBuffer & buf) { buf.nextIfAtEnd(); *buf.position() = x; ++buf.position(); } /// Write the same character n times. inline void writeChar(char c, size_t n, WriteBuffer & buf) { while (n) { buf.nextIfAtEnd(); size_t count = std::min(n, buf.available()); memset(buf.position(), c, count); n -= count; buf.position() += count; } } /// Write POD-type in native format. It's recommended to use only with packed (dense) data types. template inline void writePODBinary(const T & x, WriteBuffer & buf) { buf.write(reinterpret_cast(&x), sizeof(x)); /// NOLINT } template inline void writeIntBinary(const T & x, WriteBuffer & buf) { writePODBinary(x, buf); } template inline void writeFloatBinary(const T & x, WriteBuffer & buf) { writePODBinary(x, buf); } inline void writeStringBinary(const std::string & s, WriteBuffer & buf) { writeVarUInt(s.size(), buf); buf.write(s.data(), s.size()); } inline void writeStringBinary(StringRef s, WriteBuffer & buf) { writeVarUInt(s.size, buf); buf.write(s.data, s.size); } inline void writeStringBinary(const char * s, WriteBuffer & buf) { writeStringBinary(StringRef{s}, buf); } inline void writeStringBinary(std::string_view s, WriteBuffer & buf) { writeStringBinary(StringRef{s}, buf); } template void writeVectorBinary(const std::vector & v, WriteBuffer & buf) { writeVarUInt(v.size(), buf); for (typename std::vector::const_iterator it = v.begin(); it != v.end(); ++it) writeBinary(*it, buf); } inline void writeBoolText(bool x, WriteBuffer & buf) { writeChar(x ? '1' : '0', buf); } template inline size_t writeFloatTextFastPath(T x, char * buffer) { Int64 result = 0; if constexpr (std::is_same_v) { /// The library Ryu has low performance on integers. /// This workaround improves performance 6..10 times. if (DecomposedFloat64(x).isIntegerInRepresentableRange()) result = itoa(Int64(x), buffer) - buffer; else result = jkj::dragonbox::to_chars_n(x, buffer) - buffer; } else { if (DecomposedFloat32(x).isIntegerInRepresentableRange()) result = itoa(Int32(x), buffer) - buffer; else result = jkj::dragonbox::to_chars_n(x, buffer) - buffer; } if (result <= 0) throw Exception("Cannot print floating point number", ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER); return result; } template inline void writeFloatText(T x, WriteBuffer & buf) { static_assert(std::is_same_v || std::is_same_v, "Argument for writeFloatText must be float or double"); using Converter = DoubleConverter; if (likely(buf.available() >= Converter::MAX_REPRESENTATION_LENGTH)) { buf.position() += writeFloatTextFastPath(x, buf.position()); return; } Converter::BufferType buffer; size_t result = writeFloatTextFastPath(x, buffer); buf.write(buffer, result); } inline void writeString(const char * data, size_t size, WriteBuffer & buf) { buf.write(data, size); } // Otherwise StringRef and string_view overloads are ambiguous when passing string literal. Prefer std::string_view void writeString(std::same_as auto ref, WriteBuffer & buf) { writeString(ref.data, ref.size, buf); } inline void writeString(std::string_view ref, WriteBuffer & buf) { writeString(ref.data(), ref.size(), buf); } /** Writes a C-string without creating a temporary object. If the string is a literal, then `strlen` is executed at the compilation stage. * Use when the string is a literal. */ #define writeCString(s, buf) \ (buf).write((s), strlen(s)) /** Writes a string for use in the JSON format: * - the string is written in double quotes * - slash character '/' is escaped for compatibility with JavaScript * - bytes from the range 0x00-0x1F except `\b', '\f', '\n', '\r', '\t' are escaped as \u00XX * - code points U+2028 and U+2029 (byte sequences in UTF-8: e2 80 a8, e2 80 a9) are escaped as \u2028 and \u2029 * - it is assumed that string is in UTF-8, the invalid UTF-8 is not processed * - all other non-ASCII characters remain as is */ inline void writeJSONString(const char * begin, const char * end, WriteBuffer & buf, const FormatSettings & settings) { writeChar('"', buf); for (const char * it = begin; it != end; ++it) { switch (*it) { case '\b': writeChar('\\', buf); writeChar('b', buf); break; case '\f': writeChar('\\', buf); writeChar('f', buf); break; case '\n': writeChar('\\', buf); writeChar('n', buf); break; case '\r': writeChar('\\', buf); writeChar('r', buf); break; case '\t': writeChar('\\', buf); writeChar('t', buf); break; case '\\': writeChar('\\', buf); writeChar('\\', buf); break; case '/': if (settings.json.escape_forward_slashes) writeChar('\\', buf); writeChar('/', buf); break; case '"': writeChar('\\', buf); writeChar('"', buf); break; default: UInt8 c = *it; if (c <= 0x1F) { /// Escaping of ASCII control characters. UInt8 higher_half = c >> 4; UInt8 lower_half = c & 0xF; writeCString("\\u00", buf); writeChar('0' + higher_half, buf); if (lower_half <= 9) writeChar('0' + lower_half, buf); else writeChar('A' + lower_half - 10, buf); } else if (end - it >= 3 && it[0] == '\xE2' && it[1] == '\x80' && (it[2] == '\xA8' || it[2] == '\xA9')) { /// This is for compatibility with JavaScript, because unescaped line separators are prohibited in string literals, /// and these code points are alternative line separators. if (it[2] == '\xA8') writeCString("\\u2028", buf); if (it[2] == '\xA9') writeCString("\\u2029", buf); /// Byte sequence is 3 bytes long. We have additional two bytes to skip. it += 2; } else writeChar(*it, buf); } } writeChar('"', buf); } /** Will escape quote_character and a list of special characters('\b', '\f', '\n', '\r', '\t', '\0', '\\'). * - when escape_quote_with_quote is true, use backslash to escape list of special characters, * and use quote_character to escape quote_character. such as: 'hello''world' * - otherwise use backslash to escape list of special characters and quote_character */ template void writeAnyEscapedString(const char * begin, const char * end, WriteBuffer & buf) { const char * pos = begin; while (true) { /// On purpose we will escape more characters than minimally necessary. const char * next_pos = find_first_symbols<'\b', '\f', '\n', '\r', '\t', '\0', '\\', quote_character>(pos, end); if (next_pos == end) { buf.write(pos, next_pos - pos); break; } else { buf.write(pos, next_pos - pos); pos = next_pos; switch (*pos) { case '\b': writeChar('\\', buf); writeChar('b', buf); break; case '\f': writeChar('\\', buf); writeChar('f', buf); break; case '\n': writeChar('\\', buf); writeChar('n', buf); break; case '\r': writeChar('\\', buf); writeChar('r', buf); break; case '\t': writeChar('\\', buf); writeChar('t', buf); break; case '\0': writeChar('\\', buf); writeChar('0', buf); break; case '\\': writeChar('\\', buf); writeChar('\\', buf); break; case quote_character: { if constexpr (escape_quote_with_quote) writeChar(quote_character, buf); else writeChar('\\', buf); writeChar(quote_character, buf); break; } default: writeChar(*pos, buf); } ++pos; } } } inline void writeJSONString(std::string_view s, WriteBuffer & buf, const FormatSettings & settings) { writeJSONString(s.data(), s.data() + s.size(), buf, settings); } template void writeJSONNumber(T x, WriteBuffer & ostr, const FormatSettings & settings) { bool is_finite = isFinite(x); const bool need_quote = (is_integer && (sizeof(T) >= 8) && settings.json.quote_64bit_integers) || (settings.json.quote_denormals && !is_finite) || (is_floating_point && (sizeof(T) >= 8) && settings.json.quote_64bit_floats); if (need_quote) writeChar('"', ostr); if (is_finite) writeText(x, ostr); else if (!settings.json.quote_denormals) writeCString("null", ostr); else { if constexpr (std::is_floating_point_v) { if (std::signbit(x)) { if (isNaN(x)) writeCString("-nan", ostr); else writeCString("-inf", ostr); } else { if (isNaN(x)) writeCString("nan", ostr); else writeCString("inf", ostr); } } } if (need_quote) writeChar('"', ostr); } template void writeAnyEscapedString(std::string_view s, WriteBuffer & buf) { writeAnyEscapedString(s.data(), s.data() + s.size(), buf); } inline void writeEscapedString(const char * str, size_t size, WriteBuffer & buf) { writeAnyEscapedString<'\''>(str, str + size, buf); } inline void writeEscapedString(std::string_view ref, WriteBuffer & buf) { writeEscapedString(ref.data(), ref.size(), buf); } template void writeAnyQuotedString(const char * begin, const char * end, WriteBuffer & buf) { writeChar(quote_character, buf); writeAnyEscapedString(begin, end, buf); writeChar(quote_character, buf); } template void writeAnyQuotedString(std::string_view ref, WriteBuffer & buf) { writeAnyQuotedString(ref.data(), ref.data() + ref.size(), buf); } inline void writeQuotedString(const String & s, WriteBuffer & buf) { writeAnyQuotedString<'\''>(s, buf); } inline void writeQuotedString(StringRef ref, WriteBuffer & buf) { writeAnyQuotedString<'\''>(ref.toView(), buf); } inline void writeQuotedString(std::string_view ref, WriteBuffer & buf) { writeAnyQuotedString<'\''>(ref.data(), ref.data() + ref.size(), buf); } inline void writeDoubleQuotedString(const String & s, WriteBuffer & buf) { writeAnyQuotedString<'"'>(s, buf); } inline void writeDoubleQuotedString(StringRef s, WriteBuffer & buf) { writeAnyQuotedString<'"'>(s.toView(), buf); } inline void writeDoubleQuotedString(std::string_view s, WriteBuffer & buf) { writeAnyQuotedString<'"'>(s.data(), s.data() + s.size(), buf); } /// Outputs a string in backquotes. inline void writeBackQuotedString(StringRef s, WriteBuffer & buf) { writeAnyQuotedString<'`'>(s.toView(), buf); } /// Outputs a string in backquotes for MySQL. inline void writeBackQuotedStringMySQL(StringRef s, WriteBuffer & buf) { writeChar('`', buf); writeAnyEscapedString<'`', true>(s.data, s.data + s.size, buf); writeChar('`', buf); } /// Write quoted if the string doesn't look like and identifier. void writeProbablyBackQuotedString(StringRef s, WriteBuffer & buf); void writeProbablyDoubleQuotedString(StringRef s, WriteBuffer & buf); void writeProbablyBackQuotedStringMySQL(StringRef s, WriteBuffer & buf); /** Outputs the string in for the CSV format. * Rules: * - the string is outputted in quotation marks; * - the quotation mark inside the string is outputted as two quotation marks in sequence. */ template void writeCSVString(const char * begin, const char * end, WriteBuffer & buf) { writeChar(quote, buf); const char * pos = begin; while (true) { const char * next_pos = find_first_symbols(pos, end); if (next_pos == end) { buf.write(pos, end - pos); break; } else /// Quotation. { ++next_pos; buf.write(pos, next_pos - pos); writeChar(quote, buf); } pos = next_pos; } writeChar(quote, buf); } template void writeCSVString(const String & s, WriteBuffer & buf) { writeCSVString(s.data(), s.data() + s.size(), buf); } template void writeCSVString(StringRef s, WriteBuffer & buf) { writeCSVString(s.data, s.data + s.size, buf); } inline void writeXMLStringForTextElementOrAttributeValue(const char * begin, const char * end, WriteBuffer & buf) { const char * pos = begin; while (true) { const char * next_pos = find_first_symbols<'<', '&', '>', '"', '\''>(pos, end); if (next_pos == end) { buf.write(pos, end - pos); break; } else if (*next_pos == '<') { buf.write(pos, next_pos - pos); ++next_pos; writeCString("<", buf); } else if (*next_pos == '&') { buf.write(pos, next_pos - pos); ++next_pos; writeCString("&", buf); } else if (*next_pos == '>') { buf.write(pos, next_pos - pos); ++next_pos; writeCString(">", buf); } else if (*next_pos == '"') { buf.write(pos, next_pos - pos); ++next_pos; writeCString(""", buf); } else if (*next_pos == '\'') { buf.write(pos, next_pos - pos); ++next_pos; writeCString("'", buf); } pos = next_pos; } } inline void writeXMLStringForTextElementOrAttributeValue(std::string_view s, WriteBuffer & buf) { writeXMLStringForTextElementOrAttributeValue(s.data(), s.data() + s.size(), buf); } /// Writing a string to a text node in XML (not into an attribute - otherwise you need more escaping). inline void writeXMLStringForTextElement(const char * begin, const char * end, WriteBuffer & buf) { const char * pos = begin; while (true) { /// NOTE Perhaps for some XML parsers, you need to escape the zero byte and some control characters. const char * next_pos = find_first_symbols<'<', '&'>(pos, end); if (next_pos == end) { buf.write(pos, end - pos); break; } else if (*next_pos == '<') { buf.write(pos, next_pos - pos); ++next_pos; writeCString("<", buf); } else if (*next_pos == '&') { buf.write(pos, next_pos - pos); ++next_pos; writeCString("&", buf); } pos = next_pos; } } inline void writeXMLStringForTextElement(std::string_view s, WriteBuffer & buf) { writeXMLStringForTextElement(s.data(), s.data() + s.size(), buf); } void formatUUID(std::reverse_iterator src16, UInt8 * dst36); inline void writeUUIDText(const UUID & uuid, WriteBuffer & buf) { char s[36]; formatUUID(std::reverse_iterator(reinterpret_cast(&uuid) + 16), reinterpret_cast(s)); buf.write(s, sizeof(s)); } template inline void writeDateTime64FractionalText(typename DecimalType::NativeType fractional, UInt32 scale, WriteBuffer & buf) { static constexpr UInt32 MaxScale = DecimalUtils::max_precision; char data[20] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; static_assert(sizeof(data) >= MaxScale); for (Int32 pos = scale - 1; pos >= 0 && fractional; --pos, fractional /= DateTime64(10)) data[pos] += fractional % DateTime64(10); writeString(&data[0], static_cast(scale), buf); } static const char digits100[201] = "00010203040506070809" "10111213141516171819" "20212223242526272829" "30313233343536373839" "40414243444546474849" "50515253545556575859" "60616263646566676869" "70717273747576777879" "80818283848586878889" "90919293949596979899"; /// in YYYY-MM-DD format template inline void writeDateText(const LocalDate & date, WriteBuffer & buf) { if (reinterpret_cast(buf.position()) + 10 <= reinterpret_cast(buf.buffer().end())) { memcpy(buf.position(), &digits100[date.year() / 100 * 2], 2); buf.position() += 2; memcpy(buf.position(), &digits100[date.year() % 100 * 2], 2); buf.position() += 2; *buf.position() = delimiter; ++buf.position(); memcpy(buf.position(), &digits100[date.month() * 2], 2); buf.position() += 2; *buf.position() = delimiter; ++buf.position(); memcpy(buf.position(), &digits100[date.day() * 2], 2); buf.position() += 2; } else { buf.write(&digits100[date.year() / 100 * 2], 2); buf.write(&digits100[date.year() % 100 * 2], 2); buf.write(delimiter); buf.write(&digits100[date.month() * 2], 2); buf.write(delimiter); buf.write(&digits100[date.day() * 2], 2); } } template inline void writeDateText(DayNum date, WriteBuffer & buf) { writeDateText(LocalDate(date), buf); } template inline void writeDateText(ExtendedDayNum date, WriteBuffer & buf) { writeDateText(LocalDate(date), buf); } /// In the format YYYY-MM-DD HH:MM:SS template inline void writeDateTimeText(const LocalDateTime & datetime, WriteBuffer & buf) { if (reinterpret_cast(buf.position()) + 19 <= reinterpret_cast(buf.buffer().end())) { memcpy(buf.position(), &digits100[datetime.year() / 100 * 2], 2); buf.position() += 2; memcpy(buf.position(), &digits100[datetime.year() % 100 * 2], 2); buf.position() += 2; *buf.position() = date_delimeter; ++buf.position(); memcpy(buf.position(), &digits100[datetime.month() * 2], 2); buf.position() += 2; *buf.position() = date_delimeter; ++buf.position(); memcpy(buf.position(), &digits100[datetime.day() * 2], 2); buf.position() += 2; *buf.position() = between_date_time_delimiter; ++buf.position(); memcpy(buf.position(), &digits100[datetime.hour() * 2], 2); buf.position() += 2; *buf.position() = time_delimeter; ++buf.position(); memcpy(buf.position(), &digits100[datetime.minute() * 2], 2); buf.position() += 2; *buf.position() = time_delimeter; ++buf.position(); memcpy(buf.position(), &digits100[datetime.second() * 2], 2); buf.position() += 2; } else { buf.write(&digits100[datetime.year() / 100 * 2], 2); buf.write(&digits100[datetime.year() % 100 * 2], 2); buf.write(date_delimeter); buf.write(&digits100[datetime.month() * 2], 2); buf.write(date_delimeter); buf.write(&digits100[datetime.day() * 2], 2); buf.write(between_date_time_delimiter); buf.write(&digits100[datetime.hour() * 2], 2); buf.write(time_delimeter); buf.write(&digits100[datetime.minute() * 2], 2); buf.write(time_delimeter); buf.write(&digits100[datetime.second() * 2], 2); } } /// In the format YYYY-MM-DD HH:MM:SS, according to the specified time zone. template inline void writeDateTimeText(time_t datetime, WriteBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance()) { writeDateTimeText(LocalDateTime(datetime, time_zone), buf); } /// In the format YYYY-MM-DD HH:MM:SS.NNNNNNNNN, according to the specified time zone. template inline void writeDateTimeText(DateTime64 datetime64, UInt32 scale, WriteBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance()) { static constexpr UInt32 MaxScale = DecimalUtils::max_precision; scale = scale > MaxScale ? MaxScale : scale; auto components = DecimalUtils::split(datetime64, scale); /// Case1: /// -127914467.877 /// => whole = -127914467, fraction = 877(After DecimalUtils::split) /// => new whole = -127914468(1965-12-12 12:12:12), new fraction = 1000 - 877 = 123(.123) /// => 1965-12-12 12:12:12.123 /// /// Case2: /// -0.877 /// => whole = 0, fractional = -877(After DecimalUtils::split) /// => whole = -1(1969-12-31 23:59:59), fractional = 1000 + (-877) = 123(.123) using T = typename DateTime64::NativeType; if (datetime64.value < 0 && components.fractional) { components.fractional = DecimalUtils::scaleMultiplier(scale) + (components.whole ? T(-1) : T(1)) * components.fractional; --components.whole; } writeDateTimeText(LocalDateTime(components.whole, time_zone), buf); if (scale > 0) { buf.write(fractional_time_delimiter); writeDateTime64FractionalText(components.fractional, scale, buf); } } /// In the RFC 1123 format: "Tue, 03 Dec 2019 00:11:50 GMT". You must provide GMT DateLUT. /// This is needed for HTTP requests. inline void writeDateTimeTextRFC1123(time_t datetime, WriteBuffer & buf, const DateLUTImpl & time_zone = DateLUT::instance()) { const auto & values = time_zone.getValues(datetime); static const char week_days[3 * 8 + 1] = "XXX" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"; static const char months[3 * 13 + 1] = "XXX" "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"; buf.write(&week_days[values.day_of_week * 3], 3); buf.write(", ", 2); buf.write(&digits100[values.day_of_month * 2], 2); buf.write(' '); buf.write(&months[values.month * 3], 3); buf.write(' '); buf.write(&digits100[values.year / 100 * 2], 2); buf.write(&digits100[values.year % 100 * 2], 2); buf.write(' '); buf.write(&digits100[time_zone.toHour(datetime) * 2], 2); buf.write(':'); buf.write(&digits100[time_zone.toMinute(datetime) * 2], 2); buf.write(':'); buf.write(&digits100[time_zone.toSecond(datetime) * 2], 2); buf.write(" GMT", 4); } inline void writeDateTimeTextISO(time_t datetime, WriteBuffer & buf, const DateLUTImpl & utc_time_zone) { writeDateTimeText<'-', ':', 'T'>(datetime, buf, utc_time_zone); buf.write('Z'); } inline void writeDateTimeTextISO(DateTime64 datetime64, UInt32 scale, WriteBuffer & buf, const DateLUTImpl & utc_time_zone) { writeDateTimeText<'-', ':', 'T'>(datetime64, scale, buf, utc_time_zone); buf.write('Z'); } inline void writeDateTimeUnixTimestamp(DateTime64 datetime64, UInt32 scale, WriteBuffer & buf) { static constexpr UInt32 MaxScale = DecimalUtils::max_precision; scale = scale > MaxScale ? MaxScale : scale; auto components = DecimalUtils::split(datetime64, scale); writeIntText(components.whole, buf); if (scale > 0) //-V547 { buf.write('.'); writeDateTime64FractionalText(components.fractional, scale, buf); } } /// Methods for output in binary format. template requires is_arithmetic_v inline void writeBinary(const T & x, WriteBuffer & buf) { writePODBinary(x, buf); } inline void writeBinary(const String & x, WriteBuffer & buf) { writeStringBinary(x, buf); } inline void writeBinary(StringRef x, WriteBuffer & buf) { writeStringBinary(x, buf); } inline void writeBinary(std::string_view x, WriteBuffer & buf) { writeStringBinary(x, buf); } inline void writeBinary(const Decimal32 & x, WriteBuffer & buf) { writePODBinary(x, buf); } inline void writeBinary(const Decimal64 & x, WriteBuffer & buf) { writePODBinary(x, buf); } inline void writeBinary(const Decimal128 & x, WriteBuffer & buf) { writePODBinary(x, buf); } inline void writeBinary(const Decimal256 & x, WriteBuffer & buf) { writePODBinary(x.value, buf); } inline void writeBinary(const LocalDate & x, WriteBuffer & buf) { writePODBinary(x, buf); } inline void writeBinary(const LocalDateTime & x, WriteBuffer & buf) { writePODBinary(x, buf); } inline void writeBinary(const UUID & x, WriteBuffer & buf) { writePODBinary(x, buf); } /// Methods for outputting the value in text form for a tab-separated format. inline void writeText(is_integer auto x, WriteBuffer & buf) { if constexpr (std::is_same_v) writeBoolText(x, buf); else if constexpr (std::is_same_v) writeChar(x, buf); else writeIntText(x, buf); } inline void writeText(is_floating_point auto x, WriteBuffer & buf) { writeFloatText(x, buf); } inline void writeText(is_enum auto x, WriteBuffer & buf) { writeText(magic_enum::enum_name(x), buf); } inline void writeText(std::string_view x, WriteBuffer & buf) { writeString(x.data(), x.size(), buf); } inline void writeText(const DayNum & x, WriteBuffer & buf) { writeDateText(LocalDate(x), buf); } inline void writeText(const LocalDate & x, WriteBuffer & buf) { writeDateText(x, buf); } inline void writeText(const LocalDateTime & x, WriteBuffer & buf) { writeDateTimeText(x, buf); } inline void writeText(const UUID & x, WriteBuffer & buf) { writeUUIDText(x, buf); } template void writeDecimalFractional(const T & x, UInt32 scale, WriteBuffer & ostr, bool trailing_zeros) { /// If it's big integer, but the number of digits is small, /// use the implementation for smaller integers for more efficient arithmetic. if constexpr (std::is_same_v) { if (x <= std::numeric_limits::max()) { writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros); return; } else if (x <= std::numeric_limits::max()) { writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros); return; } else if (x <= std::numeric_limits::max()) { writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros); return; } } else if constexpr (std::is_same_v) { if (x <= std::numeric_limits::max()) { writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros); return; } else if (x <= std::numeric_limits::max()) { writeDecimalFractional(static_cast(x), scale, ostr, trailing_zeros); return; } } constexpr size_t max_digits = std::numeric_limits::digits10; assert(scale <= max_digits); char buf[max_digits]; memset(buf, '0', scale); T value = x; Int32 last_nonzero_pos = 0; for (Int32 pos = scale - 1; pos >= 0; --pos) { auto remainder = value % 10; value /= 10; if (remainder != 0 && last_nonzero_pos == 0) last_nonzero_pos = pos; buf[pos] += static_cast(remainder); } writeChar('.', ostr); ostr.write(buf, trailing_zeros ? scale : last_nonzero_pos + 1); } template void writeText(Decimal x, UInt32 scale, WriteBuffer & ostr, bool trailing_zeros) { T part = DecimalUtils::getWholePart(x, scale); if (x.value < 0 && part == 0) { writeChar('-', ostr); /// avoid crop leading minus when whole part is zero } writeIntText(part, ostr); if (scale) { part = DecimalUtils::getFractionalPart(x, scale); if (part || trailing_zeros) { if (part < 0) part *= T(-1); writeDecimalFractional(part, scale, ostr, trailing_zeros); } } } /// String, date, datetime are in single quotes with C-style escaping. Numbers - without. template requires is_arithmetic_v inline void writeQuoted(const T & x, WriteBuffer & buf) { writeText(x, buf); } inline void writeQuoted(const String & x, WriteBuffer & buf) { writeQuotedString(x, buf); } inline void writeQuoted(std::string_view x, WriteBuffer & buf) { writeQuotedString(x, buf); } inline void writeQuoted(StringRef x, WriteBuffer & buf) { writeQuotedString(x, buf); } inline void writeQuoted(const LocalDate & x, WriteBuffer & buf) { writeChar('\'', buf); writeDateText(x, buf); writeChar('\'', buf); } inline void writeQuoted(const LocalDateTime & x, WriteBuffer & buf) { writeChar('\'', buf); writeDateTimeText(x, buf); writeChar('\'', buf); } inline void writeQuoted(const UUID & x, WriteBuffer & buf) { writeChar('\'', buf); writeText(x, buf); writeChar('\'', buf); } /// String, date, datetime are in double quotes with C-style escaping. Numbers - without. template requires is_arithmetic_v inline void writeDoubleQuoted(const T & x, WriteBuffer & buf) { writeText(x, buf); } inline void writeDoubleQuoted(const String & x, WriteBuffer & buf) { writeDoubleQuotedString(x, buf); } inline void writeDoubleQuoted(std::string_view x, WriteBuffer & buf) { writeDoubleQuotedString(x, buf); } inline void writeDoubleQuoted(StringRef x, WriteBuffer & buf) { writeDoubleQuotedString(x, buf); } inline void writeDoubleQuoted(const LocalDate & x, WriteBuffer & buf) { writeChar('"', buf); writeDateText(x, buf); writeChar('"', buf); } inline void writeDoubleQuoted(const LocalDateTime & x, WriteBuffer & buf) { writeChar('"', buf); writeDateTimeText(x, buf); writeChar('"', buf); } inline void writeDoubleQuoted(const UUID & x, WriteBuffer & buf) { writeChar('"', buf); writeText(x, buf); writeChar('"', buf); } /// String - in double quotes and with CSV-escaping; date, datetime - in double quotes. Numbers - without. template requires is_arithmetic_v inline void writeCSV(const T & x, WriteBuffer & buf) { writeText(x, buf); } inline void writeCSV(const String & x, WriteBuffer & buf) { writeCSVString<>(x, buf); } inline void writeCSV(const LocalDate & x, WriteBuffer & buf) { writeDoubleQuoted(x, buf); } inline void writeCSV(const LocalDateTime & x, WriteBuffer & buf) { writeDoubleQuoted(x, buf); } inline void writeCSV(const UUID & x, WriteBuffer & buf) { writeDoubleQuoted(x, buf); } template void writeBinary(const std::vector & x, WriteBuffer & buf) { size_t size = x.size(); writeVarUInt(size, buf); for (size_t i = 0; i < size; ++i) writeBinary(x[i], buf); } template void writeQuoted(const std::vector & x, WriteBuffer & buf) { writeChar('[', buf); for (size_t i = 0, size = x.size(); i < size; ++i) { if (i != 0) writeChar(',', buf); writeQuoted(x[i], buf); } writeChar(']', buf); } template void writeDoubleQuoted(const std::vector & x, WriteBuffer & buf) { writeChar('[', buf); for (size_t i = 0, size = x.size(); i < size; ++i) { if (i != 0) writeChar(',', buf); writeDoubleQuoted(x[i], buf); } writeChar(']', buf); } template void writeText(const std::vector & x, WriteBuffer & buf) { writeQuoted(x, buf); } /// Serialize exception (so that it can be transferred over the network) void writeException(const Exception & e, WriteBuffer & buf, bool with_stack_trace); /// An easy-to-use method for converting something to a string in text form. template inline String toString(const T & x) { WriteBufferFromOwnString buf; writeText(x, buf); return buf.str(); } inline void writeNullTerminatedString(const String & s, WriteBuffer & buffer) { /// c_str is guaranteed to return zero-terminated string buffer.write(s.c_str(), s.size() + 1); } template requires is_arithmetic_v && (sizeof(T) <= 8) inline void writeBinaryBigEndian(T x, WriteBuffer & buf) /// Assuming little endian architecture. { if constexpr (std::endian::native == std::endian::little) { if constexpr (sizeof(x) == 2) x = __builtin_bswap16(x); else if constexpr (sizeof(x) == 4) x = __builtin_bswap32(x); else if constexpr (sizeof(x) == 8) x = __builtin_bswap64(x); } writePODBinary(x, buf); } template requires is_big_int_v inline void writeBinaryBigEndian(const T & x, WriteBuffer & buf) /// Assuming little endian architecture. { for (size_t i = 0; i != std::size(x.items); ++i) { const auto & item = x.items[std::size(x.items) - i - 1]; writeBinaryBigEndian(item, buf); } } struct PcgSerializer { static void serializePcg32(const pcg32_fast & rng, WriteBuffer & buf) { writeText(rng.multiplier(), buf); writeChar(' ', buf); writeText(rng.increment(), buf); writeChar(' ', buf); writeText(rng.state_, buf); } }; void writePointerHex(const void * ptr, WriteBuffer & buf); } template<> struct fmt::formatter { template constexpr auto parse(ParseContext & context) { return context.begin(); } template auto format(const DB::UUID & uuid, FormatContext & context) { return fmt::format_to(context.out(), "{}", toString(uuid)); } };