libpqxx
The C++ client library for PostgreSQL
strconv.hxx
Go to the documentation of this file.
1 /* String conversion definitions.
2  *
3  * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/stringconv instead.
4  *
5  * Copyright (c) 2000-2026, Jeroen T. Vermeulen.
6  *
7  * See COPYING for copyright license. If you did not receive a file called
8  * COPYING with this source code, please notify the distributor of this
9  * mistake, or contact the author.
10  */
11 #ifndef PQXX_STRCONV_HXX
12 #define PQXX_STRCONV_HXX
13 
14 #if !defined(PQXX_HEADER_PRE)
15 # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
16 #endif
17 
18 #include <algorithm>
19 #include <charconv>
20 #include <cstring>
21 #include <limits>
22 #include <ranges>
23 #include <sstream>
24 #include <stdexcept>
25 #include <typeinfo>
26 
27 #include "pqxx/encoding_group.hxx"
28 #include "pqxx/except.hxx"
29 #include "pqxx/util.hxx"
30 #include "pqxx/zview.hxx"
31 
32 
33 namespace pqxx
34 {
56 
57 
58 // TODO: Drop `ENABLE` in 9.x.
60 
69 template<typename TYPE, typename ENABLE = void> struct nullness final
70 {
72  static bool has_null;
73 
75 
76  static constexpr bool always_null = false;
77 
79  PQXX_PURE static bool is_null(TYPE const &value);
80 
82 
87  [[nodiscard]] PQXX_PURE static TYPE null();
88 };
89 
90 
92 template<typename TYPE> struct no_null
93 {
95 
105  static constexpr bool has_null = false;
106 
108 
111  static constexpr bool always_null = false;
112 
114 
118  [[nodiscard]] PQXX_PURE static constexpr bool is_null(TYPE const &) noexcept
119  {
120  return false;
121  }
122 };
123 
124 
126 
132 template<typename TYPE, TYPE null_value> struct all_null
133 {
135  static constexpr bool has_null = true;
136 
138  static constexpr bool always_null = true;
139 
141  [[nodiscard]] PQXX_PURE static constexpr bool is_null(TYPE const &) noexcept
142  {
143  return true;
144  }
145 
147  [[nodiscard]] PQXX_PURE static constexpr TYPE null() { return null_value; }
148 };
149 
150 
152 
162 struct conversion_context final
163 {
164  // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
165 
167 
173 
175 
183  sl loc = sl::current();
184 
185  // NOLINTEND(misc-non-private-member-variables-in-classes)
186 
187  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
188  constexpr conversion_context(sl lc = sl::current()) : loc{lc} {}
189 
190  explicit constexpr conversion_context(
191  encoding_group e, sl l = sl::current()) :
192  enc{e}, loc{l}
193  {}
194 };
195 
196 
198 
201 using ctx = conversion_context const &;
202 
203 
205 
212 template<typename TYPE> struct string_traits final
213 {
215 
217  [[nodiscard]] static inline std::size_t
218  size_buffer(TYPE const &value) noexcept;
219 
221 
241  [[nodiscard]] static inline std::string_view
242  to_buf(std::span<char> buf, TYPE const &value, ctx = {});
243 
245 
258  [[nodiscard]] static inline TYPE
259  from_string(std::string_view text, ctx = {});
260 
261  // TODO: Move is_unquoted_safe into the traits after all?
262 };
263 
264 
265 // C++26: Use "=delete" with reason.
267 
273 template<typename TYPE> struct forbidden_conversion
274 {
275  [[noreturn]] static std::string_view
276  to_buf(std::span<char>, TYPE const &, ctx = {}) = delete;
277  [[noreturn]] static TYPE from_string(std::string_view, ctx = {}) = delete;
278  [[noreturn]] static std::size_t size_buffer(TYPE const &) noexcept = delete;
279 };
280 
281 
283 
298 template<> struct string_traits<char> final : forbidden_conversion<char>
299 {};
300 
301 
303 
316 template<>
317 struct string_traits<unsigned char> final : forbidden_conversion<unsigned char>
318 {};
319 
320 
322 
335 template<>
336 struct string_traits<signed char> final : forbidden_conversion<signed char>
337 {};
338 
339 
341 
346 template<>
347 struct string_traits<std::byte> final : forbidden_conversion<std::byte>
348 {};
349 
350 
352 template<enum_type ENUM> struct nullness<ENUM> final : no_null<ENUM>
353 {};
354 } // namespace pqxx
355 
356 namespace pqxx::internal
357 {
359 template<typename TYPE>
360 concept to_buf_7 =
361  requires(zview out, char *begin, char *end, TYPE const &value) {
362  out = string_traits<TYPE>::to_buf(begin, end, value);
363  };
364 
366 template<typename TYPE>
367 concept to_buf_8 = requires(
368  std::string_view out, std::span<char> buf, TYPE const &value, ctx c) {
369  out = string_traits<TYPE>::to_buf(buf, value, c);
370  out = string_traits<TYPE>::to_buf(buf, value);
371 };
372 
373 
375 template<typename TYPE>
376 concept from_string_8 = requires(std::string_view text, ctx c) {
379 };
380 
382 
385 template<typename TYPE>
386 concept from_string_7 = requires(std::string_view text) {
388 };
389 } // namespace pqxx::internal
390 
391 
392 namespace pqxx
393 {
395 
397 template<typename... TYPE>
398 [[nodiscard]] inline constexpr std::size_t
399 size_buffer(TYPE const &...value) noexcept
400 {
401  return (string_traits<std::remove_cvref_t<TYPE>>::size_buffer(value) + ...);
402 }
403 
404 
406 
416 template<typename TYPE>
417  requires(pqxx::internal::to_buf_7<TYPE> or pqxx::internal::to_buf_8<TYPE>)
418 consteval bool supports_to_buf_8()
419 {
420  return requires { requires pqxx::internal::to_buf_8<TYPE>; };
421 }
422 
423 
425 
428 template<typename TYPE>
429 [[nodiscard]] inline std::string_view
430 to_buf(std::span<char> buf, TYPE const &value, ctx c = {})
431 {
432  using traits = string_traits<TYPE>;
433  if constexpr (supports_to_buf_8<TYPE>())
434  {
435  return traits::to_buf(buf, value, c);
436  }
437  else
438  {
439  auto const begin{std::data(buf)}, end{begin + std::size(buf)};
440  return traits::to_buf(begin, end, value);
441  }
442 }
443 
444 
446 
452 template<typename TYPE>
453 [[nodiscard]] inline std::size_t
454 into_buf(std::span<char> buf, TYPE const &value, ctx c = {})
455 {
456  auto const data{std::data(buf)};
457  std::string_view out{to_buf(buf, value, c)};
458  auto const sz{std::size(out)};
459 
460  // Be careful to use a memory "move," not a "copy." Source and destination
461  // may overlap (or be identical).
462  if (not std::empty(out)) [[likely]]
463  {
464  if (std::cmp_greater(std::size(out), std::size(buf)))
465  throw conversion_overrun{
466  std::format(
467  "Buffer too small to convert {} value to string (needs a {}-byte "
468  "buffer).",
469  name_type<TYPE>(), std::size(out)),
470  c.loc};
471  std::memmove(data, std::data(out), sz);
472  }
473 
474  return sz;
475 }
476 
477 
479 
482 template<typename TYPE>
484  pqxx::internal::from_string_7<TYPE> or pqxx::internal::from_string_8<TYPE>)
485 consteval bool supports_from_string_8()
486 {
487  return requires { requires pqxx::internal::from_string_8<TYPE>; };
488 }
489 
490 
492 
504 template<typename TYPE>
505 [[nodiscard]] inline TYPE from_string(std::string_view text, ctx c = {})
506 {
507  if constexpr (supports_from_string_8<TYPE>())
508  return string_traits<TYPE>::from_string(text, c);
509  else
510  return string_traits<TYPE>::from_string(text);
511 }
512 
513 // clang-tidy rule bug:
514 // NOLINTBEGIN(misc-unused-parameters)
515 
517 
523 template<>
524 [[nodiscard]] inline std::string_view from_string(std::string_view text, ctx)
525 {
526  return text;
527 }
528 
529 // NOLINTEND(misc-unused-parameters)
530 
531 
533 
540 template<typename T>
541 inline void from_string(std::string_view text, T &value, ctx c = {})
542 {
543  value = from_string<T>(text, c);
544 }
545 
546 
548 
553 template<typename TYPE>
554 inline std::string to_string(TYPE const &value, ctx c = {});
555 } // namespace pqxx
556 
557 
558 namespace pqxx::internal
559 {
561 
574 template<enum_type ENUM> struct enum_traits
575 {
576  using impl_type = std::underlying_type_t<ENUM>;
577 
578  [[nodiscard]] static constexpr std::string_view
579  to_buf(std::span<char> buf, ENUM const &value, ctx c = {})
580  {
581  return pqxx::to_buf(buf, to_underlying(value), c);
582  }
583 
584  [[nodiscard]] static ENUM from_string(std::string_view text, ctx c = {})
585  {
586  return static_cast<ENUM>(pqxx::from_string<impl_type>(text, c));
587  }
588 
589  [[nodiscard]] static constexpr std::size_t
590  size_buffer(ENUM const &value) noexcept
591  {
592  return pqxx::size_buffer(to_underlying(value));
593  }
594 
595 private:
596  // C++23: Replace with std::to_underlying.
597  static constexpr impl_type to_underlying(ENUM const &value) noexcept
598  {
599  return static_cast<impl_type>(value);
600  }
601 };
602 } // namespace pqxx::internal
603 
604 
606 
617 #define PQXX_DECLARE_ENUM_CONVERSION(ENUM) \
618  template<> \
619  [[maybe_unused, deprecated("Use name_type() instead of type_name.")]] \
620  constexpr inline std::string_view type_name<ENUM>{#ENUM}; \
621  template<> inline constexpr std::string_view name_type<ENUM>() noexcept \
622  { \
623  return #ENUM; \
624  } \
625  template<> \
626  struct string_traits<ENUM> final : pqxx::internal::enum_traits<ENUM> \
627  {}
628 
629 
630 namespace pqxx
631 {
633 
640 template<typename... TYPE>
641 [[nodiscard, deprecated("Pass span and string_view.")]]
642 inline std::vector<std::string_view>
643 to_buf(char *begin, char const *end, TYPE... value)
644 {
645  assert(begin <= end);
646 
647  // We can't construct the span as {begin, end} because end points to const.
648  // Works fine on gcc 13, but clang 18 vomits huge cryptic errors.
649 
650  // clang-tidy rule bug:
651  // NOLINTNEXTLINE(misc-const-correctness)
652  std::span<char> buf{
653  begin, check_cast<std::size_t>(
654  end - begin, "string_view too large.", sl::current())};
655  std::size_t here{0u};
656  return {[&here, buf](auto v) {
657  auto start{here};
658  here += pqxx::into_buf(buf.subspan(start), v);
659  assert(start < here);
660  assert(here <= std::size(buf));
661  // C++26: Use buf.at().
662  assert(buf[here - 1] == '\0');
663  // Exclude the trailing zero out of the string_view.
664  auto len{here - start - 1};
665  return std::string_view{std::data(buf) + start, len};
666  }(value)...};
667 }
668 
669 
671 
678 template<typename... TYPE>
679 inline std::vector<std::string_view>
680 to_buf_multi(ctx c, std::span<char> buf, TYPE... value)
681 {
682  // TODO: Would it be worth merging consecutive identical strings?
683  std::size_t here{0u};
684  return {[&here, buf, &c](auto v) {
685  auto start{here};
686  here += pqxx::into_buf(buf.subspan(start), v, c);
687  assert(start < here);
688  assert(here <= std::size(buf));
689  // C++26: Use buf.at().
690  auto const len{here - start};
691  return std::string_view{std::data(buf) + start, len};
692  }(value)...};
693 }
694 
695 
697 
709 template<typename... TYPE>
710 inline std::vector<std::string_view>
711 to_buf_multi(std::span<char> buf, TYPE... value)
712 {
713  // TODO: Would it be worth merging consecutive identical strings?
714  std::size_t here{0u};
715  conversion_context c{};
716  return {[&here, buf, &c](auto v) {
717  auto start{here};
718  here += pqxx::into_buf(buf.subspan(start), v, c);
719  assert(start < here);
720  assert(here <= std::size(buf));
721  // C++26: Use buf.at().
722  auto const len{here - start};
723  return std::string_view{std::data(buf) + start, len};
724  }(value)...};
725 }
726 
727 
729 
732 template<typename TYPE>
733 inline void into_string(TYPE const &value, std::string &out);
734 
735 
737 
744 template<typename TYPE> [[nodiscard]] inline constexpr bool has_null() noexcept
745 {
746  using base_type = std::remove_cvref_t<TYPE>;
748 }
749 
750 
752 
754 template<typename TYPE>
755 [[nodiscard]] inline constexpr bool always_null() noexcept
756 {
757  using base_type = std::remove_cvref_t<TYPE>;
759 }
760 
761 
763 template<typename TYPE>
764 [[nodiscard]] inline constexpr bool is_null(TYPE const &value) noexcept
765 {
766  using base_type = std::remove_cvref_t<TYPE>;
767  if constexpr (always_null<TYPE>())
768  return true;
769  else
770  return nullness<base_type>::is_null(value);
771 }
772 
773 
775 
780 template<typename TYPE>
781 [[nodiscard]] inline constexpr TYPE make_null()
782  requires(pqxx::has_null<TYPE>())
783 {
784  using base_type = std::remove_cvref_t<TYPE>;
785  return nullness<base_type>::null();
786 }
787 
788 
790 
796 template<typename TYPE> inline constexpr bool is_sql_array{false};
797 
798 
800 
812 template<typename TYPE> inline constexpr bool is_unquoted_safe{false};
813 
814 
816 template<typename T> inline constexpr char array_separator{','};
817 
818 
820 
827 template<typename TYPE> inline constexpr format param_format(TYPE const &)
828 {
829  return format::text;
830 }
832 } // namespace pqxx
833 
834 
836 #endif
Marker-type wrapper: zero-terminated std::string_view.
Definition: zview.hxx:55
#define PQXX_PURE
Definition: header-pre.hxx:64
Private namespace for libpqxx's internal use; do not access.
Definition: connection.cxx:333
concept to_buf_7
Signature for string_traits<TYPE>::to_buf() in libpqxx 7.
Definition: strconv.hxx:360
concept from_string_8
Signature for string_traits<TYPE>::from_string() in libpqxx 8.
Definition: strconv.hxx:376
concept from_string_7
Signature for string_traits<TYPE>::from_string() in libpqxx 7.
Definition: strconv.hxx:386
concept to_buf_8
Signature for string_traits<TYPE>::to_buf() in libpqxx 8.
Definition: strconv.hxx:367
The home of all libpqxx classes, functions, templates, etc.
Definition: array.cxx:26
std::string_view to_buf(std::span< char > buf, TYPE const &value, ctx c={})
Represent value as SQL text, optionally using buf as storage.
Definition: strconv.hxx:430
constexpr bool has_null() noexcept
Does TYPE have one or more inherent null values?
Definition: strconv.hxx:744
constexpr TYPE make_null() requires(pqxx
Return a null value of TYPE.
Definition: strconv.hxx:781
constexpr char array_separator
Element separator between SQL array elements of this type.
Definition: strconv.hxx:816
std::source_location sl
Convenience alias for std::source_location. It's just too long.
Definition: types.hxx:38
constexpr format param_format(std::optional< T > const &value)
Definition: conversions.hxx:313
T from_string(field const &value, ctx c={})
Convert a field's value to type T.
Definition: field.hxx:831
PQXX_LIBEXPORT std::string to_string(field_ref const &value, ctx)
Convert a field_ref to a string.
Definition: field.hxx:891
encoding_group
Definition: encoding_group.hxx:40
@ unknown
Default: indeterminate encoding. All we know is it supports ASCII.
constexpr 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:399
constexpr bool is_null(TYPE const &value) noexcept
Is value a null?
Definition: strconv.hxx:764
std::vector< std::string_view > to_buf(char *begin, char const *end, TYPE... value)
Convert multiple values to strings inside a single buffer.
Definition: strconv.hxx:643
requires(pqxx::internal::to_buf_7< TYPE > or pqxx::internal::to_buf_8< TYPE >) const eval bool supports_to_buf_8()
Is the libpqxx 8 version of to_buf() supported for TYPE?
Definition: strconv.hxx:417
std::size_t into_buf(std::span< char > buf, TYPE const &value, ctx c={})
Write an SQL representation of value into buf.
Definition: strconv.hxx:454
constexpr bool is_sql_array
Does this type translate to an SQL array?
Definition: strconv.hxx:796
std::vector< std::string_view > to_buf_multi(ctx c, std::span< char > buf, TYPE... value)
Convert multiple values to strings inside a single buffer.
Definition: strconv.hxx:680
conversion_context const & ctx
Convenience alias: const reference to a pqxx::conversion_context.
Definition: strconv.hxx:201
constexpr bool always_null() noexcept
Is a value of TYPE always null?
Definition: strconv.hxx:755
constexpr bool is_unquoted_safe
Can we use this type in arrays and composite types without quoting them?
Definition: strconv.hxx:812
format
Format code: is data text or binary?
Definition: types.hxx:121
void into_string(T const &value, std::string &out, ctx c={})
Definition: conversions.hxx:1024
Nullness traits describing a type whose values are always null.
Definition: strconv.hxx:133
static constexpr bool has_null
Does TYPE have a null value?
Definition: strconv.hxx:135
static constexpr bool always_null
Is TYPE always null?
Definition: strconv.hxx:138
static constexpr PQXX_PURE bool is_null(TYPE const &) noexcept
Is the given TYPE value a null?
Definition: strconv.hxx:141
Contextual parameters for string conversions implementations.
Definition: strconv.hxx:163
sl loc
A std::source_location for the call.
Definition: strconv.hxx:183
constexpr conversion_context(encoding_group e, sl l=sl::current())
Definition: strconv.hxx:190
encoding_group enc
Encoding group describing the client text encoding.
Definition: strconv.hxx:172
constexpr conversion_context(sl lc=sl::current())
Definition: strconv.hxx:188
String traits for a forbidden type conversion.
Definition: strconv.hxx:274
static std::string_view to_buf(std::span< char >, TYPE const &, ctx={})=delete
static TYPE from_string(std::string_view, ctx={})=delete
static std::size_t size_buffer(TYPE const &) noexcept=delete
Helper class for defining enum conversions.
Definition: strconv.hxx:575
static constexpr std::size_t size_buffer(ENUM const &value) noexcept
Definition: strconv.hxx:590
static constexpr std::string_view to_buf(std::span< char > buf, ENUM const &value, ctx c={})
Definition: strconv.hxx:579
static ENUM from_string(std::string_view text, ctx c={})
Definition: strconv.hxx:584
std::underlying_type_t< ENUM > impl_type
Definition: strconv.hxx:576
Nullness traits describing a type which does not have a null value.
Definition: strconv.hxx:93
static constexpr PQXX_PURE bool is_null(TYPE const &) noexcept
Does a given value correspond to an SQL null value?
Definition: strconv.hxx:118
static constexpr bool always_null
Are all values of this type null?
Definition: strconv.hxx:111
static constexpr bool has_null
Does TYPE have a "built-in null value"?
Definition: strconv.hxx:105
Traits describing a type's "null value," if any.
Definition: strconv.hxx:70
static constexpr bool always_null
Is this type always null?
Definition: strconv.hxx:76
static PQXX_PURE bool is_null(TYPE const &value)
Is value a null?
static bool has_null
Does this type have a null value?
Definition: strconv.hxx:72
static PQXX_PURE TYPE null()
Return a null value.
Traits class for use in string conversions.
Definition: strconv.hxx:213
static std::size_t size_buffer(TYPE const &value) noexcept
Estimate how much buffer space is needed to represent value as SQL text.
static TYPE from_string(std::string_view text, ctx={})
Parse a string representation of a TYPE value.
Definition: time.cxx:198
static std::string_view to_buf(std::span< char > buf, TYPE const &value, ctx={})
Return a string_view representing value in SQL text.
Definition: time.cxx:180