| 1 | #pragma once |
| 2 | |
| 3 | #include <cstring> |
| 4 | #include <cstdio> |
| 5 | #include <limits> |
| 6 | #include <algorithm> |
| 7 | #include <iterator> |
| 8 | |
| 9 | #include <common/DateLUT.h> |
| 10 | #include <common/LocalDate.h> |
| 11 | #include <common/LocalDateTime.h> |
| 12 | #include <common/find_symbols.h> |
| 13 | #include <common/StringRef.h> |
| 14 | |
| 15 | #include <Core/DecimalFunctions.h> |
| 16 | #include <Core/Types.h> |
| 17 | #include <Core/UUID.h> |
| 18 | |
| 19 | #include <Common/Exception.h> |
| 20 | #include <Common/StringUtils/StringUtils.h> |
| 21 | #include <Common/UInt128.h> |
| 22 | |
| 23 | #include <IO/CompressionMethod.h> |
| 24 | #include <IO/WriteBuffer.h> |
| 25 | #include <IO/WriteIntText.h> |
| 26 | #include <IO/VarInt.h> |
| 27 | #include <IO/DoubleConverter.h> |
| 28 | #include <IO/WriteBufferFromString.h> |
| 29 | #include <IO/ZlibDeflatingWriteBuffer.h> |
| 30 | |
| 31 | #include <Formats/FormatSettings.h> |
| 32 | |
| 33 | namespace DB |
| 34 | { |
| 35 | |
| 36 | namespace ErrorCodes |
| 37 | { |
| 38 | extern const int CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER; |
| 39 | extern const int ILLEGAL_TYPE_OF_ARGUMENT; |
| 40 | } |
| 41 | |
| 42 | /// Helper functions for formatted and binary output. |
| 43 | |
| 44 | inline void writeChar(char x, WriteBuffer & buf) |
| 45 | { |
| 46 | buf.nextIfAtEnd(); |
| 47 | *buf.position() = x; |
| 48 | ++buf.position(); |
| 49 | } |
| 50 | |
| 51 | /// Write the same character n times. |
| 52 | inline void writeChar(char c, size_t n, WriteBuffer & buf) |
| 53 | { |
| 54 | while (n) |
| 55 | { |
| 56 | buf.nextIfAtEnd(); |
| 57 | size_t count = std::min(n, buf.available()); |
| 58 | memset(buf.position(), c, count); |
| 59 | n -= count; |
| 60 | buf.position() += count; |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | /// Write POD-type in native format. It's recommended to use only with packed (dense) data types. |
| 65 | template <typename T> |
| 66 | inline void writePODBinary(const T & x, WriteBuffer & buf) |
| 67 | { |
| 68 | buf.write(reinterpret_cast<const char *>(&x), sizeof(x)); |
| 69 | } |
| 70 | |
| 71 | template <typename T> |
| 72 | inline void writeIntBinary(const T & x, WriteBuffer & buf) |
| 73 | { |
| 74 | writePODBinary(x, buf); |
| 75 | } |
| 76 | |
| 77 | template <typename T> |
| 78 | inline void writeFloatBinary(const T & x, WriteBuffer & buf) |
| 79 | { |
| 80 | writePODBinary(x, buf); |
| 81 | } |
| 82 | |
| 83 | |
| 84 | inline void writeStringBinary(const std::string & s, WriteBuffer & buf) |
| 85 | { |
| 86 | writeVarUInt(s.size(), buf); |
| 87 | buf.write(s.data(), s.size()); |
| 88 | } |
| 89 | |
| 90 | inline void writeStringBinary(const char * s, WriteBuffer & buf) |
| 91 | { |
| 92 | writeVarUInt(strlen(s), buf); |
| 93 | buf.write(s, strlen(s)); |
| 94 | } |
| 95 | |
| 96 | inline void writeStringBinary(const StringRef & s, WriteBuffer & buf) |
| 97 | { |
| 98 | writeVarUInt(s.size, buf); |
| 99 | buf.write(s.data, s.size); |
| 100 | } |
| 101 | |
| 102 | |
| 103 | template <typename T> |
| 104 | void writeVectorBinary(const std::vector<T> & v, WriteBuffer & buf) |
| 105 | { |
| 106 | writeVarUInt(v.size(), buf); |
| 107 | |
| 108 | for (typename std::vector<T>::const_iterator it = v.begin(); it != v.end(); ++it) |
| 109 | writeBinary(*it, buf); |
| 110 | } |
| 111 | |
| 112 | |
| 113 | inline void writeBoolText(bool x, WriteBuffer & buf) |
| 114 | { |
| 115 | writeChar(x ? '1' : '0', buf); |
| 116 | } |
| 117 | |
| 118 | template <typename T> |
| 119 | inline size_t writeFloatTextFastPath(T x, char * buffer, int len) |
| 120 | { |
| 121 | using Converter = DoubleConverter<false>; |
| 122 | double_conversion::StringBuilder builder{buffer, len}; |
| 123 | |
| 124 | bool result = false; |
| 125 | if constexpr (std::is_same_v<T, double>) |
| 126 | result = Converter::instance().ToShortest(x, &builder); |
| 127 | else |
| 128 | result = Converter::instance().ToShortestSingle(x, &builder); |
| 129 | |
| 130 | if (!result) |
| 131 | throw Exception("Cannot print floating point number" , ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER); |
| 132 | return builder.position(); |
| 133 | } |
| 134 | |
| 135 | template <typename T> |
| 136 | inline void writeFloatText(T x, WriteBuffer & buf) |
| 137 | { |
| 138 | static_assert(std::is_same_v<T, double> || std::is_same_v<T, float>, "Argument for writeFloatText must be float or double" ); |
| 139 | |
| 140 | using Converter = DoubleConverter<false>; |
| 141 | if (likely(buf.available() >= Converter::MAX_REPRESENTATION_LENGTH)) |
| 142 | { |
| 143 | buf.position() += writeFloatTextFastPath(x, buf.position(), Converter::MAX_REPRESENTATION_LENGTH); |
| 144 | return; |
| 145 | } |
| 146 | |
| 147 | Converter::BufferType buffer; |
| 148 | double_conversion::StringBuilder builder{buffer, sizeof(buffer)}; |
| 149 | |
| 150 | bool result = false; |
| 151 | if constexpr (std::is_same_v<T, double>) |
| 152 | result = Converter::instance().ToShortest(x, &builder); |
| 153 | else |
| 154 | result = Converter::instance().ToShortestSingle(x, &builder); |
| 155 | |
| 156 | if (!result) |
| 157 | throw Exception("Cannot print floating point number" , ErrorCodes::CANNOT_PRINT_FLOAT_OR_DOUBLE_NUMBER); |
| 158 | |
| 159 | buf.write(buffer, builder.position()); |
| 160 | } |
| 161 | |
| 162 | |
| 163 | inline void writeString(const String & s, WriteBuffer & buf) |
| 164 | { |
| 165 | buf.write(s.data(), s.size()); |
| 166 | } |
| 167 | |
| 168 | inline void writeString(const char * data, size_t size, WriteBuffer & buf) |
| 169 | { |
| 170 | buf.write(data, size); |
| 171 | } |
| 172 | |
| 173 | inline void writeString(const StringRef & ref, WriteBuffer & buf) |
| 174 | { |
| 175 | writeString(ref.data, ref.size, buf); |
| 176 | } |
| 177 | |
| 178 | |
| 179 | /** Writes a C-string without creating a temporary object. If the string is a literal, then `strlen` is executed at the compilation stage. |
| 180 | * Use when the string is a literal. |
| 181 | */ |
| 182 | #define writeCString(s, buf) \ |
| 183 | (buf).write((s), strlen(s)) |
| 184 | |
| 185 | /** Writes a string for use in the JSON format: |
| 186 | * - the string is written in double quotes |
| 187 | * - slash character '/' is escaped for compatibility with JavaScript |
| 188 | * - bytes from the range 0x00-0x1F except `\b', '\f', '\n', '\r', '\t' are escaped as \u00XX |
| 189 | * - code points U+2028 and U+2029 (byte sequences in UTF-8: e2 80 a8, e2 80 a9) are escaped as \u2028 and \u2029 |
| 190 | * - it is assumed that string is in UTF-8, the invalid UTF-8 is not processed |
| 191 | * - all other non-ASCII characters remain as is |
| 192 | */ |
| 193 | inline void writeJSONString(const char * begin, const char * end, WriteBuffer & buf, const FormatSettings & settings) |
| 194 | { |
| 195 | writeChar('"', buf); |
| 196 | for (const char * it = begin; it != end; ++it) |
| 197 | { |
| 198 | switch (*it) |
| 199 | { |
| 200 | case '\b': |
| 201 | writeChar('\\', buf); |
| 202 | writeChar('b', buf); |
| 203 | break; |
| 204 | case '\f': |
| 205 | writeChar('\\', buf); |
| 206 | writeChar('f', buf); |
| 207 | break; |
| 208 | case '\n': |
| 209 | writeChar('\\', buf); |
| 210 | writeChar('n', buf); |
| 211 | break; |
| 212 | case '\r': |
| 213 | writeChar('\\', buf); |
| 214 | writeChar('r', buf); |
| 215 | break; |
| 216 | case '\t': |
| 217 | writeChar('\\', buf); |
| 218 | writeChar('t', buf); |
| 219 | break; |
| 220 | case '\\': |
| 221 | writeChar('\\', buf); |
| 222 | writeChar('\\', buf); |
| 223 | break; |
| 224 | case '/': |
| 225 | if (settings.json.escape_forward_slashes) |
| 226 | writeChar('\\', buf); |
| 227 | writeChar('/', buf); |
| 228 | break; |
| 229 | case '"': |
| 230 | writeChar('\\', buf); |
| 231 | writeChar('"', buf); |
| 232 | break; |
| 233 | default: |
| 234 | UInt8 c = *it; |
| 235 | if (c <= 0x1F) |
| 236 | { |
| 237 | /// Escaping of ASCII control characters. |
| 238 | |
| 239 | UInt8 higher_half = c >> 4; |
| 240 | UInt8 lower_half = c & 0xF; |
| 241 | |
| 242 | writeCString("\\u00" , buf); |
| 243 | writeChar('0' + higher_half, buf); |
| 244 | |
| 245 | if (lower_half <= 9) |
| 246 | writeChar('0' + lower_half, buf); |
| 247 | else |
| 248 | writeChar('A' + lower_half - 10, buf); |
| 249 | } |
| 250 | else if (end - it >= 3 && it[0] == '\xE2' && it[1] == '\x80' && (it[2] == '\xA8' || it[2] == '\xA9')) |
| 251 | { |
| 252 | /// This is for compatibility with JavaScript, because unescaped line separators are prohibited in string literals, |
| 253 | /// and these code points are alternative line separators. |
| 254 | |
| 255 | if (it[2] == '\xA8') |
| 256 | writeCString("\\u2028" , buf); |
| 257 | if (it[2] == '\xA9') |
| 258 | writeCString("\\u2029" , buf); |
| 259 | |
| 260 | /// Byte sequence is 3 bytes long. We have additional two bytes to skip. |
| 261 | it += 2; |
| 262 | } |
| 263 | else |
| 264 | writeChar(*it, buf); |
| 265 | } |
| 266 | } |
| 267 | writeChar('"', buf); |
| 268 | } |
| 269 | |
| 270 | |
| 271 | /** Will escape quote_character and a list of special characters('\b', '\f', '\n', '\r', '\t', '\0', '\\'). |
| 272 | * - when escape_quote_with_quote is true, use backslash to escape list of special characters, |
| 273 | * and use quote_character to escape quote_character. such as: 'hello''world' |
| 274 | * - otherwise use backslash to escape list of special characters and quote_character |
| 275 | */ |
| 276 | template <char quote_character, bool escape_quote_with_quote = false> |
| 277 | void writeAnyEscapedString(const char * begin, const char * end, WriteBuffer & buf) |
| 278 | { |
| 279 | const char * pos = begin; |
| 280 | while (true) |
| 281 | { |
| 282 | /// On purpose we will escape more characters than minimally necessary. |
| 283 | const char * next_pos = find_first_symbols<'\b', '\f', '\n', '\r', '\t', '\0', '\\', quote_character>(pos, end); |
| 284 | |
| 285 | if (next_pos == end) |
| 286 | { |
| 287 | buf.write(pos, next_pos - pos); |
| 288 | break; |
| 289 | } |
| 290 | else |
| 291 | { |
| 292 | buf.write(pos, next_pos - pos); |
| 293 | pos = next_pos; |
| 294 | switch (*pos) |
| 295 | { |
| 296 | case '\b': |
| 297 | writeChar('\\', buf); |
| 298 | writeChar('b', buf); |
| 299 | break; |
| 300 | case '\f': |
| 301 | writeChar('\\', buf); |
| 302 | writeChar('f', buf); |
| 303 | break; |
| 304 | case '\n': |
| 305 | writeChar('\\', buf); |
| 306 | writeChar('n', buf); |
| 307 | break; |
| 308 | case '\r': |
| 309 | writeChar('\\', buf); |
| 310 | writeChar('r', buf); |
| 311 | break; |
| 312 | case '\t': |
| 313 | writeChar('\\', buf); |
| 314 | writeChar('t', buf); |
| 315 | break; |
| 316 | case '\0': |
| 317 | writeChar('\\', buf); |
| 318 | writeChar('0', buf); |
| 319 | break; |
| 320 | case '\\': |
| 321 | writeChar('\\', buf); |
| 322 | writeChar('\\', buf); |
| 323 | break; |
| 324 | case quote_character: |
| 325 | { |
| 326 | if constexpr (escape_quote_with_quote) |
| 327 | writeChar(quote_character, buf); |
| 328 | else |
| 329 | writeChar('\\', buf); |
| 330 | writeChar(quote_character, buf); |
| 331 | break; |
| 332 | } |
| 333 | default: |
| 334 | writeChar(*pos, buf); |
| 335 | } |
| 336 | ++pos; |
| 337 | } |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | |
| 342 | inline void writeJSONString(const String & s, WriteBuffer & buf, const FormatSettings & settings) |
| 343 | { |
| 344 | writeJSONString(s.data(), s.data() + s.size(), buf, settings); |
| 345 | } |
| 346 | |
| 347 | |
| 348 | inline void writeJSONString(const StringRef & ref, WriteBuffer & buf, const FormatSettings & settings) |
| 349 | { |
| 350 | writeJSONString(ref.data, ref.data + ref.size, buf, settings); |
| 351 | } |
| 352 | |
| 353 | |
| 354 | template <char c> |
| 355 | void writeAnyEscapedString(const String & s, WriteBuffer & buf) |
| 356 | { |
| 357 | writeAnyEscapedString<c>(s.data(), s.data() + s.size(), buf); |
| 358 | } |
| 359 | |
| 360 | |
| 361 | inline void writeEscapedString(const char * str, size_t size, WriteBuffer & buf) |
| 362 | { |
| 363 | writeAnyEscapedString<'\''>(str, str + size, buf); |
| 364 | } |
| 365 | |
| 366 | |
| 367 | inline void writeEscapedString(const String & s, WriteBuffer & buf) |
| 368 | { |
| 369 | writeEscapedString(s.data(), s.size(), buf); |
| 370 | } |
| 371 | |
| 372 | |
| 373 | inline void writeEscapedString(const StringRef & ref, WriteBuffer & buf) |
| 374 | { |
| 375 | writeEscapedString(ref.data, ref.size, buf); |
| 376 | } |
| 377 | |
| 378 | |
| 379 | template <char quote_character> |
| 380 | void writeAnyQuotedString(const char * begin, const char * end, WriteBuffer & buf) |
| 381 | { |
| 382 | writeChar(quote_character, buf); |
| 383 | writeAnyEscapedString<quote_character>(begin, end, buf); |
| 384 | writeChar(quote_character, buf); |
| 385 | } |
| 386 | |
| 387 | |
| 388 | |
| 389 | template <char quote_character> |
| 390 | void writeAnyQuotedString(const String & s, WriteBuffer & buf) |
| 391 | { |
| 392 | writeAnyQuotedString<quote_character>(s.data(), s.data() + s.size(), buf); |
| 393 | } |
| 394 | |
| 395 | |
| 396 | template <char quote_character> |
| 397 | void writeAnyQuotedString(const StringRef & ref, WriteBuffer & buf) |
| 398 | { |
| 399 | writeAnyQuotedString<quote_character>(ref.data, ref.data + ref.size, buf); |
| 400 | } |
| 401 | |
| 402 | |
| 403 | inline void writeQuotedString(const String & s, WriteBuffer & buf) |
| 404 | { |
| 405 | writeAnyQuotedString<'\''>(s, buf); |
| 406 | } |
| 407 | |
| 408 | |
| 409 | inline void writeQuotedString(const StringRef & ref, WriteBuffer & buf) |
| 410 | { |
| 411 | writeAnyQuotedString<'\''>(ref, buf); |
| 412 | } |
| 413 | |
| 414 | inline void writeDoubleQuotedString(const StringRef & s, WriteBuffer & buf) |
| 415 | { |
| 416 | writeAnyQuotedString<'"'>(s, buf); |
| 417 | } |
| 418 | |
| 419 | /// Outputs a string in backquotes. |
| 420 | inline void writeBackQuotedString(const StringRef & s, WriteBuffer & buf) |
| 421 | { |
| 422 | writeAnyQuotedString<'`'>(s, buf); |
| 423 | } |
| 424 | |
| 425 | /// Outputs a string in backquotes for MySQL. |
| 426 | inline void writeBackQuotedStringMySQL(const StringRef & s, WriteBuffer & buf) |
| 427 | { |
| 428 | writeChar('`', buf); |
| 429 | writeAnyEscapedString<'`', true>(s.data, s.data + s.size, buf); |
| 430 | writeChar('`', buf); |
| 431 | } |
| 432 | |
| 433 | |
| 434 | /// The same, but quotes apply only if there are characters that do not match the identifier without quotes. |
| 435 | template <typename F> |
| 436 | inline void writeProbablyQuotedStringImpl(const StringRef & s, WriteBuffer & buf, F && write_quoted_string) |
| 437 | { |
| 438 | if (!s.size || !isValidIdentifierBegin(s.data[0])) |
| 439 | write_quoted_string(s, buf); |
| 440 | else |
| 441 | { |
| 442 | const char * pos = s.data + 1; |
| 443 | const char * end = s.data + s.size; |
| 444 | for (; pos < end; ++pos) |
| 445 | if (!isWordCharASCII(*pos)) |
| 446 | break; |
| 447 | if (pos != end) |
| 448 | write_quoted_string(s, buf); |
| 449 | else |
| 450 | writeString(s, buf); |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | inline void writeProbablyBackQuotedString(const StringRef & s, WriteBuffer & buf) |
| 455 | { |
| 456 | writeProbablyQuotedStringImpl(s, buf, [](const StringRef & s_, WriteBuffer & buf_) { return writeBackQuotedString(s_, buf_); }); |
| 457 | } |
| 458 | |
| 459 | inline void writeProbablyDoubleQuotedString(const StringRef & s, WriteBuffer & buf) |
| 460 | { |
| 461 | writeProbablyQuotedStringImpl(s, buf, [](const StringRef & s_, WriteBuffer & buf_) { return writeDoubleQuotedString(s_, buf_); }); |
| 462 | } |
| 463 | |
| 464 | inline void writeProbablyBackQuotedStringMySQL(const StringRef & s, WriteBuffer & buf) |
| 465 | { |
| 466 | writeProbablyQuotedStringImpl(s, buf, [](const StringRef & s_, WriteBuffer & buf_) { return writeBackQuotedStringMySQL(s_, buf_); }); |
| 467 | } |
| 468 | |
| 469 | |
| 470 | /** Outputs the string in for the CSV format. |
| 471 | * Rules: |
| 472 | * - the string is outputted in quotation marks; |
| 473 | * - the quotation mark inside the string is outputted as two quotation marks in sequence. |
| 474 | */ |
| 475 | template <char quote = '"'> |
| 476 | void writeCSVString(const char * begin, const char * end, WriteBuffer & buf) |
| 477 | { |
| 478 | writeChar(quote, buf); |
| 479 | |
| 480 | const char * pos = begin; |
| 481 | while (true) |
| 482 | { |
| 483 | const char * next_pos = find_first_symbols<quote>(pos, end); |
| 484 | |
| 485 | if (next_pos == end) |
| 486 | { |
| 487 | buf.write(pos, end - pos); |
| 488 | break; |
| 489 | } |
| 490 | else /// Quotation. |
| 491 | { |
| 492 | ++next_pos; |
| 493 | buf.write(pos, next_pos - pos); |
| 494 | writeChar(quote, buf); |
| 495 | } |
| 496 | |
| 497 | pos = next_pos; |
| 498 | } |
| 499 | |
| 500 | writeChar(quote, buf); |
| 501 | } |
| 502 | |
| 503 | template <char quote = '"'> |
| 504 | void writeCSVString(const String & s, WriteBuffer & buf) |
| 505 | { |
| 506 | writeCSVString<quote>(s.data(), s.data() + s.size(), buf); |
| 507 | } |
| 508 | |
| 509 | template <char quote = '"'> |
| 510 | void writeCSVString(const StringRef & s, WriteBuffer & buf) |
| 511 | { |
| 512 | writeCSVString<quote>(s.data, s.data + s.size, buf); |
| 513 | } |
| 514 | |
| 515 | |
| 516 | /// Writing a string to a text node in XML (not into an attribute - otherwise you need more escaping). |
| 517 | inline void writeXMLString(const char * begin, const char * end, WriteBuffer & buf) |
| 518 | { |
| 519 | const char * pos = begin; |
| 520 | while (true) |
| 521 | { |
| 522 | /// NOTE Perhaps for some XML parsers, you need to escape the zero byte and some control characters. |
| 523 | const char * next_pos = find_first_symbols<'<', '&'>(pos, end); |
| 524 | |
| 525 | if (next_pos == end) |
| 526 | { |
| 527 | buf.write(pos, end - pos); |
| 528 | break; |
| 529 | } |
| 530 | else if (*next_pos == '<') |
| 531 | { |
| 532 | buf.write(pos, next_pos - pos); |
| 533 | ++next_pos; |
| 534 | writeCString("<" , buf); |
| 535 | } |
| 536 | else if (*next_pos == '&') |
| 537 | { |
| 538 | buf.write(pos, next_pos - pos); |
| 539 | ++next_pos; |
| 540 | writeCString("&" , buf); |
| 541 | } |
| 542 | |
| 543 | pos = next_pos; |
| 544 | } |
| 545 | } |
| 546 | |
| 547 | inline void writeXMLString(const String & s, WriteBuffer & buf) |
| 548 | { |
| 549 | writeXMLString(s.data(), s.data() + s.size(), buf); |
| 550 | } |
| 551 | |
| 552 | inline void writeXMLString(const StringRef & s, WriteBuffer & buf) |
| 553 | { |
| 554 | writeXMLString(s.data, s.data + s.size, buf); |
| 555 | } |
| 556 | |
| 557 | template <typename IteratorSrc, typename IteratorDst> |
| 558 | void formatHex(IteratorSrc src, IteratorDst dst, const size_t num_bytes); |
| 559 | void formatUUID(const UInt8 * src16, UInt8 * dst36); |
| 560 | void formatUUID(std::reverse_iterator<const UInt8 *> dst16, UInt8 * dst36); |
| 561 | |
| 562 | inline void writeUUIDText(const UUID & uuid, WriteBuffer & buf) |
| 563 | { |
| 564 | char s[36]; |
| 565 | |
| 566 | formatUUID(std::reverse_iterator<const UInt8 *>(reinterpret_cast<const UInt8 *>(&uuid) + 16), reinterpret_cast<UInt8 *>(s)); |
| 567 | buf.write(s, sizeof(s)); |
| 568 | } |
| 569 | |
| 570 | |
| 571 | static const char digits100[201] = |
| 572 | "00010203040506070809" |
| 573 | "10111213141516171819" |
| 574 | "20212223242526272829" |
| 575 | "30313233343536373839" |
| 576 | "40414243444546474849" |
| 577 | "50515253545556575859" |
| 578 | "60616263646566676869" |
| 579 | "70717273747576777879" |
| 580 | "80818283848586878889" |
| 581 | "90919293949596979899" ; |
| 582 | |
| 583 | /// in YYYY-MM-DD format |
| 584 | template <char delimiter = '-'> |
| 585 | inline void writeDateText(const LocalDate & date, WriteBuffer & buf) |
| 586 | { |
| 587 | if (buf.position() + 10 <= buf.buffer().end()) |
| 588 | { |
| 589 | memcpy(buf.position(), &digits100[date.year() / 100 * 2], 2); |
| 590 | buf.position() += 2; |
| 591 | memcpy(buf.position(), &digits100[date.year() % 100 * 2], 2); |
| 592 | buf.position() += 2; |
| 593 | *buf.position() = delimiter; |
| 594 | ++buf.position(); |
| 595 | memcpy(buf.position(), &digits100[date.month() * 2], 2); |
| 596 | buf.position() += 2; |
| 597 | *buf.position() = delimiter; |
| 598 | ++buf.position(); |
| 599 | memcpy(buf.position(), &digits100[date.day() * 2], 2); |
| 600 | buf.position() += 2; |
| 601 | } |
| 602 | else |
| 603 | { |
| 604 | buf.write(&digits100[date.year() / 100 * 2], 2); |
| 605 | buf.write(&digits100[date.year() % 100 * 2], 2); |
| 606 | buf.write(delimiter); |
| 607 | buf.write(&digits100[date.month() * 2], 2); |
| 608 | buf.write(delimiter); |
| 609 | buf.write(&digits100[date.day() * 2], 2); |
| 610 | } |
| 611 | } |
| 612 | |
| 613 | template <char delimiter = '-'> |
| 614 | inline void writeDateText(DayNum date, WriteBuffer & buf) |
| 615 | { |
| 616 | if (unlikely(!date)) |
| 617 | { |
| 618 | static const char s[] = {'0', '0', '0', '0', delimiter, '0', '0', delimiter, '0', '0'}; |
| 619 | buf.write(s, sizeof(s)); |
| 620 | return; |
| 621 | } |
| 622 | |
| 623 | writeDateText<delimiter>(LocalDate(date), buf); |
| 624 | } |
| 625 | |
| 626 | |
| 627 | /// In the format YYYY-MM-DD HH:MM:SS |
| 628 | template <char date_delimeter = '-', char time_delimeter = ':', char between_date_time_delimiter = ' '> |
| 629 | inline void writeDateTimeText(const LocalDateTime & datetime, WriteBuffer & buf) |
| 630 | { |
| 631 | if (buf.position() + 19 <= buf.buffer().end()) |
| 632 | { |
| 633 | memcpy(buf.position(), &digits100[datetime.year() / 100 * 2], 2); |
| 634 | buf.position() += 2; |
| 635 | memcpy(buf.position(), &digits100[datetime.year() % 100 * 2], 2); |
| 636 | buf.position() += 2; |
| 637 | *buf.position() = date_delimeter; |
| 638 | ++buf.position(); |
| 639 | memcpy(buf.position(), &digits100[datetime.month() * 2], 2); |
| 640 | buf.position() += 2; |
| 641 | *buf.position() = date_delimeter; |
| 642 | ++buf.position(); |
| 643 | memcpy(buf.position(), &digits100[datetime.day() * 2], 2); |
| 644 | buf.position() += 2; |
| 645 | *buf.position() = between_date_time_delimiter; |
| 646 | ++buf.position(); |
| 647 | memcpy(buf.position(), &digits100[datetime.hour() * 2], 2); |
| 648 | buf.position() += 2; |
| 649 | *buf.position() = time_delimeter; |
| 650 | ++buf.position(); |
| 651 | memcpy(buf.position(), &digits100[datetime.minute() * 2], 2); |
| 652 | buf.position() += 2; |
| 653 | *buf.position() = time_delimeter; |
| 654 | ++buf.position(); |
| 655 | memcpy(buf.position(), &digits100[datetime.second() * 2], 2); |
| 656 | buf.position() += 2; |
| 657 | } |
| 658 | else |
| 659 | { |
| 660 | buf.write(&digits100[datetime.year() / 100 * 2], 2); |
| 661 | buf.write(&digits100[datetime.year() % 100 * 2], 2); |
| 662 | buf.write(date_delimeter); |
| 663 | buf.write(&digits100[datetime.month() * 2], 2); |
| 664 | buf.write(date_delimeter); |
| 665 | buf.write(&digits100[datetime.day() * 2], 2); |
| 666 | buf.write(between_date_time_delimiter); |
| 667 | buf.write(&digits100[datetime.hour() * 2], 2); |
| 668 | buf.write(time_delimeter); |
| 669 | buf.write(&digits100[datetime.minute() * 2], 2); |
| 670 | buf.write(time_delimeter); |
| 671 | buf.write(&digits100[datetime.second() * 2], 2); |
| 672 | } |
| 673 | } |
| 674 | |
| 675 | /// In the format YYYY-MM-DD HH:MM:SS, according to the specified time zone. |
| 676 | template <char date_delimeter = '-', char time_delimeter = ':', char between_date_time_delimiter = ' '> |
| 677 | inline void writeDateTimeText(time_t datetime, WriteBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance()) |
| 678 | { |
| 679 | if (unlikely(!datetime)) |
| 680 | { |
| 681 | static const char s[] = |
| 682 | { |
| 683 | '0', '0', '0', '0', date_delimeter, '0', '0', date_delimeter, '0', '0', |
| 684 | between_date_time_delimiter, |
| 685 | '0', '0', time_delimeter, '0', '0', time_delimeter, '0', '0' |
| 686 | }; |
| 687 | buf.write(s, sizeof(s)); |
| 688 | return; |
| 689 | } |
| 690 | |
| 691 | const auto & values = date_lut.getValues(datetime); |
| 692 | writeDateTimeText<date_delimeter, time_delimeter, between_date_time_delimiter>( |
| 693 | LocalDateTime(values.year, values.month, values.day_of_month, |
| 694 | date_lut.toHour(datetime), date_lut.toMinute(datetime), date_lut.toSecond(datetime)), buf); |
| 695 | } |
| 696 | |
| 697 | /// In the format YYYY-MM-DD HH:MM:SS.NNNNNNNNN, according to the specified time zone. |
| 698 | template <char date_delimeter = '-', char time_delimeter = ':', char between_date_time_delimiter = ' ', char fractional_time_delimiter = '.'> |
| 699 | inline void writeDateTimeText(DateTime64 datetime64, UInt32 scale, WriteBuffer & buf, const DateLUTImpl & date_lut = DateLUT::instance()) |
| 700 | { |
| 701 | static constexpr UInt32 MaxScale = DecimalUtils::maxPrecision<DateTime64>(); |
| 702 | scale = scale > MaxScale ? MaxScale : scale; |
| 703 | if (unlikely(!datetime64)) |
| 704 | { |
| 705 | static const char s[] = |
| 706 | { |
| 707 | '0', '0', '0', '0', date_delimeter, '0', '0', date_delimeter, '0', '0', |
| 708 | between_date_time_delimiter, |
| 709 | '0', '0', time_delimeter, '0', '0', time_delimeter, '0', '0', |
| 710 | fractional_time_delimiter, |
| 711 | // Exactly MaxScale zeroes |
| 712 | '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0' |
| 713 | }; |
| 714 | buf.write(s, sizeof(s) - (MaxScale - scale)); |
| 715 | return; |
| 716 | } |
| 717 | auto c = DecimalUtils::split(datetime64, scale); |
| 718 | const auto & values = date_lut.getValues(c.whole); |
| 719 | writeDateTimeText<date_delimeter, time_delimeter, between_date_time_delimiter>( |
| 720 | LocalDateTime(values.year, values.month, values.day_of_month, |
| 721 | date_lut.toHour(c.whole), date_lut.toMinute(c.whole), date_lut.toSecond(c.whole)), buf); |
| 722 | |
| 723 | if (scale > 0) |
| 724 | { |
| 725 | buf.write(fractional_time_delimiter); |
| 726 | |
| 727 | char data[20] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; |
| 728 | static_assert(sizeof(data) >= MaxScale); |
| 729 | |
| 730 | auto fractional = c.fractional; |
| 731 | for (Int32 pos = scale - 1; pos >= 0 && fractional; --pos, fractional /= DateTime64(10)) |
| 732 | data[pos] += fractional % DateTime64(10); |
| 733 | |
| 734 | writeString(&data[0], static_cast<size_t>(scale), buf); |
| 735 | } |
| 736 | } |
| 737 | |
| 738 | /// In the RFC 1123 format: "Tue, 03 Dec 2019 00:11:50 GMT". You must provide GMT DateLUT. |
| 739 | /// This is needed for HTTP requests. |
| 740 | inline void writeDateTimeTextRFC1123(time_t datetime, WriteBuffer & buf, const DateLUTImpl & date_lut) |
| 741 | { |
| 742 | const auto & values = date_lut.getValues(datetime); |
| 743 | |
| 744 | static const char week_days[3 * 8 + 1] = "XXX" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun" ; |
| 745 | static const char months[3 * 13 + 1] = "XXX" "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec" ; |
| 746 | |
| 747 | buf.write(&week_days[values.day_of_week * 3], 3); |
| 748 | buf.write(", " , 2); |
| 749 | buf.write(&digits100[values.day_of_month * 2], 2); |
| 750 | buf.write(' '); |
| 751 | buf.write(&months[values.month * 3], 3); |
| 752 | buf.write(' '); |
| 753 | buf.write(&digits100[values.year / 100 * 2], 2); |
| 754 | buf.write(&digits100[values.year % 100 * 2], 2); |
| 755 | buf.write(' '); |
| 756 | buf.write(&digits100[date_lut.toHour(datetime) * 2], 2); |
| 757 | buf.write(':'); |
| 758 | buf.write(&digits100[date_lut.toMinute(datetime) * 2], 2); |
| 759 | buf.write(':'); |
| 760 | buf.write(&digits100[date_lut.toSecond(datetime) * 2], 2); |
| 761 | buf.write(" GMT" , 4); |
| 762 | } |
| 763 | |
| 764 | |
| 765 | /// Methods for output in binary format. |
| 766 | template <typename T> |
| 767 | inline std::enable_if_t<is_arithmetic_v<T>, void> |
| 768 | writeBinary(const T & x, WriteBuffer & buf) { writePODBinary(x, buf); } |
| 769 | |
| 770 | inline void writeBinary(const String & x, WriteBuffer & buf) { writeStringBinary(x, buf); } |
| 771 | inline void writeBinary(const StringRef & x, WriteBuffer & buf) { writeStringBinary(x, buf); } |
| 772 | inline void writeBinary(const Int128 & x, WriteBuffer & buf) { writePODBinary(x, buf); } |
| 773 | inline void writeBinary(const UInt128 & x, WriteBuffer & buf) { writePODBinary(x, buf); } |
| 774 | inline void writeBinary(const UInt256 & x, WriteBuffer & buf) { writePODBinary(x, buf); } |
| 775 | inline void writeBinary(const Decimal32 & x, WriteBuffer & buf) { writePODBinary(x, buf); } |
| 776 | inline void writeBinary(const Decimal64 & x, WriteBuffer & buf) { writePODBinary(x, buf); } |
| 777 | inline void writeBinary(const Decimal128 & x, WriteBuffer & buf) { writePODBinary(x, buf); } |
| 778 | inline void writeBinary(const LocalDate & x, WriteBuffer & buf) { writePODBinary(x, buf); } |
| 779 | inline void writeBinary(const LocalDateTime & x, WriteBuffer & buf) { writePODBinary(x, buf); } |
| 780 | |
| 781 | |
| 782 | /// Methods for outputting the value in text form for a tab-separated format. |
| 783 | template <typename T> |
| 784 | inline std::enable_if_t<is_integral_v<T>, void> |
| 785 | writeText(const T & x, WriteBuffer & buf) { writeIntText(x, buf); } |
| 786 | |
| 787 | template <typename T> |
| 788 | inline std::enable_if_t<std::is_floating_point_v<T>, void> |
| 789 | writeText(const T & x, WriteBuffer & buf) { writeFloatText(x, buf); } |
| 790 | |
| 791 | inline void writeText(const String & x, WriteBuffer & buf) { writeEscapedString(x, buf); } |
| 792 | |
| 793 | /// Implemented as template specialization (not function overload) to avoid preference over templates on arithmetic types above. |
| 794 | template <> inline void writeText<bool>(const bool & x, WriteBuffer & buf) { writeBoolText(x, buf); } |
| 795 | |
| 796 | /// unlike the method for std::string |
| 797 | /// assumes here that `x` is a null-terminated string. |
| 798 | inline void writeText(const char * x, WriteBuffer & buf) { writeEscapedString(x, strlen(x), buf); } |
| 799 | inline void writeText(const char * x, size_t size, WriteBuffer & buf) { writeEscapedString(x, size, buf); } |
| 800 | |
| 801 | inline void writeText(const LocalDate & x, WriteBuffer & buf) { writeDateText(x, buf); } |
| 802 | inline void writeText(const LocalDateTime & x, WriteBuffer & buf) { writeDateTimeText(x, buf); } |
| 803 | inline void writeText(const UUID & x, WriteBuffer & buf) { writeUUIDText(x, buf); } |
| 804 | inline void writeText(const UInt128 & x, WriteBuffer & buf) { writeText(UUID(x), buf); } |
| 805 | |
| 806 | template <typename T> |
| 807 | void writeText(Decimal<T> value, UInt32 scale, WriteBuffer & ostr) |
| 808 | { |
| 809 | if (value < Decimal<T>(0)) |
| 810 | { |
| 811 | value *= Decimal<T>(-1); |
| 812 | writeChar('-', ostr); /// avoid crop leading minus when whole part is zero |
| 813 | } |
| 814 | |
| 815 | const T whole_part = DecimalUtils::getWholePart(value, scale); |
| 816 | |
| 817 | writeIntText(whole_part, ostr); |
| 818 | if (scale) |
| 819 | { |
| 820 | writeChar('.', ostr); |
| 821 | String str_fractional(scale, '0'); |
| 822 | for (Int32 pos = scale - 1; pos >= 0; --pos, value /= Decimal<T>(10)) |
| 823 | str_fractional[pos] += value % Decimal<T>(10); |
| 824 | ostr.write(str_fractional.data(), scale); |
| 825 | } |
| 826 | } |
| 827 | |
| 828 | /// String, date, datetime are in single quotes with C-style escaping. Numbers - without. |
| 829 | template <typename T> |
| 830 | inline std::enable_if_t<is_arithmetic_v<T>, void> |
| 831 | writeQuoted(const T & x, WriteBuffer & buf) { writeText(x, buf); } |
| 832 | |
| 833 | inline void writeQuoted(const String & x, WriteBuffer & buf) { writeQuotedString(x, buf); } |
| 834 | |
| 835 | inline void writeQuoted(const LocalDate & x, WriteBuffer & buf) |
| 836 | { |
| 837 | writeChar('\'', buf); |
| 838 | writeDateText(x, buf); |
| 839 | writeChar('\'', buf); |
| 840 | } |
| 841 | |
| 842 | inline void writeQuoted(const LocalDateTime & x, WriteBuffer & buf) |
| 843 | { |
| 844 | writeChar('\'', buf); |
| 845 | writeDateTimeText(x, buf); |
| 846 | writeChar('\'', buf); |
| 847 | } |
| 848 | |
| 849 | inline void writeQuoted(const UUID & x, WriteBuffer & buf) |
| 850 | { |
| 851 | writeChar('\'', buf); |
| 852 | writeText(x, buf); |
| 853 | writeChar('\'', buf); |
| 854 | } |
| 855 | |
| 856 | /// String, date, datetime are in double quotes with C-style escaping. Numbers - without. |
| 857 | template <typename T> |
| 858 | inline std::enable_if_t<is_arithmetic_v<T>, void> |
| 859 | writeDoubleQuoted(const T & x, WriteBuffer & buf) { writeText(x, buf); } |
| 860 | |
| 861 | inline void writeDoubleQuoted(const String & x, WriteBuffer & buf) { writeDoubleQuotedString(x, buf); } |
| 862 | |
| 863 | inline void writeDoubleQuoted(const LocalDate & x, WriteBuffer & buf) |
| 864 | { |
| 865 | writeChar('"', buf); |
| 866 | writeDateText(x, buf); |
| 867 | writeChar('"', buf); |
| 868 | } |
| 869 | |
| 870 | inline void writeDoubleQuoted(const LocalDateTime & x, WriteBuffer & buf) |
| 871 | { |
| 872 | writeChar('"', buf); |
| 873 | writeDateTimeText(x, buf); |
| 874 | writeChar('"', buf); |
| 875 | } |
| 876 | |
| 877 | inline void writeDoubleQuoted(const UUID & x, WriteBuffer & buf) |
| 878 | { |
| 879 | writeChar('"', buf); |
| 880 | writeText(x, buf); |
| 881 | writeChar('"', buf); |
| 882 | } |
| 883 | |
| 884 | |
| 885 | /// String - in double quotes and with CSV-escaping; date, datetime - in double quotes. Numbers - without. |
| 886 | template <typename T> |
| 887 | inline std::enable_if_t<is_arithmetic_v<T>, void> |
| 888 | writeCSV(const T & x, WriteBuffer & buf) { writeText(x, buf); } |
| 889 | |
| 890 | inline void writeCSV(const String & x, WriteBuffer & buf) { writeCSVString<>(x, buf); } |
| 891 | inline void writeCSV(const LocalDate & x, WriteBuffer & buf) { writeDoubleQuoted(x, buf); } |
| 892 | inline void writeCSV(const LocalDateTime & x, WriteBuffer & buf) { writeDoubleQuoted(x, buf); } |
| 893 | inline void writeCSV(const UUID & x, WriteBuffer & buf) { writeDoubleQuoted(x, buf); } |
| 894 | [[noreturn]] inline void writeCSV(const UInt128, WriteBuffer &) |
| 895 | { |
| 896 | /** Because UInt128 isn't a natural type, without arithmetic operator and only use as an intermediary type -for UUID- |
| 897 | * it should never arrive here. But because we used the DataTypeNumber class we should have at least a definition of it. |
| 898 | */ |
| 899 | throw Exception("UInt128 cannot be write as a text" , ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
| 900 | } |
| 901 | |
| 902 | template <typename T> |
| 903 | void writeBinary(const std::vector<T> & x, WriteBuffer & buf) |
| 904 | { |
| 905 | size_t size = x.size(); |
| 906 | writeVarUInt(size, buf); |
| 907 | for (size_t i = 0; i < size; ++i) |
| 908 | writeBinary(x[i], buf); |
| 909 | } |
| 910 | |
| 911 | template <typename T> |
| 912 | void writeQuoted(const std::vector<T> & x, WriteBuffer & buf) |
| 913 | { |
| 914 | writeChar('[', buf); |
| 915 | for (size_t i = 0, size = x.size(); i < size; ++i) |
| 916 | { |
| 917 | if (i != 0) |
| 918 | writeChar(',', buf); |
| 919 | writeQuoted(x[i], buf); |
| 920 | } |
| 921 | writeChar(']', buf); |
| 922 | } |
| 923 | |
| 924 | template <typename T> |
| 925 | void writeDoubleQuoted(const std::vector<T> & x, WriteBuffer & buf) |
| 926 | { |
| 927 | writeChar('[', buf); |
| 928 | for (size_t i = 0, size = x.size(); i < size; ++i) |
| 929 | { |
| 930 | if (i != 0) |
| 931 | writeChar(',', buf); |
| 932 | writeDoubleQuoted(x[i], buf); |
| 933 | } |
| 934 | writeChar(']', buf); |
| 935 | } |
| 936 | |
| 937 | template <typename T> |
| 938 | void writeText(const std::vector<T> & x, WriteBuffer & buf) |
| 939 | { |
| 940 | writeQuoted(x, buf); |
| 941 | } |
| 942 | |
| 943 | |
| 944 | |
| 945 | /// Serialize exception (so that it can be transferred over the network) |
| 946 | void writeException(const Exception & e, WriteBuffer & buf, bool with_stack_trace); |
| 947 | |
| 948 | |
| 949 | /// An easy-to-use method for converting something to a string in text form. |
| 950 | template <typename T> |
| 951 | inline String toString(const T & x) |
| 952 | { |
| 953 | WriteBufferFromOwnString buf; |
| 954 | writeText(x, buf); |
| 955 | return buf.str(); |
| 956 | } |
| 957 | |
| 958 | template <class TWriteBuffer, class... Types> |
| 959 | std::unique_ptr<WriteBuffer> getWriteBuffer(const DB::CompressionMethod method, Types&&... args) |
| 960 | { |
| 961 | if (method == DB::CompressionMethod::Gzip) |
| 962 | { |
| 963 | auto write_buf = std::make_unique<TWriteBuffer>(std::forward<Types>(args)...); |
| 964 | return std::make_unique<ZlibDeflatingWriteBuffer>(std::move(write_buf), method, 1 /* compression level */); |
| 965 | } |
| 966 | return std::make_unique<TWriteBuffer>(args...); |
| 967 | } |
| 968 | |
| 969 | } |
| 970 | |