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) && __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  */
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 (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 
325 template<typename T>
326 inline constexpr bool is_unquoted_safe<std::optional<T>>{is_unquoted_safe<T>};
327 
328 
329 template<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 
350 template<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 
386 template<typename... Args>
387 inline constexpr format param_format(std::variant<Args...> const &value)
388 {
389  return std::visit([](auto &v) { return param_format(v); }, value);
390 }
391 
392 
393 template<typename... T>
394 inline constexpr bool is_unquoted_safe<std::variant<T...>>{
395  (is_unquoted_safe<T> and ...)};
396 
397 
398 template<typename T> inline T from_string(std::stringstream const &text)
399 {
400  return from_string<T>(text.str());
401 }
402 
403 
404 template<> 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 
426 template<> 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 
448 template<> 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 
471 template<> inline constexpr bool is_unquoted_safe<std::nullptr_t>{true};
472 
473 
474 template<> 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 
487 template<> 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 
519 template<> 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 
532 template<> 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 
555 template<std::size_t N> struct nullness<char[N]> : no_null<char[N]>
556 {};
557 
558 
560 
563 template<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 
592 template<> struct nullness<std::string> : no_null<std::string>
593 {};
594 
595 
596 template<> 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 
633 template<> struct nullness<std::string_view> : no_null<std::string_view>
634 {};
635 
636 
638 template<> 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 
671 template<> struct nullness<zview> : no_null<zview>
672 {};
673 
674 
676 template<> 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 
708 template<> struct nullness<std::stringstream> : no_null<std::stringstream>
709 {};
710 
711 
712 template<> 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 
732 template<> 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 
744 template<> 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 
756 template<> 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 
768 template<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 
780 template<typename T, typename... Args>
781 struct 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 
816 template<typename T, typename... Args>
817 inline format param_format(std::unique_ptr<T, Args...> const &value)
818 {
819  return param_format(*value);
820 }
821 
822 
823 template<typename T, typename... Args>
824 inline constexpr bool is_unquoted_safe<std::unique_ptr<T, Args...>>{
825  is_unquoted_safe<T>};
826 
827 
828 template<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 
840 template<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 
868 template<typename T> format param_format(std::shared_ptr<T> const &value)
869 {
870  return param_format(*value);
871 }
872 
873 
874 template<typename T>
875 inline constexpr bool is_unquoted_safe<std::shared_ptr<T>>{
876  is_unquoted_safe<T>};
877 
878 
879 template<> struct nullness<bytes> : no_null<bytes>
880 {};
881 
882 
883 #if defined(PQXX_HAVE_CONCEPTS)
884 template<binary DATA> struct nullness<DATA> : no_null<DATA>
885 {};
886 
887 
888 template<binary DATA> inline constexpr format param_format(DATA const &)
889 {
890  return format::binary;
891 }
892 
893 
894 template<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 
931 template<> 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 
967 template<> inline constexpr format param_format(bytes const &)
968 {
969  return format::binary;
970 }
971 
972 
973 template<> struct nullness<bytes_view> : no_null<bytes_view>
974 {};
975 
976 
977 template<> 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 
1005 template<> inline constexpr format param_format(bytes_view const &)
1006 {
1007  return format::binary;
1008 }
1009 } // namespace pqxx
1010 
1011 
1012 namespace pqxx::internal
1013 {
1015 template<typename Container> struct array_string_traits
1016 {
1017 private:
1018  using elt_type = strip_t<value_type<Container>>;
1020  static constexpr zview s_null{"NULL"};
1021 
1022 public:
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  }
1080  *here++ = array_separator<elt_type>;
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) :
1103  elt_traits::size_buffer(elt)) -
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 
1125 namespace pqxx
1126 {
1127 template<typename T, typename... Args>
1128 struct nullness<std::vector<T, Args...>> : no_null<std::vector<T>>
1129 {};
1130 
1131 
1132 template<typename T, typename... Args>
1133 struct string_traits<std::vector<T, Args...>>
1134  : internal::array_string_traits<std::vector<T, Args...>>
1135 {};
1136 
1137 
1139 template<typename T, typename... Args>
1140 inline constexpr format param_format(std::vector<T, Args...> const &)
1141 {
1142  return format::text;
1143 }
1144 
1145 
1147 template<typename... Args>
1148 inline constexpr format param_format(std::vector<std::byte, Args...> const &)
1149 {
1150  return format::binary;
1151 }
1152 
1153 
1154 template<typename T> inline constexpr bool is_sql_array<std::vector<T>>{true};
1155 
1156 
1157 #if defined(PQXX_HAVE_SPAN) && __has_include(<span>)
1158 template<typename T, size_t Extent>
1159 struct nullness<std::span<T, Extent>> : no_null<std::span<T, Extent>>
1160 {};
1161 
1162 
1163 template<typename T, size_t Extent>
1164 struct string_traits<std::span<T, Extent>>
1165  : internal::array_string_traits<std::span<T, Extent>>
1166 {};
1167 
1168 
1169 template<typename T, size_t Extent>
1170 inline constexpr format param_format(std::span<T, Extent> const &)
1171 {
1172  return format::text;
1173 }
1174 
1175 
1176 template<size_t Extent>
1177 inline constexpr format param_format(std::span<std::byte, Extent> const &)
1178 {
1179  return format::binary;
1180 }
1181 
1182 
1183 template<typename T, size_t Extent>
1184 inline constexpr bool is_sql_array<std::span<T, Extent>>{true};
1185 #endif
1186 
1187 
1188 template<typename T, std::size_t N>
1189 struct nullness<std::array<T, N>> : no_null<std::array<T, N>>
1190 {};
1191 
1192 
1193 template<typename T, std::size_t N>
1194 struct string_traits<std::array<T, N>>
1195  : internal::array_string_traits<std::array<T, N>>
1196 {};
1197 
1198 
1200 template<typename T, typename... Args, Args... args>
1201 inline constexpr format param_format(std::array<T, args...> const &)
1202 {
1203  return format::text;
1204 }
1205 
1206 
1208 template<typename... Args, Args... args>
1209 inline constexpr format param_format(std::array<std::byte, args...> const &)
1210 {
1211  return format::binary;
1212 }
1213 
1214 
1215 template<typename T, std::size_t N>
1216 inline constexpr bool is_sql_array<std::array<T, N>>{true};
1217 } // namespace pqxx
1218 
1219 
1220 namespace pqxx
1221 {
1222 template<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 
1241 template<> inline std::string to_string(float const &value)
1242 {
1243  return internal::to_string_float(value);
1244 }
1245 template<> inline std::string to_string(double const &value)
1246 {
1247  return internal::to_string_float(value);
1248 }
1249 template<> inline std::string to_string(long double const &value)
1250 {
1251  return internal::to_string_float(value);
1252 }
1253 template<> inline std::string to_string(std::stringstream const &value)
1254 {
1255  return value.str();
1256 }
1257 
1258 
1259 template<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
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
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
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::remove_cv_t< std::remove_reference_t< TYPE > > strip_t
Remove any constness, volatile, and reference-ness from a type.
Definition: types.hxx:78
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
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
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
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
PQXX_LIBEXPORT std::string to_string(field const &value)
Convert a field to a string.
Definition: result.cxx:566
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
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
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 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