libpqxx
The C++ client library for PostgreSQL
Loading...
Searching...
No Matches
conversions.hxx
1#include <array>
2#include <cstring>
3#include <map>
4#include <memory>
5#include <numeric>
6#include <optional>
7
8#if defined(PQXX_HAVE_SPAN) && __has_include(<span>)
9# include <span>
10#endif
11
12#include <type_traits>
13#include <variant>
14#include <vector>
15
16#include "pqxx/types.hxx"
17#include "pqxx/util.hxx"
18
19
20/* Internal helpers for string conversion, and conversion implementations.
21 *
22 * Do not include this header directly. The libpqxx headers do it for you.
23 */
24namespace pqxx::internal
25{
27inline constexpr char number_to_digit(int i) noexcept
28{
29 return static_cast<char>(i + '0');
30}
31
32
34constexpr int digit_to_number(char c) noexcept
35{
36 return c - '0';
37}
38
39
41
44std::string PQXX_LIBEXPORT
46
47
48template<typename HAVE, typename NEED>
50{
52 static_cast<int>(have_bytes), static_cast<int>(need_bytes));
53}
54
55
57[[noreturn]] PQXX_LIBEXPORT PQXX_COLD void
58throw_null_conversion(std::string const &type);
59
60
62[[noreturn]] PQXX_LIBEXPORT PQXX_COLD void
63throw_null_conversion(std::string_view type);
64
65
67
76template<typename CHAR_TYPE> struct disallowed_ambiguous_char_conversion
77{
78 static constexpr bool converts_to_string{false};
79 static constexpr bool converts_from_string{false};
80 static char *into_buf(char *, char *, CHAR_TYPE) = delete;
81 static constexpr zview
82 to_buf(char *, char *, CHAR_TYPE const &) noexcept = delete;
83
84 static constexpr std::size_t
85 size_buffer(CHAR_TYPE const &) noexcept = delete;
86 static CHAR_TYPE from_string(std::string_view) = delete;
87};
88
89
90template<typename T> PQXX_LIBEXPORT extern std::string to_string_float(T);
91
92
94template<typename T>
95inline char *generic_into_buf(char *begin, char *end, T const &value)
96{
97 zview const text{string_traits<T>::to_buf(begin, end, value)};
98 auto const space{end - begin};
99 // Include the trailing zero.
100 auto const len = std::size(text) + 1;
102 throw conversion_overrun{
103 "Not enough buffer space to insert " + type_name<T> + ". " +
105 std::memmove(begin, text.data(), len);
106 return begin + len;
107}
108
109
110// C++20: Guard with concept?
112template<typename T> struct integral_traits
113{
114 static constexpr bool converts_to_string{true};
115 static constexpr bool converts_from_string{true};
116 static PQXX_LIBEXPORT T from_string(std::string_view text);
117 static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value);
118 static PQXX_LIBEXPORT char *into_buf(char *begin, char *end, T const &value);
119
120 static constexpr std::size_t size_buffer(T const &) noexcept
121 {
126 return std::is_signed_v<T> + std::numeric_limits<T>::digits10 + 1 + 1;
127 }
128};
129
130
131// C++20: Guard with concept?
133template<typename T> struct float_traits
134{
135 static constexpr bool converts_to_string{true};
136 static constexpr bool converts_from_string{true};
137 static PQXX_LIBEXPORT T from_string(std::string_view text);
138 static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value);
139 static PQXX_LIBEXPORT char *into_buf(char *begin, char *end, T const &value);
140
141 // Return a nonnegative integral value's number of decimal digits.
142 static constexpr std::size_t digits10(std::size_t value) noexcept
143 {
144 if (value < 10)
145 return 1;
146 else
147 return 1 + digits10(value / 10);
148 }
149
150 static constexpr std::size_t size_buffer(T const &) noexcept
151 {
152 using lims = std::numeric_limits<T>;
153 // See #328 for a detailed discussion on the maximum number of digits.
154 //
155 // In a nutshell: for the big cases, the scientific notation is always
156 // the shortest one, and therefore the one that to_chars will pick.
157 //
158 // So... How long can the scientific notation get? 1 (for sign) + 1 (for
159 // decimal point) + 1 (for 'e') + 1 (for exponent sign) + max_digits10 +
160 // max number of digits in the exponent + 1 (terminating zero).
161 //
162 // What's the max number of digits in the exponent? It's the max number of
163 // digits out of the most negative exponent and the most positive one.
164 //
165 // The longest positive exponent is easy: 1 + ceil(log10(max_exponent10)).
166 // (The extra 1 is because 10^n takes up 1 + n digits, not n.)
167 //
168 // The longest negative exponent is a bit harder: min_exponent10 gives us
169 // the smallest power of 10 which a normalised version of T can represent.
170 // But the smallest denormalised power of 10 that T can represent is
171 // another max_digits10 powers of 10 below that.
172 // needs a minus sign.
173 //
174 // All this stuff messes with my head a bit because it's on the order of
175 // log10(log10(n)). It's easy to get the number of logs wrong.
176 auto const max_pos_exp{digits10(lims::max_exponent10)};
177 // Really want std::abs(lims::min_exponent10), but MSVC 2017 apparently has
178 // problems with std::abs. So we use -lims::min_exponent10 instead.
179 auto const max_neg_exp{
180 digits10(lims::max_digits10 - lims::min_exponent10)};
181 return 1 + // Sign.
182 1 + // Decimal point.
183 std::numeric_limits<T>::max_digits10 + // Mantissa digits.
184 1 + // Exponent "e".
185 1 + // Exponent sign.
186 // Spell this weirdly to stop Windows compilers from reading this as
187 // a call to their "max" macro when NOMINMAX is not defined.
188 (std::max)(max_pos_exp, max_neg_exp) + // Exponent digits.
189 1; // Terminating zero.
190 }
191};
192} // namespace pqxx::internal
193
194
195namespace pqxx
196{
198
201template<typename T>
202struct nullness<T, std::enable_if_t<std::is_arithmetic_v<T>>> : no_null<T>
203{};
204
205
207{};
208template<> inline constexpr bool is_unquoted_safe<short>{true};
209template<>
211 : internal::integral_traits<unsigned short>
212{};
213template<> inline constexpr bool is_unquoted_safe<unsigned short>{true};
215{};
216template<> inline constexpr bool is_unquoted_safe<int>{true};
218{};
219template<> inline constexpr bool is_unquoted_safe<unsigned>{true};
221{};
222template<> inline constexpr bool is_unquoted_safe<long>{true};
223template<>
225{};
226template<> inline constexpr bool is_unquoted_safe<unsigned long>{true};
227template<>
230template<> inline constexpr bool is_unquoted_safe<long long>{true};
231template<>
233 : internal::integral_traits<unsigned long long>
234{};
235template<> inline constexpr bool is_unquoted_safe<unsigned long long>{true};
236template<> struct string_traits<float> : internal::float_traits<float>
237{};
238template<> inline constexpr bool is_unquoted_safe<float>{true};
239template<> struct string_traits<double> : internal::float_traits<double>
240{};
241template<> inline constexpr bool is_unquoted_safe<double>{true};
242template<>
244{};
245template<> inline constexpr bool is_unquoted_safe<long double>{true};
246
247
248template<> struct string_traits<bool>
249{
250 static constexpr bool converts_to_string{true};
251 static constexpr bool converts_from_string{true};
252
253 static PQXX_LIBEXPORT bool from_string(std::string_view text);
254
255 static constexpr zview to_buf(char *, char *, bool const &value) noexcept
256 {
257 return value ? "true"_zv : "false"_zv;
258 }
259
260 static char *into_buf(char *begin, char *end, bool const &value)
261 {
262 return pqxx::internal::generic_into_buf(begin, end, value);
263 }
264
265 static constexpr std::size_t size_buffer(bool const &) noexcept { return 6; }
266};
267
268
269template<> inline constexpr bool is_unquoted_safe<bool>{true};
270
271
272template<typename T> struct nullness<std::optional<T>>
273{
274 static constexpr bool has_null = true;
276 static constexpr bool always_null = nullness<T>::always_null;
277 static constexpr bool is_null(std::optional<T> const &v) noexcept
278 {
279 return ((not v.has_value()) or pqxx::is_null(*v));
280 }
281 static constexpr std::optional<T> null() { return {}; }
282};
283
284
285template<typename T>
286inline constexpr format param_format(std::optional<T> const &value)
287{
288 return param_format(*value);
289}
290
291
292template<typename T> struct string_traits<std::optional<T>>
293{
294 static constexpr bool converts_to_string{
296 static constexpr bool converts_from_string{
298
299 static char *into_buf(char *begin, char *end, std::optional<T> const &value)
300 {
301 return string_traits<T>::into_buf(begin, end, *value);
302 }
303
304 static zview to_buf(char *begin, char *end, std::optional<T> const &value)
305 {
306 if (value.has_value())
307 return string_traits<T>::to_buf(begin, end, *value);
308 else
309 return {};
310 }
311
312 static std::optional<T> from_string(std::string_view text)
313 {
314 return std::optional<T>{
315 std::in_place, string_traits<T>::from_string(text)};
316 }
317
318 static std::size_t size_buffer(std::optional<T> const &value) noexcept
319 {
320 return pqxx::size_buffer(value.value());
321 }
322};
323
324
325template<typename T>
327
328
329template<typename... T> struct nullness<std::variant<T...>>
330{
331 static constexpr bool has_null = (nullness<T>::has_null or ...);
332 static constexpr bool always_null = (nullness<T>::always_null and ...);
333 static constexpr bool is_null(std::variant<T...> const &value) noexcept
334 {
335 return std::visit(
336 [](auto const &i) noexcept {
337 return nullness<strip_t<decltype(i)>>::is_null(i);
338 },
339 value);
340 }
341
342 // We don't support `null()` for `std::variant`.
346 static constexpr std::variant<T...> null() = delete;
347};
348
349
350template<typename... T> struct string_traits<std::variant<T...>>
351{
352 static constexpr bool converts_to_string{
354
355 static char *
356 into_buf(char *begin, char *end, std::variant<T...> const &value)
357 {
358 return std::visit(
359 [begin, end](auto const &i) {
360 return string_traits<strip_t<decltype(i)>>::into_buf(begin, end, i);
361 },
362 value);
363 }
364 static zview to_buf(char *begin, char *end, std::variant<T...> const &value)
365 {
366 return std::visit(
367 [begin, end](auto const &i) {
368 return string_traits<strip_t<decltype(i)>>::to_buf(begin, end, i);
369 },
370 value);
371 }
372 static std::size_t size_buffer(std::variant<T...> const &value) noexcept
373 {
374 return std::visit(
375 [](auto const &i) noexcept { return pqxx::size_buffer(i); }, value);
376 }
377
382 static std::variant<T...> from_string(std::string_view) = delete;
383};
384
385
386template<typename... Args>
387inline constexpr format param_format(std::variant<Args...> const &value)
388{
389 return std::visit([](auto &v) { return param_format(v); }, value);
390}
391
392
393template<typename... T>
394inline constexpr bool is_unquoted_safe<std::variant<T...>>{
396
397
398template<typename T> inline T from_string(std::stringstream const &text)
399{
400 return from_string<T>(text.str());
401}
402
403
404template<> struct string_traits<std::nullptr_t>
405{
406 static constexpr bool converts_to_string{false};
407 static constexpr bool converts_from_string{false};
408
409 static char *into_buf(char *, char *, std::nullptr_t) = delete;
410
411 [[deprecated("Do not convert nulls.")]] static constexpr zview
412 to_buf(char *, char *, std::nullptr_t const &) noexcept
413 {
414 return {};
415 }
416
417 [[deprecated("Do not convert nulls.")]] static constexpr std::size_t
418 size_buffer(std::nullptr_t = nullptr) noexcept
419 {
420 return 0;
421 }
422 static std::nullptr_t from_string(std::string_view) = delete;
423};
424
425
426template<> struct string_traits<std::nullopt_t>
427{
428 static constexpr bool converts_to_string{false};
429 static constexpr bool converts_from_string{false};
430
431 static char *into_buf(char *, char *, std::nullopt_t) = delete;
432
433 [[deprecated("Do not convert nulls.")]] static constexpr zview
434 to_buf(char *, char *, std::nullopt_t const &) noexcept
435 {
436 return {};
437 }
438
439 [[deprecated("Do not convert nulls.")]] static constexpr std::size_t
440 size_buffer(std::nullopt_t) noexcept
441 {
442 return 0;
443 }
444 static std::nullopt_t from_string(std::string_view) = delete;
445};
446
447
448template<> struct string_traits<std::monostate>
449{
450 static constexpr bool converts_to_string{false};
451 static constexpr bool converts_from_string{false};
452
453 static char *into_buf(char *, char *, std::monostate) = delete;
454
455 [[deprecated("Do not convert nulls.")]] static constexpr zview
456 to_buf(char *, char *, std::monostate const &) noexcept
457 {
458 return {};
459 }
460
461 [[deprecated("Do not convert nulls.")]] static constexpr std::size_t
462 size_buffer(std::monostate) noexcept
463 {
464 return 0;
465 }
466 [[deprecated("Do not convert nulls.")]] static std::monostate
467 from_string(std::string_view) = delete;
468};
469
470
471template<> inline constexpr bool is_unquoted_safe<std::nullptr_t>{true};
472
473
474template<> struct nullness<char const *>
475{
476 static constexpr bool has_null = true;
477 static constexpr bool always_null = false;
478 static constexpr bool is_null(char const *t) noexcept
479 {
480 return t == nullptr;
481 }
482 static constexpr char const *null() noexcept { return nullptr; }
483};
484
485
487template<> struct string_traits<char const *>
488{
489 static constexpr bool converts_to_string{true};
490 static constexpr bool converts_from_string{true};
491
492 static char const *from_string(std::string_view text) { return text.data(); }
493
494 static zview to_buf(char *begin, char *end, char const *const &value)
495 {
496 return generic_to_buf(begin, end, value);
497 }
498
499 static char *into_buf(char *begin, char *end, char const *const &value)
500 {
501 auto const space{end - begin};
502 // Count the trailing zero, even though std::strlen() and friends don't.
503 auto const len{std::strlen(value) + 1};
504 if (space < ptrdiff_t(len))
505 throw conversion_overrun{
506 "Could not copy string: buffer too small. " +
508 std::memmove(begin, value, len);
509 return begin + len;
510 }
511
512 static std::size_t size_buffer(char const *const &value) noexcept
513 {
514 return std::strlen(value) + 1;
515 }
516};
517
518
519template<> struct nullness<char *>
520{
521 static constexpr bool has_null = true;
522 static constexpr bool always_null = false;
523 static constexpr bool is_null(char const *t) noexcept
524 {
525 return t == nullptr;
526 }
527 static constexpr char const *null() { return nullptr; }
528};
529
530
532template<> struct string_traits<char *>
533{
534 static constexpr bool converts_to_string{true};
535 static constexpr bool converts_from_string{false};
536
537 static char *into_buf(char *begin, char *end, char *const &value)
538 {
539 return string_traits<char const *>::into_buf(begin, end, value);
540 }
541 static zview to_buf(char *begin, char *end, char *const &value)
542 {
543 return string_traits<char const *>::to_buf(begin, end, value);
544 }
545 static std::size_t size_buffer(char *const &value) noexcept
546 {
548 }
549
551 static char *from_string(std::string_view) = delete;
552};
553
554
555template<std::size_t N> struct nullness<char[N]> : no_null<char[N]>
556{};
557
558
560
563template<std::size_t N> struct string_traits<char[N]>
564{
565 static constexpr bool converts_to_string{true};
566 static constexpr bool converts_from_string{false};
567
568 static constexpr zview
569 to_buf(char *, char *, char const (&value)[N]) noexcept
570 {
571 return zview{value, N - 1};
572 }
573
574 static char *into_buf(char *begin, char *end, char const (&value)[N])
575 {
576 if (internal::cmp_less(end - begin, size_buffer(value)))
577 throw conversion_overrun{
578 "Could not convert char[] to string: too long for buffer."};
579 std::memcpy(begin, value, N);
580 return begin + N;
581 }
582 static constexpr std::size_t size_buffer(char const (&)[N]) noexcept
583 {
584 return N;
585 }
586
588 static void from_string(std::string_view) = delete;
589};
590
591
592template<> struct nullness<std::string> : no_null<std::string>
593{};
594
595
596template<> struct string_traits<std::string>
597{
598 static constexpr bool converts_to_string{true};
599 static constexpr bool converts_from_string{true};
600
601 static std::string from_string(std::string_view text)
602 {
603 return std::string{text};
604 }
605
606 static char *into_buf(char *begin, char *end, std::string const &value)
607 {
608 if (internal::cmp_greater_equal(std::size(value), end - begin))
609 throw conversion_overrun{
610 "Could not convert string to string: too long for buffer."};
611 // Include the trailing zero.
612 value.copy(begin, std::size(value));
613 begin[std::size(value)] = '\0';
614 return begin + std::size(value) + 1;
615 }
616
617 static zview to_buf(char *begin, char *end, std::string const &value)
618 {
619 return generic_to_buf(begin, end, value);
620 }
621
622 static std::size_t size_buffer(std::string const &value) noexcept
623 {
624 return std::size(value) + 1;
625 }
626};
627
628
630
633template<> struct nullness<std::string_view> : no_null<std::string_view>
634{};
635
636
638template<> struct string_traits<std::string_view>
639{
640 static constexpr bool converts_to_string{true};
641 static constexpr bool converts_from_string{false};
642
643 static constexpr std::size_t
644 size_buffer(std::string_view const &value) noexcept
645 {
646 return std::size(value) + 1;
647 }
648
649 static char *into_buf(char *begin, char *end, std::string_view const &value)
650 {
651 if (internal::cmp_greater_equal(std::size(value), end - begin))
652 throw conversion_overrun{
653 "Could not store string_view: too long for buffer."};
654 value.copy(begin, std::size(value));
655 begin[std::size(value)] = '\0';
656 return begin + std::size(value) + 1;
657 }
658
659 static zview to_buf(char *begin, char *end, std::string_view const &value)
660 {
661 // You'd think we could just return the same view but alas, there's no
662 // zero-termination on a string_view.
663 return generic_to_buf(begin, end, value);
664 }
665
667 static std::string_view from_string(std::string_view) = delete;
668};
669
670
671template<> struct nullness<zview> : no_null<zview>
672{};
673
674
676template<> struct string_traits<zview>
677{
678 static constexpr bool converts_to_string{true};
679 static constexpr bool converts_from_string{false};
680
681 static constexpr std::size_t
682 size_buffer(std::string_view const &value) noexcept
683 {
684 return std::size(value) + 1;
685 }
686
687 static char *into_buf(char *begin, char *end, zview const &value)
688 {
689 auto const size{std::size(value)};
690 if (internal::cmp_less_equal(end - begin, std::size(value)))
691 throw conversion_overrun{"Not enough buffer space to store this zview."};
692 value.copy(begin, size);
693 begin[size] = '\0';
694 return begin + size + 1;
695 }
696
697 static std::string_view to_buf(char *begin, char *end, zview const &value)
698 {
699 char *const stop{into_buf(begin, end, value)};
700 return {begin, static_cast<std::size_t>(stop - begin - 1)};
701 }
702
704 static zview from_string(std::string_view) = delete;
705};
706
707
708template<> struct nullness<std::stringstream> : no_null<std::stringstream>
709{};
710
711
712template<> struct string_traits<std::stringstream>
713{
714 static constexpr bool converts_to_string{false};
715 static constexpr bool converts_from_string{true};
716
717 static std::size_t size_buffer(std::stringstream const &) = delete;
718
719 static std::stringstream from_string(std::string_view text)
720 {
721 std::stringstream stream;
722 stream.write(text.data(), std::streamsize(std::size(text)));
723 return stream;
724 }
725
726 static char *into_buf(char *, char *, std::stringstream const &) = delete;
727 static std::string_view
728 to_buf(char *, char *, std::stringstream const &) = delete;
729};
730
731
732template<> struct nullness<std::nullptr_t>
733{
734 static constexpr bool has_null = true;
735 static constexpr bool always_null = true;
736 static constexpr bool is_null(std::nullptr_t const &) noexcept
737 {
738 return true;
739 }
740 static constexpr std::nullptr_t null() noexcept { return nullptr; }
741};
742
743
744template<> struct nullness<std::nullopt_t>
745{
746 static constexpr bool has_null = true;
747 static constexpr bool always_null = true;
748 static constexpr bool is_null(std::nullopt_t const &) noexcept
749 {
750 return true;
751 }
752 static constexpr std::nullopt_t null() noexcept { return std::nullopt; }
753};
754
755
756template<> struct nullness<std::monostate>
757{
758 static constexpr bool has_null = true;
759 static constexpr bool always_null = true;
760 static constexpr bool is_null(std::monostate const &) noexcept
761 {
762 return true;
763 }
764 static constexpr std::monostate null() noexcept { return {}; }
765};
766
767
768template<typename T> struct nullness<std::unique_ptr<T>>
769{
770 static constexpr bool has_null = true;
771 static constexpr bool always_null = false;
772 static constexpr bool is_null(std::unique_ptr<T> const &t) noexcept
773 {
774 return not t or pqxx::is_null(*t);
775 }
776 static constexpr std::unique_ptr<T> null() { return {}; }
777};
778
779
780template<typename T, typename... Args>
781struct string_traits<std::unique_ptr<T, Args...>>
782{
783 static constexpr bool converts_to_string{
785 static constexpr bool converts_from_string{
787
788 static std::unique_ptr<T> from_string(std::string_view text)
789 {
790 return std::make_unique<T>(string_traits<T>::from_string(text));
791 }
792
793 static char *
794 into_buf(char *begin, char *end, std::unique_ptr<T, Args...> const &value)
795 {
796 return string_traits<T>::into_buf(begin, end, *value);
797 }
798
799 static zview
800 to_buf(char *begin, char *end, std::unique_ptr<T, Args...> const &value)
801 {
802 if (value)
803 return string_traits<T>::to_buf(begin, end, *value);
804 else
805 return {};
806 }
807
808 static std::size_t
809 size_buffer(std::unique_ptr<T, Args...> const &value) noexcept
810 {
811 return pqxx::size_buffer(*value.get());
812 }
813};
814
815
816template<typename T, typename... Args>
817inline format param_format(std::unique_ptr<T, Args...> const &value)
818{
819 return param_format(*value);
820}
821
822
823template<typename T, typename... Args>
824inline constexpr bool is_unquoted_safe<std::unique_ptr<T, Args...>>{
826
827
828template<typename T> struct nullness<std::shared_ptr<T>>
829{
830 static constexpr bool has_null = true;
831 static constexpr bool always_null = false;
832 static constexpr bool is_null(std::shared_ptr<T> const &t) noexcept
833 {
834 return not t or pqxx::is_null(*t);
835 }
836 static constexpr std::shared_ptr<T> null() { return {}; }
837};
838
839
840template<typename T> struct string_traits<std::shared_ptr<T>>
841{
842 static constexpr bool converts_to_string{
844 static constexpr bool converts_from_string{
846
847 static std::shared_ptr<T> from_string(std::string_view text)
848 {
849 return std::make_shared<T>(string_traits<T>::from_string(text));
850 }
851
852 static zview to_buf(char *begin, char *end, std::shared_ptr<T> const &value)
853 {
854 return string_traits<T>::to_buf(begin, end, *value);
855 }
856 static char *
857 into_buf(char *begin, char *end, std::shared_ptr<T> const &value)
858 {
859 return string_traits<T>::into_buf(begin, end, *value);
860 }
861 static std::size_t size_buffer(std::shared_ptr<T> const &value) noexcept
862 {
863 return pqxx::size_buffer(*value);
864 }
865};
866
867
868template<typename T> format param_format(std::shared_ptr<T> const &value)
869{
870 return param_format(*value);
871}
872
873
874template<typename T>
875inline constexpr bool is_unquoted_safe<std::shared_ptr<T>>{
877
878
879template<> struct nullness<bytes> : no_null<bytes>
880{};
881
882
883#if defined(PQXX_HAVE_CONCEPTS)
884template<binary DATA> struct nullness<DATA> : no_null<DATA>
885{};
886
887
888template<binary DATA> inline constexpr format param_format(DATA const &)
889{
890 return format::binary;
891}
892
893
894template<binary DATA> struct string_traits<DATA>
895{
896 static constexpr bool converts_to_string{true};
897 static constexpr bool converts_from_string{true};
898
899 static std::size_t size_buffer(DATA const &value) noexcept
900 {
901 return internal::size_esc_bin(std::size(value));
902 }
903
904 static zview to_buf(char *begin, char *end, DATA const &value)
905 {
906 return generic_to_buf(begin, end, value);
907 }
908
909 static char *into_buf(char *begin, char *end, DATA const &value)
910 {
911 auto const budget{size_buffer(value)};
912 if (internal::cmp_less(end - begin, budget))
913 throw conversion_overrun{
914 "Not enough buffer space to escape binary data."};
915 internal::esc_bin(value, begin);
916 return begin + budget;
917 }
918
919 static DATA from_string(std::string_view text)
920 {
921 auto const size{pqxx::internal::size_unesc_bin(std::size(text))};
922 bytes buf;
923 buf.resize(size);
924 pqxx::internal::unesc_bin(text, reinterpret_cast<std::byte *>(buf.data()));
925 return buf;
926 }
927};
928#endif // PQXX_HAVE_CONCEPTS
929
930
931template<> struct string_traits<bytes>
932{
933 static constexpr bool converts_to_string{true};
934 static constexpr bool converts_from_string{true};
935
936 static std::size_t size_buffer(bytes const &value) noexcept
937 {
938 return internal::size_esc_bin(std::size(value));
939 }
940
941 static zview to_buf(char *begin, char *end, bytes const &value)
942 {
943 return generic_to_buf(begin, end, value);
944 }
945
946 static char *into_buf(char *begin, char *end, bytes const &value)
947 {
948 auto const budget{size_buffer(value)};
949 if (internal::cmp_less(end - begin, budget))
950 throw conversion_overrun{
951 "Not enough buffer space to escape binary data."};
952 internal::esc_bin(value, begin);
953 return begin + budget;
954 }
955
956 static bytes from_string(std::string_view text)
957 {
958 auto const size{pqxx::internal::size_unesc_bin(std::size(text))};
959 bytes buf;
960 buf.resize(size);
961 pqxx::internal::unesc_bin(text, reinterpret_cast<std::byte *>(buf.data()));
962 return buf;
963 }
964};
965
966
967template<> inline constexpr format param_format(bytes const &)
968{
969 return format::binary;
970}
971
972
973template<> struct nullness<bytes_view> : no_null<bytes_view>
974{};
975
976
977template<> struct string_traits<bytes_view>
978{
979 static constexpr bool converts_to_string{true};
980 static constexpr bool converts_from_string{false};
981
982 static std::size_t size_buffer(bytes_view const &value) noexcept
983 {
984 return internal::size_esc_bin(std::size(value));
985 }
986
987 static zview to_buf(char *begin, char *end, bytes_view const &value)
988 {
989 return generic_to_buf(begin, end, value);
990 }
991
992 static char *into_buf(char *begin, char *end, bytes_view const &value)
993 {
994 auto const budget{size_buffer(value)};
995 if (internal::cmp_less(end - begin, budget))
996 throw conversion_overrun{
997 "Not enough buffer space to escape binary data."};
998 internal::esc_bin(value, begin);
999 return begin + budget;
1000 }
1001
1002 // There's no from_string, because there's nobody to hold the data.
1003};
1004
1005template<> inline constexpr format param_format(bytes_view const &)
1006{
1007 return format::binary;
1008}
1009} // namespace pqxx
1010
1011
1012namespace pqxx::internal
1013{
1015template<typename Container> struct array_string_traits
1016{
1017private:
1018 using elt_type = strip_t<value_type<Container>>;
1020 static constexpr zview s_null{"NULL"};
1021
1022public:
1023 static constexpr bool converts_to_string{true};
1024 static constexpr bool converts_from_string{false};
1025
1026 static zview to_buf(char *begin, char *end, Container const &value)
1027 {
1028 return generic_to_buf(begin, end, value);
1029 }
1030
1031 static char *into_buf(char *begin, char *end, Container const &value)
1032 {
1033 assert(begin <= end);
1034 std::size_t const budget{size_buffer(value)};
1035 if (internal::cmp_less(end - begin, budget))
1036 throw conversion_overrun{
1037 "Not enough buffer space to convert array to string."};
1038
1039 char *here = begin;
1040 *here++ = '{';
1041
1042 bool nonempty{false};
1043 for (auto const &elt : value)
1044 {
1045 if (is_null(elt))
1046 {
1047 s_null.copy(here, std::size(s_null));
1048 here += std::size(s_null);
1049 }
1050 else if constexpr (is_sql_array<elt_type>)
1051 {
1052 // Render nested array in-place. Then erase the trailing zero.
1053 here = elt_traits::into_buf(here, end, elt) - 1;
1054 }
1055 else if constexpr (is_unquoted_safe<elt_type>)
1056 {
1057 // No need to quote or escape. Just convert the value straight into
1058 // its place in the array, and "backspace" the trailing zero.
1059 here = elt_traits::into_buf(here, end, elt) - 1;
1060 }
1061 else
1062 {
1063 *here++ = '"';
1064
1065 // Use the tail end of the destination buffer as an intermediate
1066 // buffer.
1067 auto const elt_budget{pqxx::size_buffer(elt)};
1068 assert(elt_budget < static_cast<std::size_t>(end - here));
1069 for (char const c : elt_traits::to_buf(end - elt_budget, end, elt))
1070 {
1071 // We copy the intermediate buffer into the final buffer, char by
1072 // char, with escaping where necessary.
1073 // TODO: This will not work for all encodings. UTF8 & ASCII are OK.
1074 if (c == '\\' or c == '"')
1075 *here++ = '\\';
1076 *here++ = c;
1077 }
1078 *here++ = '"';
1079 }
1081 nonempty = true;
1082 }
1083
1084 // Erase that last comma, if present.
1085 if (nonempty)
1086 here--;
1087
1088 *here++ = '}';
1089 *here++ = '\0';
1090
1091 return here;
1092 }
1093
1094 static std::size_t size_buffer(Container const &value) noexcept
1095 {
1096 if constexpr (is_unquoted_safe<elt_type>)
1097 return 3 + std::accumulate(
1098 std::begin(value), std::end(value), std::size_t{},
1099 [](std::size_t acc, elt_type const &elt) {
1100 return acc +
1101 (pqxx::is_null(elt) ?
1102 std::size(s_null) :
1104 1;
1105 });
1106 else
1107 return 3 + std::accumulate(
1108 std::begin(value), std::end(value), std::size_t{},
1109 [](std::size_t acc, elt_type const &elt) {
1110 // Opening and closing quotes, plus worst-case escaping,
1111 // but don't count the trailing zeroes.
1112 std::size_t const elt_size{
1113 pqxx::is_null(elt) ? std::size(s_null) :
1115 return acc + 2 * elt_size + 2;
1116 });
1117 }
1118
1119 // We don't yet support parsing of array types using from_string. Doing so
1120 // would require a reference to the connection.
1121};
1122} // namespace pqxx::internal
1123
1124
1125namespace pqxx
1126{
1127template<typename T, typename... Args>
1128struct nullness<std::vector<T, Args...>> : no_null<std::vector<T>>
1129{};
1130
1131
1132template<typename T, typename... Args>
1133struct string_traits<std::vector<T, Args...>>
1134 : internal::array_string_traits<std::vector<T, Args...>>
1135{};
1136
1137
1139template<typename T, typename... Args>
1140inline constexpr format param_format(std::vector<T, Args...> const &)
1141{
1142 return format::text;
1143}
1144
1145
1147template<typename... Args>
1148inline constexpr format param_format(std::vector<std::byte, Args...> const &)
1149{
1150 return format::binary;
1151}
1152
1153
1154template<typename T> inline constexpr bool is_sql_array<std::vector<T>>{true};
1155
1156
1157#if defined(PQXX_HAVE_SPAN) && __has_include(<span>)
1158template<typename T, size_t Extent>
1159struct nullness<std::span<T, Extent>> : no_null<std::span<T, Extent>>
1160{};
1161
1162
1163template<typename T, size_t Extent>
1164struct string_traits<std::span<T, Extent>>
1165 : internal::array_string_traits<std::span<T, Extent>>
1166{};
1167
1168
1169template<typename T, size_t Extent>
1170inline constexpr format param_format(std::span<T, Extent> const &)
1171{
1172 return format::text;
1173}
1174
1175
1176template<size_t Extent>
1177inline constexpr format param_format(std::span<std::byte, Extent> const &)
1178{
1179 return format::binary;
1180}
1181
1182
1183template<typename T, size_t Extent>
1184inline constexpr bool is_sql_array<std::span<T, Extent>>{true};
1185#endif
1186
1187
1188template<typename T, std::size_t N>
1189struct nullness<std::array<T, N>> : no_null<std::array<T, N>>
1190{};
1191
1192
1193template<typename T, std::size_t N>
1194struct string_traits<std::array<T, N>>
1195 : internal::array_string_traits<std::array<T, N>>
1196{};
1197
1198
1200template<typename T, typename... Args, Args... args>
1201inline constexpr format param_format(std::array<T, args...> const &)
1202{
1203 return format::text;
1204}
1205
1206
1208template<typename... Args, Args... args>
1209inline constexpr format param_format(std::array<std::byte, args...> const &)
1210{
1211 return format::binary;
1212}
1213
1214
1215template<typename T, std::size_t N>
1216inline constexpr bool is_sql_array<std::array<T, N>>{true};
1217} // namespace pqxx
1218
1219
1220namespace pqxx
1221{
1222template<typename T> inline std::string to_string(T const &value)
1223{
1224 if (is_null(value))
1225 throw conversion_error{
1226 "Attempt to convert null " + std::string{type_name<T>} +
1227 " to a string."};
1228
1229 std::string buf;
1230 // We can't just reserve() space; modifying the terminating zero leads to
1231 // undefined behaviour.
1232 buf.resize(size_buffer(value));
1233 auto const data{buf.data()};
1234 auto const end{
1235 string_traits<T>::into_buf(data, data + std::size(buf), value)};
1236 buf.resize(static_cast<std::size_t>(end - data - 1));
1237 return buf;
1238}
1239
1240
1241template<> inline std::string to_string(float const &value)
1242{
1243 return internal::to_string_float(value);
1244}
1245template<> inline std::string to_string(double const &value)
1246{
1247 return internal::to_string_float(value);
1248}
1249template<> inline std::string to_string(long double const &value)
1250{
1251 return internal::to_string_float(value);
1252}
1253template<> inline std::string to_string(std::stringstream const &value)
1254{
1255 return value.str();
1256}
1257
1258
1259template<typename T> inline void into_string(T const &value, std::string &out)
1260{
1261 if (is_null(value))
1262 throw conversion_error{
1263 "Attempt to convert null " + type_name<T> + " to a string."};
1264
1265 // We can't just reserve() data; modifying the terminating zero leads to
1266 // undefined behaviour.
1267 out.resize(size_buffer(value) + 1);
1268 auto const data{out.data()};
1269 auto const end{
1270 string_traits<T>::into_buf(data, data + std::size(out), value)};
1271 out.resize(static_cast<std::size_t>(end - data - 1));
1272}
1273} // namespace pqxx
An SQL array received from the database.
Definition array.hxx:56
Marker-type wrapper: zero-terminated std::string_view.
Definition zview.hxx:38
Could not convert value to string: not enough buffer space.
Definition except.hxx:313
Internal items for libpqxx' own use. Do not use these yourself.
Definition encodings.cxx:33
void PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data, std::byte buffer[])
Reconstitute binary data from its escaped version.
Definition util.cxx:165
void throw_null_conversion(std::string const &type)
Throw exception for attempt to convert SQL NULL to given type.
Definition strconv.cxx:255
constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
Compute buffer size needed to escape binary data for use as a BYTEA.
Definition util.hxx:516
constexpr char number_to_digit(int i) noexcept
Convert a number in [0, 9] to its ASCII digit.
Definition conversions.hxx:27
constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept
Same as std::cmp_less, or a workaround where that's not available.
Definition util.hxx:65
constexpr int digit_to_number(char c) noexcept
Compute numeric value of given textual digit (assuming that it is a digit).
Definition conversions.hxx:34
void PQXX_LIBEXPORT esc_bin(bytes_view binary_data, char buffer[]) noexcept
Hex-escape binary data into a buffer.
Definition util.cxx:133
std::string to_string_float(T value)
Floating-point implementations for pqxx::to_string().
Definition strconv.cxx:668
constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater, or workaround if not available.
Definition util.hxx:87
std::string state_buffer_overrun(int have_bytes, int need_bytes)
Summarize buffer overrun.
Definition strconv.cxx:267
char * generic_into_buf(char *begin, char *end, T const &value)
Generic implementation for into_buf, on top of to_buf.
Definition conversions.hxx:95
constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater_equal, or workaround if not available.
Definition util.hxx:113
constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_less_equal, or workaround if not available.
Definition util.hxx:100
constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
Compute binary size from the size of its escaped version.
Definition util.hxx:525
The home of all libpqxx classes, functions, templates, etc.
Definition array.cxx:27
std::vector< std::string_view > to_buf(char *here, char const *end, TYPE... value)
Convert multiple values to strings inside a single buffer.
Definition strconv.hxx:492
constexpr char array_separator
Element separator between SQL array elements of this type.
Definition strconv.hxx:558
std::conditional< has_generic_bytes_char_traits, std::basic_string_view< std::byte >, std::basic_string_view< std::byte, byte_char_traits > >::type bytes_view
Type alias for a view of bytes.
Definition util.hxx:383
std::conditional< has_generic_bytes_char_traits, std::basic_string< std::byte >, std::basic_string< std::byte, byte_char_traits > >::type bytes
Type alias for a container containing bytes.
Definition util.hxx:373
std::size_t size_buffer(TYPE const &...value) noexcept
Estimate how much buffer space is needed to represent values as a string.
Definition strconv.hxx:525
std::remove_cv_t< std::remove_reference_t< TYPE > > strip_t
Remove any constness, volatile, and reference-ness from a type.
Definition types.hxx:78
constexpr bool is_null(TYPE const &value) noexcept
Is value null?
Definition strconv.hxx:514
zview generic_to_buf(char *begin, char *end, TYPE const &value)
Implement string_traits<TYPE>::to_buf by calling into_buf.
Definition strconv.hxx:586
T from_string(field const &value)
Convert a field's value to type T.
Definition field.hxx:532
constexpr bool is_unquoted_safe
Can we use this type in arrays and composite types without quoting them?
Definition strconv.hxx:554
format
Format code: is data text or binary?
Definition types.hxx:68
String traits for SQL arrays.
Definition conversions.hxx:1016
Deliberately nonfunctional conversion traits for char types.
Definition conversions.hxx:77
String traits for builtin floating-point types.
Definition conversions.hxx:134
static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value)
Floating-point to_buf implemented in terms of to_string.
Definition strconv.cxx:600
String traits for builtin integral types (though not bool).
Definition conversions.hxx:113
Nullness traits describing a type which does not have a null value.
Definition strconv.hxx:113
static constexpr std::variant< T... > null()=delete
Traits describing a type's "null value," if any.
Definition strconv.hxx:91
static bool is_null(TYPE const &value)
Is value a null?
static TYPE null()
Return a null value.
static bool has_null
Does this type have a null value?
Definition strconv.hxx:93
static bool always_null
Is this type always null?
Definition strconv.hxx:96
static char * from_string(std::string_view)=delete
Don't allow conversion to this type since it breaks const-safety.
static void from_string(std::string_view)=delete
Don't allow conversion to this type.
static std::string_view from_string(std::string_view)=delete
Don't convert to this type; it has nowhere to store its contents.
static std::variant< T... > from_string(std::string_view)=delete
static zview from_string(std::string_view)=delete
Don't convert to this type; it has nowhere to store its contents.
Traits class for use in string conversions.
Definition strconv.hxx:153
static TYPE from_string(std::string_view text)
Parse a string representation of a TYPE value.
static std::size_t size_buffer(TYPE const &value) noexcept
Estimate how much buffer space is needed to represent value.
static zview to_buf(char *begin, char *end, TYPE const &value)
Return a string_view representing value, plus terminating zero.
static constexpr bool converts_to_string
Is conversion from TYPE to strings supported?
Definition strconv.hxx:158
static char * into_buf(char *begin, char *end, TYPE const &value)
Write value's string representation into buffer at begin.
static constexpr bool converts_from_string
Is conversion from string_view to TYPE supported?
Definition strconv.hxx:164