libpqxx
The C++ client library for PostgreSQL
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) && defined(PQXX_HAVE_RANGES)
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  */
24 namespace pqxx::internal
25 {
27 inline constexpr char number_to_digit(int i) noexcept
28 {
29  return static_cast<char>(i + '0');
30 }
31 
32 
34 constexpr int digit_to_number(char c) noexcept
35 {
36  return c - '0';
37 }
38 
39 
41 
44 std::string PQXX_LIBEXPORT
45 state_buffer_overrun(int have_bytes, int need_bytes);
46 
47 
48 template<typename HAVE, typename NEED>
49 inline std::string state_buffer_overrun(HAVE have_bytes, NEED need_bytes)
50 {
51  return state_buffer_overrun(
52  static_cast<int>(have_bytes), static_cast<int>(need_bytes));
53 }
54 
55 
57 [[noreturn]] PQXX_LIBEXPORT PQXX_COLD void
58 throw_null_conversion(std::string const &type);
59 
60 
62 [[noreturn]] PQXX_LIBEXPORT PQXX_COLD void
63 throw_null_conversion(std::string_view type);
64 
65 
67 
76 template<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 
90 template<typename T> PQXX_LIBEXPORT extern std::string to_string_float(T);
91 
92 
94 template<typename T>
95 inline 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;
101  if (internal::cmp_greater(len, space))
102  throw conversion_overrun{
103  "Not enough buffer space to insert " + type_name<T> + ". " +
104  state_buffer_overrun(space, len)};
105  std::memmove(begin, text.data(), len);
106  return begin + len;
107 }
108 
109 
110 // C++20: Guard with concept?
112 template<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?
133 template<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 
195 namespace pqxx
196 {
198 
201 template<typename T>
202 struct nullness<T, std::enable_if_t<std::is_arithmetic_v<T>>> : no_null<T>
203 {};
204 
205 
206 template<> struct string_traits<short> : internal::integral_traits<short>
207 {};
208 template<> inline constexpr bool is_unquoted_safe<short>{true};
209 template<>
210 struct string_traits<unsigned short>
211  : internal::integral_traits<unsigned short>
212 {};
213 template<> inline constexpr bool is_unquoted_safe<unsigned short>{true};
214 template<> struct string_traits<int> : internal::integral_traits<int>
215 {};
216 template<> inline constexpr bool is_unquoted_safe<int>{true};
217 template<> struct string_traits<unsigned> : internal::integral_traits<unsigned>
218 {};
219 template<> inline constexpr bool is_unquoted_safe<unsigned>{true};
220 template<> struct string_traits<long> : internal::integral_traits<long>
221 {};
222 template<> inline constexpr bool is_unquoted_safe<long>{true};
223 template<>
224 struct string_traits<unsigned long> : internal::integral_traits<unsigned long>
225 {};
226 template<> inline constexpr bool is_unquoted_safe<unsigned long>{true};
227 template<>
228 struct string_traits<long long> : internal::integral_traits<long long>
229 {};
230 template<> inline constexpr bool is_unquoted_safe<long long>{true};
231 template<>
232 struct string_traits<unsigned long long>
233  : internal::integral_traits<unsigned long long>
234 {};
235 template<> inline constexpr bool is_unquoted_safe<unsigned long long>{true};
236 template<> struct string_traits<float> : internal::float_traits<float>
237 {};
238 template<> inline constexpr bool is_unquoted_safe<float>{true};
239 template<> struct string_traits<double> : internal::float_traits<double>
240 {};
241 template<> inline constexpr bool is_unquoted_safe<double>{true};
242 template<>
243 struct string_traits<long double> : internal::float_traits<long double>
244 {};
245 template<> inline constexpr bool is_unquoted_safe<long double>{true};
246 
247 
248 template<> 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 
269 template<> inline constexpr bool is_unquoted_safe<bool>{true};
270 
271 
272 template<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 
285 template<typename T>
286 inline constexpr format param_format(std::optional<T> const &value)
287 {
288  return param_format(*value);
289 }
290 
291 
292 template<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 
328 template<typename T>
329 inline constexpr bool is_unquoted_safe<std::optional<T>>{is_unquoted_safe<T>};
330 
331 
332 template<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 {
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 
354 template<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 
393 template<typename... Args>
394 inline constexpr format param_format(std::variant<Args...> const &value)
395 {
396  return std::visit([](auto &v) { return param_format(v); }, value);
397 }
398 
399 
400 template<typename... T>
401 inline constexpr bool is_unquoted_safe<std::variant<T...>>{
402  (is_unquoted_safe<T> and ...)};
403 
404 
405 template<typename T> inline T from_string(std::stringstream const &text)
406 {
407  return from_string<T>(text.str());
408 }
409 
410 
411 template<> 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 
433 template<> 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 
455 template<> 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 
478 template<> inline constexpr bool is_unquoted_safe<std::nullptr_t>{true};
479 
480 
481 template<> 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 
494 template<> 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 
529 template<> 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 
542 template<> 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 
568 template<std::size_t N> struct nullness<char[N]> : no_null<char[N]>
569 {};
570 
571 
573 
576 template<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 
605 template<> struct nullness<std::string> : no_null<std::string>
606 {};
607 
608 
609 template<> 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 
646 template<> struct nullness<std::string_view> : no_null<std::string_view>
647 {};
648 
649 
651 template<> 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 
684 template<> struct nullness<zview> : no_null<zview>
685 {};
686 
687 
689 template<> 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 
721 template<> struct nullness<std::stringstream> : no_null<std::stringstream>
722 {};
723 
724 
725 template<> 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 
745 template<> 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 
757 template<> 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 
769 template<> 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 
781 template<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 
793 template<typename T, typename... Args>
794 struct 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 
832 template<typename T, typename... Args>
833 inline format param_format(std::unique_ptr<T, Args...> const &value)
834 {
835  return param_format(*value);
836 }
837 
838 
839 template<typename T, typename... Args>
840 inline constexpr bool is_unquoted_safe<std::unique_ptr<T, Args...>>{
841  is_unquoted_safe<T>};
842 
843 
844 template<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 
856 template<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 
887 template<typename T> format param_format(std::shared_ptr<T> const &value)
888 {
889  return param_format(*value);
890 }
891 
892 
893 template<typename T>
894 inline constexpr bool is_unquoted_safe<std::shared_ptr<T>>{
895  is_unquoted_safe<T>};
896 
897 
898 template<> struct nullness<bytes> : no_null<bytes>
899 {};
900 
901 
902 #if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_RANGES)
903 template<binary DATA> struct nullness<DATA> : no_null<DATA>
904 {};
905 
906 
907 template<binary DATA> inline constexpr format param_format(DATA const &)
908 {
909  return format::binary;
910 }
911 
912 
913 template<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 
950 template<> 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 
986 template<> inline constexpr format param_format(bytes const &)
987 {
988  return format::binary;
989 }
990 
991 
992 template<> struct nullness<bytes_view> : no_null<bytes_view>
993 {};
994 
995 
996 template<> 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 
1024 template<> inline constexpr format param_format(bytes_view const &)
1025 {
1026  return format::binary;
1027 }
1028 } // namespace pqxx
1029 
1030 
1031 namespace pqxx::internal
1032 {
1033 // C++20: Use concepts to identify arrays.
1035 template<typename Container> struct array_string_traits
1036 {
1037 private:
1038  using elt_type = strip_t<value_type<Container>>;
1040  static constexpr zview s_null{"NULL"};
1041 
1042 public:
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  }
1100  *here++ = array_separator<elt_type>;
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 
1152 namespace pqxx
1153 {
1154 template<typename T, typename... Args>
1155 struct nullness<std::vector<T, Args...>> : no_null<std::vector<T>>
1156 {};
1157 
1158 
1159 template<typename T, typename... Args>
1160 struct string_traits<std::vector<T, Args...>>
1161  : internal::array_string_traits<std::vector<T, Args...>>
1162 {};
1163 
1164 
1166 template<typename T, typename... Args>
1167 inline constexpr format param_format(std::vector<T, Args...> const &)
1168 {
1169  return format::text;
1170 }
1171 
1172 
1174 template<typename... Args>
1175 inline constexpr format param_format(std::vector<std::byte, Args...> const &)
1176 {
1177  return format::binary;
1178 }
1179 
1180 
1181 template<typename T> inline constexpr bool is_sql_array<std::vector<T>>{true};
1182 
1183 
1184 #if defined(PQXX_HAVE_SPAN) && __has_include(<span>)
1185 template<typename T, size_t Extent>
1186 struct nullness<std::span<T, Extent>> : no_null<std::span<T, Extent>>
1187 {};
1188 
1189 
1190 template<typename T, size_t Extent>
1191 struct string_traits<std::span<T, Extent>>
1192  : internal::array_string_traits<std::span<T, Extent>>
1193 {};
1194 
1195 
1196 template<typename T, size_t Extent>
1197 inline constexpr format param_format(std::span<T, Extent> const &)
1198 {
1199  return format::text;
1200 }
1201 
1202 
1203 template<size_t Extent>
1204 inline constexpr format param_format(std::span<std::byte, Extent> const &)
1205 {
1206  return format::binary;
1207 }
1208 
1209 
1210 template<typename T, size_t Extent>
1211 inline constexpr bool is_sql_array<std::span<T, Extent>>{true};
1212 #endif
1213 
1214 
1215 template<typename T, std::size_t N>
1216 struct nullness<std::array<T, N>> : no_null<std::array<T, N>>
1217 {};
1218 
1219 
1220 template<typename T, std::size_t N>
1221 struct string_traits<std::array<T, N>>
1222  : internal::array_string_traits<std::array<T, N>>
1223 {};
1224 
1225 
1227 template<typename T, typename... Args, Args... args>
1228 inline constexpr format param_format(std::array<T, args...> const &)
1229 {
1230  return format::text;
1231 }
1232 
1233 
1235 template<typename... Args, Args... args>
1236 inline constexpr format param_format(std::array<std::byte, args...> const &)
1237 {
1238  return format::binary;
1239 }
1240 
1241 
1242 template<typename T, std::size_t N>
1243 inline constexpr bool is_sql_array<std::array<T, N>>{true};
1244 } // namespace pqxx
1245 
1246 
1247 namespace pqxx
1248 {
1249 template<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 
1268 template<> inline std::string to_string(float const &value)
1269 {
1270  return internal::to_string_float(value);
1271 }
1272 template<> inline std::string to_string(double const &value)
1273 {
1274  return internal::to_string_float(value);
1275 }
1276 template<> inline std::string to_string(long double const &value)
1277 {
1278  return internal::to_string_float(value);
1279 }
1280 template<> inline std::string to_string(std::stringstream const &value)
1281 {
1282  return value.str();
1283 }
1284 
1285 
1286 template<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
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:601
Marker-type wrapper: zero-terminated std::string_view.
Definition: zview.hxx:37
std::string to_string_float(T value)
Floating-point implementations for pqxx::to_string().
Definition: strconv.cxx:670
String traits for builtin floating-point types.
Definition: conversions.hxx:133
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
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
void PQXX_LIBEXPORT esc_bin(bytes_view binary_data, char buffer[]) noexcept
Hex-escape binary data into a buffer.
Definition: util.cxx:133
static zview to_buf(char *begin, char *end, TYPE const &value)
Return a string_view representing value, plus terminating zero.
Internal items for libpqxx' own use. Do not use these yourself.
Definition: encodings.cxx:32
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
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
T from_string(field const &value)
Convert a field's value to type T.
Definition: field.hxx:548
Deliberately nonfunctional conversion traits for char types.
Definition: conversions.hxx:76
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
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
static TYPE from_string(std::string_view text)
Parse a string representation of a TYPE value.
static bool has_null
Does this type have a null value?
Definition: strconv.hxx:93
PQXX_LIBEXPORT std::string to_string(field const &value)
Convert a field to a string.
format
Format code: is data text or binary?
Definition: types.hxx:69
constexpr bool is_null(TYPE const &value) noexcept
Is value null?
Definition: strconv.hxx:515
constexpr bool is_unquoted_safe
Can we use this type in arrays and composite types without quoting them?
Definition: strconv.hxx:555
String traits for builtin integral types (though not bool).
Definition: conversions.hxx:112
Could not convert value to string: not enough buffer space.
Definition: except.hxx:312
void PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data, std::byte buffer[])
Reconstitute binary data from its escaped version.
Definition: util.cxx:165
constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater, or workaround if not available.
Definition: util.hxx:87
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
static constexpr bool converts_from_string
Is conversion from string_view to TYPE supported?
Definition: strconv.hxx:165
static TYPE null()
Return a null value.
The home of all libpqxx classes, functions, templates, etc.
Definition: array.cxx:26
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:375
std::string state_buffer_overrun(int have_bytes, int need_bytes)
Summarize buffer overrun.
Definition: strconv.cxx:268
zview generic_to_buf(char *begin, char *end, TYPE const &value)
Implement string_traits::to_buf by calling into_buf.
Definition: strconv.hxx:587
Nullness traits describing a type which does not have a null value.
Definition: strconv.hxx:112
Traits class for use in string conversions.
Definition: strconv.hxx:153
String traits for SQL arrays.
Definition: conversions.hxx:1035
static constexpr bool converts_to_string
Is conversion from TYPE to strings supported?
Definition: strconv.hxx:159
static bool always_null
Is this type always null?
Definition: strconv.hxx:96
Traits describing a type's "null value," if any.
Definition: strconv.hxx:90
void throw_null_conversion(std::string const &type)
Throw exception for attempt to convert SQL NULL to given type.
Definition: strconv.cxx:256
static char * into_buf(char *begin, char *end, TYPE const &value)
Write value's string representation into buffer at begin.
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:385
static bool is_null(TYPE const &value)
Is value a null?
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 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 char number_to_digit(int i) noexcept
Convert a number in [0, 9] to its ASCII digit.
Definition: conversions.hxx:27
static std::size_t size_buffer(TYPE const &value) noexcept
Estimate how much buffer space is needed to represent value.
An SQL array received from the database.
Definition: array.hxx:55
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