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 | |