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 (pqxx::is_null(value))
307 return {};
308 else
309 return string_traits<T>::to_buf(begin, end, *value);
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 if (pqxx::is_null(value))
321 return 0;
322 else
323 return pqxx::size_buffer(value.value());
324 }
325};
326
327
328template<typename T>
330
331
332template<typename... T> struct nullness<std::variant<T...>>
333{
334 static constexpr bool has_null = (nullness<T>::has_null or ...);
335 static constexpr bool always_null = (nullness<T>::always_null and ...);
336 static constexpr bool is_null(std::variant<T...> const &value) noexcept
337 {
338 return value.valueless_by_exception() or
339 std::visit(
340 [](auto const &i) noexcept {
341 return nullness<strip_t<decltype(i)>>::is_null(i);
342 },
343 value);
344 }
345
346 // We don't support `null()` for `std::variant`.
350 static constexpr std::variant<T...> null() = delete;
351};
352
353
354template<typename... T> struct string_traits<std::variant<T...>>
355{
356 static constexpr bool converts_to_string{
358
359 static char *
360 into_buf(char *begin, char *end, std::variant<T...> const &value)
361 {
362 return std::visit(
363 [begin, end](auto const &i) {
364 return string_traits<strip_t<decltype(i)>>::into_buf(begin, end, i);
365 },
366 value);
367 }
368 static zview to_buf(char *begin, char *end, std::variant<T...> const &value)
369 {
370 return std::visit(
371 [begin, end](auto const &i) {
372 return string_traits<strip_t<decltype(i)>>::to_buf(begin, end, i);
373 },
374 value);
375 }
376 static std::size_t size_buffer(std::variant<T...> const &value) noexcept
377 {
378 if (pqxx::is_null(value))
379 return 0;
380 else
381 return std::visit(
382 [](auto const &i) noexcept { return pqxx::size_buffer(i); }, value);
383 }
384
389 static std::variant<T...> from_string(std::string_view) = delete;
390};
391
392
393template<typename... Args>
394inline constexpr format param_format(std::variant<Args...> const &value)
395{
396 return std::visit([](auto &v) { return param_format(v); }, value);
397}
398
399
400template<typename... T>
401inline constexpr bool is_unquoted_safe<std::variant<T...>>{
403
404
405template<typename T> inline T from_string(std::stringstream const &text)
406{
407 return from_string<T>(text.str());
408}
409
410
411template<> struct string_traits<std::nullptr_t>
412{
413 static constexpr bool converts_to_string{false};
414 static constexpr bool converts_from_string{false};
415
416 static char *into_buf(char *, char *, std::nullptr_t) = delete;
417
418 [[deprecated("Do not convert nulls.")]] static constexpr zview
419 to_buf(char *, char *, std::nullptr_t const &) noexcept
420 {
421 return {};
422 }
423
424 [[deprecated("Do not convert nulls.")]] static constexpr std::size_t
425 size_buffer(std::nullptr_t = nullptr) noexcept
426 {
427 return 0;
428 }
429 static std::nullptr_t from_string(std::string_view) = delete;
430};
431
432
433template<> struct string_traits<std::nullopt_t>
434{
435 static constexpr bool converts_to_string{false};
436 static constexpr bool converts_from_string{false};
437
438 static char *into_buf(char *, char *, std::nullopt_t) = delete;
439
440 [[deprecated("Do not convert nulls.")]] static constexpr zview
441 to_buf(char *, char *, std::nullopt_t const &) noexcept
442 {
443 return {};
444 }
445
446 [[deprecated("Do not convert nulls.")]] static constexpr std::size_t
447 size_buffer(std::nullopt_t) noexcept
448 {
449 return 0;
450 }
451 static std::nullopt_t from_string(std::string_view) = delete;
452};
453
454
455template<> struct string_traits<std::monostate>
456{
457 static constexpr bool converts_to_string{false};
458 static constexpr bool converts_from_string{false};
459
460 static char *into_buf(char *, char *, std::monostate) = delete;
461
462 [[deprecated("Do not convert nulls.")]] static constexpr zview
463 to_buf(char *, char *, std::monostate const &) noexcept
464 {
465 return {};
466 }
467
468 [[deprecated("Do not convert nulls.")]] static constexpr std::size_t
469 size_buffer(std::monostate) noexcept
470 {
471 return 0;
472 }
473 [[deprecated("Do not convert nulls.")]] static std::monostate
474 from_string(std::string_view) = delete;
475};
476
477
478template<> inline constexpr bool is_unquoted_safe<std::nullptr_t>{true};
479
480
481template<> struct nullness<char const *>
482{
483 static constexpr bool has_null = true;
484 static constexpr bool always_null = false;
485 static constexpr bool is_null(char const *t) noexcept
486 {
487 return t == nullptr;
488 }
489 static constexpr char const *null() noexcept { return nullptr; }
490};
491
492
494template<> struct string_traits<char const *>
495{
496 static constexpr bool converts_to_string{true};
497 static constexpr bool converts_from_string{true};
498
499 static char const *from_string(std::string_view text) { return text.data(); }
500
501 static zview to_buf(char *begin, char *end, char const *const &value)
502 {
503 return generic_to_buf(begin, end, value);
504 }
505
506 static char *into_buf(char *begin, char *end, char const *const &value)
507 {
508 auto const space{end - begin};
509 // Count the trailing zero, even though std::strlen() and friends don't.
510 auto const len{std::strlen(value) + 1};
511 if (space < ptrdiff_t(len))
512 throw conversion_overrun{
513 "Could not copy string: buffer too small. " +
515 std::memmove(begin, value, len);
516 return begin + len;
517 }
518
519 static std::size_t size_buffer(char const *const &value) noexcept
520 {
521 if (pqxx::is_null(value))
522 return 0;
523 else
524 return std::strlen(value) + 1;
525 }
526};
527
528
529template<> struct nullness<char *>
530{
531 static constexpr bool has_null = true;
532 static constexpr bool always_null = false;
533 static constexpr bool is_null(char const *t) noexcept
534 {
535 return t == nullptr;
536 }
537 static constexpr char const *null() { return nullptr; }
538};
539
540
542template<> struct string_traits<char *>
543{
544 static constexpr bool converts_to_string{true};
545 static constexpr bool converts_from_string{false};
546
547 static char *into_buf(char *begin, char *end, char *const &value)
548 {
549 return string_traits<char const *>::into_buf(begin, end, value);
550 }
551 static zview to_buf(char *begin, char *end, char *const &value)
552 {
553 return string_traits<char const *>::to_buf(begin, end, value);
554 }
555 static std::size_t size_buffer(char *const &value) noexcept
556 {
557 if (pqxx::is_null(value))
558 return 0;
559 else
561 }
562
564 static char *from_string(std::string_view) = delete;
565};
566
567
568template<std::size_t N> struct nullness<char[N]> : no_null<char[N]>
569{};
570
571
573
576template<std::size_t N> struct string_traits<char[N]>
577{
578 static constexpr bool converts_to_string{true};
579 static constexpr bool converts_from_string{false};
580
581 static constexpr zview
582 to_buf(char *, char *, char const (&value)[N]) noexcept
583 {
584 return zview{value, N - 1};
585 }
586
587 static char *into_buf(char *begin, char *end, char const (&value)[N])
588 {
589 if (internal::cmp_less(end - begin, size_buffer(value)))
590 throw conversion_overrun{
591 "Could not convert char[] to string: too long for buffer."};
592 std::memcpy(begin, value, N);
593 return begin + N;
594 }
595 static constexpr std::size_t size_buffer(char const (&)[N]) noexcept
596 {
597 return N;
598 }
599
601 static void from_string(std::string_view) = delete;
602};
603
604
605template<> struct nullness<std::string> : no_null<std::string>
606{};
607
608
609template<> struct string_traits<std::string>
610{
611 static constexpr bool converts_to_string{true};
612 static constexpr bool converts_from_string{true};
613
614 static std::string from_string(std::string_view text)
615 {
616 return std::string{text};
617 }
618
619 static char *into_buf(char *begin, char *end, std::string const &value)
620 {
621 if (internal::cmp_greater_equal(std::size(value), end - begin))
622 throw conversion_overrun{
623 "Could not convert string to string: too long for buffer."};
624 // Include the trailing zero.
625 value.copy(begin, std::size(value));
626 begin[std::size(value)] = '\0';
627 return begin + std::size(value) + 1;
628 }
629
630 static zview to_buf(char *begin, char *end, std::string const &value)
631 {
632 return generic_to_buf(begin, end, value);
633 }
634
635 static std::size_t size_buffer(std::string const &value) noexcept
636 {
637 return std::size(value) + 1;
638 }
639};
640
641
643
646template<> struct nullness<std::string_view> : no_null<std::string_view>
647{};
648
649
651template<> struct string_traits<std::string_view>
652{
653 static constexpr bool converts_to_string{true};
654 static constexpr bool converts_from_string{false};
655
656 static constexpr std::size_t
657 size_buffer(std::string_view const &value) noexcept
658 {
659 return std::size(value) + 1;
660 }
661
662 static char *into_buf(char *begin, char *end, std::string_view const &value)
663 {
664 if (internal::cmp_greater_equal(std::size(value), end - begin))
665 throw conversion_overrun{
666 "Could not store string_view: too long for buffer."};
667 value.copy(begin, std::size(value));
668 begin[std::size(value)] = '\0';
669 return begin + std::size(value) + 1;
670 }
671
672 static zview to_buf(char *begin, char *end, std::string_view const &value)
673 {
674 // You'd think we could just return the same view but alas, there's no
675 // zero-termination on a string_view.
676 return generic_to_buf(begin, end, value);
677 }
678
680 static std::string_view from_string(std::string_view) = delete;
681};
682
683
684template<> struct nullness<zview> : no_null<zview>
685{};
686
687
689template<> struct string_traits<zview>
690{
691 static constexpr bool converts_to_string{true};
692 static constexpr bool converts_from_string{false};
693
694 static constexpr std::size_t
695 size_buffer(std::string_view const &value) noexcept
696 {
697 return std::size(value) + 1;
698 }
699
700 static char *into_buf(char *begin, char *end, zview const &value)
701 {
702 auto const size{std::size(value)};
703 if (internal::cmp_less_equal(end - begin, std::size(value)))
704 throw conversion_overrun{"Not enough buffer space to store this zview."};
705 value.copy(begin, size);
706 begin[size] = '\0';
707 return begin + size + 1;
708 }
709
710 static std::string_view to_buf(char *begin, char *end, zview const &value)
711 {
712 char *const stop{into_buf(begin, end, value)};
713 return {begin, static_cast<std::size_t>(stop - begin - 1)};
714 }
715
717 static zview from_string(std::string_view) = delete;
718};
719
720
721template<> struct nullness<std::stringstream> : no_null<std::stringstream>
722{};
723
724
725template<> struct string_traits<std::stringstream>
726{
727 static constexpr bool converts_to_string{false};
728 static constexpr bool converts_from_string{true};
729
730 static std::size_t size_buffer(std::stringstream const &) = delete;
731
732 static std::stringstream from_string(std::string_view text)
733 {
734 std::stringstream stream;
735 stream.write(text.data(), std::streamsize(std::size(text)));
736 return stream;
737 }
738
739 static char *into_buf(char *, char *, std::stringstream const &) = delete;
740 static std::string_view
741 to_buf(char *, char *, std::stringstream const &) = delete;
742};
743
744
745template<> struct nullness<std::nullptr_t>
746{
747 static constexpr bool has_null = true;
748 static constexpr bool always_null = true;
749 static constexpr bool is_null(std::nullptr_t const &) noexcept
750 {
751 return true;
752 }
753 static constexpr std::nullptr_t null() noexcept { return nullptr; }
754};
755
756
757template<> struct nullness<std::nullopt_t>
758{
759 static constexpr bool has_null = true;
760 static constexpr bool always_null = true;
761 static constexpr bool is_null(std::nullopt_t const &) noexcept
762 {
763 return true;
764 }
765 static constexpr std::nullopt_t null() noexcept { return std::nullopt; }
766};
767
768
769template<> struct nullness<std::monostate>
770{
771 static constexpr bool has_null = true;
772 static constexpr bool always_null = true;
773 static constexpr bool is_null(std::monostate const &) noexcept
774 {
775 return true;
776 }
777 static constexpr std::monostate null() noexcept { return {}; }
778};
779
780
781template<typename T> struct nullness<std::unique_ptr<T>>
782{
783 static constexpr bool has_null = true;
784 static constexpr bool always_null = false;
785 static constexpr bool is_null(std::unique_ptr<T> const &t) noexcept
786 {
787 return not t or pqxx::is_null(*t);
788 }
789 static constexpr std::unique_ptr<T> null() { return {}; }
790};
791
792
793template<typename T, typename... Args>
794struct string_traits<std::unique_ptr<T, Args...>>
795{
796 static constexpr bool converts_to_string{
798 static constexpr bool converts_from_string{
800
801 static std::unique_ptr<T> from_string(std::string_view text)
802 {
803 return std::make_unique<T>(string_traits<T>::from_string(text));
804 }
805
806 static char *
807 into_buf(char *begin, char *end, std::unique_ptr<T, Args...> const &value)
808 {
809 return string_traits<T>::into_buf(begin, end, *value);
810 }
811
812 static zview
813 to_buf(char *begin, char *end, std::unique_ptr<T, Args...> const &value)
814 {
815 if (value)
816 return string_traits<T>::to_buf(begin, end, *value);
817 else
818 return {};
819 }
820
821 static std::size_t
822 size_buffer(std::unique_ptr<T, Args...> const &value) noexcept
823 {
824 if (pqxx::is_null(value))
825 return 0;
826 else
827 return pqxx::size_buffer(*value.get());
828 }
829};
830
831
832template<typename T, typename... Args>
833inline format param_format(std::unique_ptr<T, Args...> const &value)
834{
835 return param_format(*value);
836}
837
838
839template<typename T, typename... Args>
840inline constexpr bool is_unquoted_safe<std::unique_ptr<T, Args...>>{
842
843
844template<typename T> struct nullness<std::shared_ptr<T>>
845{
846 static constexpr bool has_null = true;
847 static constexpr bool always_null = false;
848 static constexpr bool is_null(std::shared_ptr<T> const &t) noexcept
849 {
850 return not t or pqxx::is_null(*t);
851 }
852 static constexpr std::shared_ptr<T> null() { return {}; }
853};
854
855
856template<typename T> struct string_traits<std::shared_ptr<T>>
857{
858 static constexpr bool converts_to_string{
860 static constexpr bool converts_from_string{
862
863 static std::shared_ptr<T> from_string(std::string_view text)
864 {
865 return std::make_shared<T>(string_traits<T>::from_string(text));
866 }
867
868 static zview to_buf(char *begin, char *end, std::shared_ptr<T> const &value)
869 {
870 return string_traits<T>::to_buf(begin, end, *value);
871 }
872 static char *
873 into_buf(char *begin, char *end, std::shared_ptr<T> const &value)
874 {
875 return string_traits<T>::into_buf(begin, end, *value);
876 }
877 static std::size_t size_buffer(std::shared_ptr<T> const &value) noexcept
878 {
879 if (pqxx::is_null(value))
880 return 0;
881 else
882 return pqxx::size_buffer(*value);
883 }
884};
885
886
887template<typename T> format param_format(std::shared_ptr<T> const &value)
888{
889 return param_format(*value);
890}
891
892
893template<typename T>
894inline constexpr bool is_unquoted_safe<std::shared_ptr<T>>{
896
897
898template<> struct nullness<bytes> : no_null<bytes>
899{};
900
901
902#if defined(PQXX_HAVE_CONCEPTS)
903template<binary DATA> struct nullness<DATA> : no_null<DATA>
904{};
905
906
907template<binary DATA> inline constexpr format param_format(DATA const &)
908{
909 return format::binary;
910}
911
912
913template<binary DATA> struct string_traits<DATA>
914{
915 static constexpr bool converts_to_string{true};
916 static constexpr bool converts_from_string{true};
917
918 static std::size_t size_buffer(DATA const &value) noexcept
919 {
920 return internal::size_esc_bin(std::size(value));
921 }
922
923 static zview to_buf(char *begin, char *end, DATA const &value)
924 {
925 return generic_to_buf(begin, end, value);
926 }
927
928 static char *into_buf(char *begin, char *end, DATA const &value)
929 {
930 auto const budget{size_buffer(value)};
931 if (internal::cmp_less(end - begin, budget))
932 throw conversion_overrun{
933 "Not enough buffer space to escape binary data."};
934 internal::esc_bin(value, begin);
935 return begin + budget;
936 }
937
938 static DATA from_string(std::string_view text)
939 {
940 auto const size{pqxx::internal::size_unesc_bin(std::size(text))};
941 bytes buf;
942 buf.resize(size);
943 pqxx::internal::unesc_bin(text, reinterpret_cast<std::byte *>(buf.data()));
944 return buf;
945 }
946};
947#endif // PQXX_HAVE_CONCEPTS
948
949
950template<> struct string_traits<bytes>
951{
952 static constexpr bool converts_to_string{true};
953 static constexpr bool converts_from_string{true};
954
955 static std::size_t size_buffer(bytes const &value) noexcept
956 {
957 return internal::size_esc_bin(std::size(value));
958 }
959
960 static zview to_buf(char *begin, char *end, bytes const &value)
961 {
962 return generic_to_buf(begin, end, value);
963 }
964
965 static char *into_buf(char *begin, char *end, bytes const &value)
966 {
967 auto const budget{size_buffer(value)};
968 if (internal::cmp_less(end - begin, budget))
969 throw conversion_overrun{
970 "Not enough buffer space to escape binary data."};
971 internal::esc_bin(value, begin);
972 return begin + budget;
973 }
974
975 static bytes from_string(std::string_view text)
976 {
977 auto const size{pqxx::internal::size_unesc_bin(std::size(text))};
978 bytes buf;
979 buf.resize(size);
980 pqxx::internal::unesc_bin(text, reinterpret_cast<std::byte *>(buf.data()));
981 return buf;
982 }
983};
984
985
986template<> inline constexpr format param_format(bytes const &)
987{
988 return format::binary;
989}
990
991
992template<> struct nullness<bytes_view> : no_null<bytes_view>
993{};
994
995
996template<> struct string_traits<bytes_view>
997{
998 static constexpr bool converts_to_string{true};
999 static constexpr bool converts_from_string{false};
1000
1001 static std::size_t size_buffer(bytes_view const &value) noexcept
1002 {
1003 return internal::size_esc_bin(std::size(value));
1004 }
1005
1006 static zview to_buf(char *begin, char *end, bytes_view const &value)
1007 {
1008 return generic_to_buf(begin, end, value);
1009 }
1010
1011 static char *into_buf(char *begin, char *end, bytes_view const &value)
1012 {
1013 auto const budget{size_buffer(value)};
1014 if (internal::cmp_less(end - begin, budget))
1015 throw conversion_overrun{
1016 "Not enough buffer space to escape binary data."};
1017 internal::esc_bin(value, begin);
1018 return begin + budget;
1019 }
1020
1021 // There's no from_string, because there's nobody to hold the data.
1022};
1023
1024template<> inline constexpr format param_format(bytes_view const &)
1025{
1026 return format::binary;
1027}
1028} // namespace pqxx
1029
1030
1031namespace pqxx::internal
1032{
1033// C++20: Use concepts to identify arrays.
1035template<typename Container> struct array_string_traits
1036{
1037private:
1038 using elt_type = strip_t<value_type<Container>>;
1040 static constexpr zview s_null{"NULL"};
1041
1042public:
1043 static constexpr bool converts_to_string{true};
1044 static constexpr bool converts_from_string{false};
1045
1046 static zview to_buf(char *begin, char *end, Container const &value)
1047 {
1048 return generic_to_buf(begin, end, value);
1049 }
1050
1051 static char *into_buf(char *begin, char *end, Container const &value)
1052 {
1053 assert(begin <= end);
1054 std::size_t const budget{size_buffer(value)};
1055 if (internal::cmp_less(end - begin, budget))
1056 throw conversion_overrun{
1057 "Not enough buffer space to convert array to string."};
1058
1059 char *here = begin;
1060 *here++ = '{';
1061
1062 bool nonempty{false};
1063 for (auto const &elt : value)
1064 {
1065 if (is_null(elt))
1066 {
1067 s_null.copy(here, std::size(s_null));
1068 here += std::size(s_null);
1069 }
1070 else if constexpr (is_sql_array<elt_type>)
1071 {
1072 // Render nested array in-place. Then erase the trailing zero.
1073 here = elt_traits::into_buf(here, end, elt) - 1;
1074 }
1075 else if constexpr (is_unquoted_safe<elt_type>)
1076 {
1077 // No need to quote or escape. Just convert the value straight into
1078 // its place in the array, and "backspace" the trailing zero.
1079 here = elt_traits::into_buf(here, end, elt) - 1;
1080 }
1081 else
1082 {
1083 *here++ = '"';
1084
1085 // Use the tail end of the destination buffer as an intermediate
1086 // buffer.
1087 auto const elt_budget{pqxx::size_buffer(elt)};
1088 assert(elt_budget < static_cast<std::size_t>(end - here));
1089 for (char const c : elt_traits::to_buf(end - elt_budget, end, elt))
1090 {
1091 // We copy the intermediate buffer into the final buffer, char by
1092 // char, with escaping where necessary.
1093 // TODO: This will not work for all encodings. UTF8 & ASCII are OK.
1094 if (c == '\\' or c == '"')
1095 *here++ = '\\';
1096 *here++ = c;
1097 }
1098 *here++ = '"';
1099 }
1101 nonempty = true;
1102 }
1103
1104 // Erase that last comma, if present.
1105 if (nonempty)
1106 here--;
1107
1108 *here++ = '}';
1109 *here++ = '\0';
1110
1111 return here;
1112 }
1113
1114 static std::size_t size_buffer(Container const &value) noexcept
1115 {
1116 if constexpr (is_unquoted_safe<elt_type>)
1117 return 3 + std::accumulate(
1118 std::begin(value), std::end(value), std::size_t{},
1119 [](std::size_t acc, elt_type const &elt) {
1120 // Budget for each element includes a terminating zero.
1121 // We won't actually be wanting those, but don't subtract
1122 // that one byte: we want room for a separator instead.
1123 // However, std::size(s_null) doesn't account for the
1124 // terminating zero, so add one to make s_null pay for its
1125 // own separator.
1126 return acc + (pqxx::is_null(elt) ?
1127 (std::size(s_null) + 1) :
1129 });
1130 else
1131 return 3 + std::accumulate(
1132 std::begin(value), std::end(value), std::size_t{},
1133 [](std::size_t acc, elt_type const &elt) {
1134 // Opening and closing quotes, plus worst-case escaping,
1135 // and the one byte for the trailing zero becomes room
1136 // for a separator. However, std::size(s_null) doesn't
1137 // account for the terminating zero, so add one to make
1138 // s_null pay for its own separator.
1139 std::size_t const elt_size{
1140 pqxx::is_null(elt) ? (std::size(s_null) + 1) :
1142 return acc + 2 * elt_size + 2;
1143 });
1144 }
1145
1146 // We don't yet support parsing of array types using from_string. Doing so
1147 // would require a reference to the connection.
1148};
1149} // namespace pqxx::internal
1150
1151
1152namespace pqxx
1153{
1154template<typename T, typename... Args>
1155struct nullness<std::vector<T, Args...>> : no_null<std::vector<T>>
1156{};
1157
1158
1159template<typename T, typename... Args>
1160struct string_traits<std::vector<T, Args...>>
1161 : internal::array_string_traits<std::vector<T, Args...>>
1162{};
1163
1164
1166template<typename T, typename... Args>
1167inline constexpr format param_format(std::vector<T, Args...> const &)
1168{
1169 return format::text;
1170}
1171
1172
1174template<typename... Args>
1175inline constexpr format param_format(std::vector<std::byte, Args...> const &)
1176{
1177 return format::binary;
1178}
1179
1180
1181template<typename T> inline constexpr bool is_sql_array<std::vector<T>>{true};
1182
1183
1184#if defined(PQXX_HAVE_SPAN) && __has_include(<span>)
1185template<typename T, size_t Extent>
1186struct nullness<std::span<T, Extent>> : no_null<std::span<T, Extent>>
1187{};
1188
1189
1190template<typename T, size_t Extent>
1191struct string_traits<std::span<T, Extent>>
1192 : internal::array_string_traits<std::span<T, Extent>>
1193{};
1194
1195
1196template<typename T, size_t Extent>
1197inline constexpr format param_format(std::span<T, Extent> const &)
1198{
1199 return format::text;
1200}
1201
1202
1203template<size_t Extent>
1204inline constexpr format param_format(std::span<std::byte, Extent> const &)
1205{
1206 return format::binary;
1207}
1208
1209
1210template<typename T, size_t Extent>
1211inline constexpr bool is_sql_array<std::span<T, Extent>>{true};
1212#endif
1213
1214
1215template<typename T, std::size_t N>
1216struct nullness<std::array<T, N>> : no_null<std::array<T, N>>
1217{};
1218
1219
1220template<typename T, std::size_t N>
1221struct string_traits<std::array<T, N>>
1222 : internal::array_string_traits<std::array<T, N>>
1223{};
1224
1225
1227template<typename T, typename... Args, Args... args>
1228inline constexpr format param_format(std::array<T, args...> const &)
1229{
1230 return format::text;
1231}
1232
1233
1235template<typename... Args, Args... args>
1236inline constexpr format param_format(std::array<std::byte, args...> const &)
1237{
1238 return format::binary;
1239}
1240
1241
1242template<typename T, std::size_t N>
1243inline constexpr bool is_sql_array<std::array<T, N>>{true};
1244} // namespace pqxx
1245
1246
1247namespace pqxx
1248{
1249template<typename T> inline std::string to_string(T const &value)
1250{
1251 if (is_null(value))
1252 throw conversion_error{
1253 "Attempt to convert null " + std::string{type_name<T>} +
1254 " to a string."};
1255
1256 std::string buf;
1257 // We can't just reserve() space; modifying the terminating zero leads to
1258 // undefined behaviour.
1259 buf.resize(size_buffer(value));
1260 auto const data{buf.data()};
1261 auto const end{
1262 string_traits<T>::into_buf(data, data + std::size(buf), value)};
1263 buf.resize(static_cast<std::size_t>(end - data - 1));
1264 return buf;
1265}
1266
1267
1268template<> inline std::string to_string(float const &value)
1269{
1270 return internal::to_string_float(value);
1271}
1272template<> inline std::string to_string(double const &value)
1273{
1274 return internal::to_string_float(value);
1275}
1276template<> inline std::string to_string(long double const &value)
1277{
1278 return internal::to_string_float(value);
1279}
1280template<> inline std::string to_string(std::stringstream const &value)
1281{
1282 return value.str();
1283}
1284
1285
1286template<typename T> inline void into_string(T const &value, std::string &out)
1287{
1288 if (is_null(value))
1289 throw conversion_error{
1290 "Attempt to convert null " + type_name<T> + " to a string."};
1291
1292 // We can't just reserve() data; modifying the terminating zero leads to
1293 // undefined behaviour.
1294 out.resize(size_buffer(value) + 1);
1295 auto const data{out.data()};
1296 auto const end{
1297 string_traits<T>::into_buf(data, data + std::size(out), value)};
1298 out.resize(static_cast<std::size_t>(end - data - 1));
1299}
1300} // 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:264
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:677
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:276
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:493
constexpr char array_separator
Element separator between SQL array elements of this type.
Definition strconv.hxx:559
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:526
std::remove_cv_t< std::remove_reference_t< TYPE > > strip_t
Remove any constness, volatile, and reference-ness from a type.
Definition types.hxx:80
constexpr bool is_null(TYPE const &value) noexcept
Is value null?
Definition strconv.hxx:515
zview generic_to_buf(char *begin, char *end, TYPE const &value)
Implement string_traits<TYPE>::to_buf by calling into_buf.
Definition strconv.hxx:587
T from_string(field const &value)
Convert a field's value to type T.
Definition field.hxx:548
constexpr bool is_unquoted_safe
Can we use this type in arrays and composite types without quoting them?
Definition strconv.hxx:555
format
Format code: is data text or binary?
Definition types.hxx:70
String traits for SQL arrays.
Definition conversions.hxx:1036
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:609
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:154
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:159
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:165