libpqxx
The C++ client library for PostgreSQL
array.hxx
Go to the documentation of this file.
1 /* Handling of SQL arrays.
2  *
3  * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/field 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_ARRAY_HXX
12 #define PQXX_ARRAY_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 <cassert>
20 #include <format>
21 #include <stdexcept>
22 #include <string>
23 #include <type_traits>
24 #include <utility>
25 #include <vector>
26 
27 #include "pqxx/connection.hxx"
28 #include "pqxx/encoding_group.hxx"
29 
31 
32 
33 namespace pqxx
34 {
35 // TODO: Specialise for string_view/zview, allocate all strings in one buffer.
36 
38 
53 template<
54  not_borrowed ELEMENT, std::size_t DIMENSIONS = 1u,
55  char SEPARATOR = array_separator<ELEMENT>>
56 class array final
57 {
58 public:
60 
69  array(std::string_view data, connection const &cx, sl loc = sl::current()) :
70  array{data, cx.get_encoding_group(loc), loc}
71  {}
72 
73  array(std::string_view data, encoding_group enc, sl loc = sl::current()) :
74  m_ctx{enc, loc}
75  {
76  using group = encoding_group;
77  switch (enc)
78  {
79  case group::unknown:
80  throw usage_error{
81  "Tried to parse array without knowing its encoding.", loc};
82 
83  case group::ascii_safe: parse<group::ascii_safe>(data, loc); break;
84  case group::two_tier: parse<group::two_tier>(data, loc); break;
85  case group::gb18030: parse<group::gb18030>(data, loc); break;
86  case group::sjis: parse<group::sjis>(data, loc); break;
87  // clang-tidy rule bug:
88  // NOLINTNEXTLINE(bugprone-suspicious-semicolon)
89  default: PQXX_UNREACHABLE; break;
90  }
91  }
92 
94  using value_type = ELEMENT;
95 
97 
99  PQXX_PURE static constexpr std::size_t dimensions() noexcept
100  {
101  return DIMENSIONS;
102  }
103 
105 
107  PQXX_PURE [[nodiscard]] static constexpr char separator() noexcept
108  {
109  return SEPARATOR;
110  }
111 
113 
117  PQXX_PURE [[nodiscard]] std::array<std::size_t, DIMENSIONS> const &
118  sizes() const noexcept
119  {
120  return m_extents;
121  }
122 
123  template<std::integral... INDEX>
124  [[nodiscard]] ELEMENT const &at(INDEX... index) const
125  {
126  static_assert(sizeof...(index) == DIMENSIONS);
127  check_bounds(index...);
128  return m_elts.at(locate(index...));
129  }
130 
132 
146  template<std::integral... INDEX>
147  PQXX_PURE ELEMENT const &operator[](INDEX... index) const
148  {
149  static_assert(sizeof...(index) == DIMENSIONS);
150  // TODO: Use operator[]. But Facebook's "infer" sees a buffer overflow.
151  return m_elts.at(locate(index...));
152  }
153 
168  PQXX_PURE [[nodiscard]] constexpr auto cbegin() const noexcept
169  {
170  return m_elts.cbegin();
171  }
173  PQXX_PURE [[nodiscard]] constexpr auto cend() const noexcept
174  {
175  return m_elts.cend();
176  }
178  PQXX_PURE [[nodiscard]] constexpr auto begin() const noexcept
179  {
180  return cbegin();
181  }
183  PQXX_PURE [[nodiscard]] constexpr auto end() const noexcept
184  {
185  return cend();
186  }
188  PQXX_PURE [[nodiscard]] constexpr auto crbegin() const noexcept
189  {
190  return m_elts.crbegin();
191  }
193  PQXX_PURE [[nodiscard]] constexpr auto crend() const noexcept
194  {
195  return m_elts.crend();
196  }
197  PQXX_PURE [[nodiscard]] constexpr auto rbegin() const noexcept
198  {
199  return crbegin();
200  }
202  PQXX_PURE [[nodiscard]] constexpr auto rend() const noexcept
203  {
204  return crend();
205  }
207 
209 
212  [[nodiscard]] constexpr std::size_t size() const noexcept
213  {
214  return m_elts.size();
215  }
216 
218 
233  [[nodiscard]] constexpr auto ssize() const noexcept
234  {
235  return static_cast<std::ptrdiff_t>(size());
236  }
237 
239 
241  [[nodiscard]] constexpr auto const &front() const noexcept
242  {
243  return m_elts.front();
244  }
245 
247 
249  [[nodiscard]] constexpr auto const &back() const noexcept
250  {
251  return m_elts.back();
252  }
253 
254 private:
256 
264  void check_dims(std::string_view data, sl loc)
265  {
266  auto sz{std::size(data)};
267  if (sz < DIMENSIONS * 2)
268  throw conversion_error{
269  std::format(
270  "Trying to parse a {}-dimensional array out of '{}'.", DIMENSIONS,
271  data),
272  loc};
273 
274  // Making some assumptions here:
275  // * The array holds no extraneous whitespace.
276  // * None of the sub-arrays can be null.
277  // * Only ASCII characters start off with a byte in the 0-127 range.
278  //
279  // Given those, the input must start with a sequence of DIMENSIONS bytes
280  // with the ASCII value for '{'; and likewise it must end with a sequence
281  // of DIMENSIONS bytes with the ASCII value for '}'.
282 
283  if (data.at(0) != '{')
284  throw conversion_error{"Malformed array: does not start with '{'.", loc};
285  for (std::size_t i{0}; i < DIMENSIONS; ++i)
286  if (data.at(i) != '{')
287  throw conversion_error{
288  std::format(
289  "Expecting {}-dimensional array, but found {}.", DIMENSIONS, i),
290  loc};
291  if (data.at(DIMENSIONS) == '{')
292  throw conversion_error{
293  std::format(
294  "Tried to parse {}-dimensional array from array data that has more "
295  "dimensions.",
296  DIMENSIONS),
297  loc};
298  for (std::size_t i{0}; i < DIMENSIONS; ++i)
299  if (data.at(sz - 1 - i) != '}')
300  throw conversion_error{
301  "Malformed array: does not end in the right number of '}'.", loc};
302  }
303 
305 
308  [[nodiscard]] std::size_t
309  parse_field_end(std::string_view data, std::size_t here, sl loc) const
310  {
311  auto const sz{std::size(data)};
312  if (here < sz)
313  switch (data.at(here))
314  {
315  case SEPARATOR:
316  ++here;
317  if (here >= sz)
318  throw conversion_error{"Array looks truncated.", loc};
319  switch (data.at(here))
320  {
321  case SEPARATOR:
322  throw conversion_error{"Array contains double separator.", loc};
323  case '}':
324  throw conversion_error{"Array contains trailing separator.", loc};
325  default: break;
326  }
327  break;
328  case '}': break;
329  default:
330  throw conversion_error{
331  std::format(
332  "Unexpected character in array: {} where separator or closing "
333  "brace expected.",
334  static_cast<unsigned>(static_cast<unsigned char>(data.at(here)))),
335  loc};
336  }
337  return here;
338  }
339 
341 
346  [[nodiscard]] constexpr std::size_t
347  estimate_elements(std::string_view data) const noexcept
348  {
349  // Dirty trick: just count the number of bytes that look as if they may be
350  // separators.
351  auto const separators{
352  std::count(std::begin(data), std::end(data), SEPARATOR)};
353  // The number of dimensions makes no difference here. It's still one
354  // separator between consecutive elements, just possibly with some extra
355  // braces as well.
356  return static_cast<std::size_t>(separators + 1);
357  }
358 
359  template<encoding_group ENC> void parse(std::string_view data, sl loc)
360  {
361  static_assert(DIMENSIONS > 0u, "Can't create a zero-dimensional array.");
362  conversion_context const c{m_ctx.enc, loc};
363  auto const sz{std::size(data)};
364  check_dims(data, loc);
365 
366  m_elts.reserve(estimate_elements(data));
367 
368  // We discover the array's extents along each of the dimensions, starting
369  // with the final dimension and working our way towards the first. At any
370  // given point during parsing, we know the extents starting at this
371  // dimension.
372  std::size_t know_extents_from{DIMENSIONS};
373 
374  // Currently parsing this dimension. We start off at -1, relying on C++'s
375  // well-defined rollover for unsigned numbers.
376  // The actual outermost dimension of the array is 0, and the innermost is
377  // at the end. But, the array as a whole is enclosed in braces just like
378  // each row. So we act like there's an anomalous "outer" dimension holding
379  // the entire array.
380  constexpr std::size_t outer{std::size_t{0u} - std::size_t{1u}};
381 
382  // We start parsing at the fictional outer dimension. The input begins
383  // with opening braces, one for each dimension, so we'll start off by
384  // bumping all the way to the innermost dimension.
385  std::size_t dim{outer};
386 
387  // Extent counters, one per "real" dimension.
388  // Note initialiser syntax; this zero-initialises all elements.
389  std::array<std::size_t, DIMENSIONS> extents{};
390 
391  // Current parsing position.
392  std::size_t here{0};
393  PQXX_ASSUME(here <= sz);
394  while (here < sz)
395  {
396  if (data.at(here) == '{')
397  {
398  if (dim == outer)
399  {
400  // This must be the initial opening brace.
401  if (know_extents_from != DIMENSIONS)
402  throw conversion_error{
403  "Array text representation closed and reopened its outside "
404  "brace pair.",
405  loc};
406  assert(here == 0);
407  PQXX_ASSUME(here == 0);
408  }
409  else
410  {
411  if (dim >= (DIMENSIONS - 1))
412  throw conversion_error{
413  "Array seems to have inconsistent number of dimensions.", loc};
414  ++extents.at(dim);
415  }
416  // (Rolls over to zero if we're coming from the outer dimension.)
417  ++dim;
418  extents.at(dim) = 0u;
419  ++here;
420  }
421  else if (data.at(here) == '}')
422  {
423  if (dim == outer)
424  throw conversion_error{"Array has spurious '}'.", loc};
425  if (dim < know_extents_from)
426  {
427  // We just finished parsing our first row in this dimension.
428  // Now we know the array dimension's extent.
429  m_extents.at(dim) = extents.at(dim);
430  know_extents_from = dim;
431  }
432  else
433  {
434  if (extents.at(dim) != m_extents.at(dim))
435  throw conversion_error{
436  "Rows in array have inconsistent sizes.", loc};
437  }
438  // Bump back down to the next-lower dimension. Which may be the outer
439  // dimension, through underflow.
440  --dim;
441  ++here;
442  here = parse_field_end(data, here, loc);
443  }
444  else
445  {
446  // Found an array element. The actual elements always live in the
447  // "inner" dimension.
448  if (dim != DIMENSIONS - 1)
449  throw conversion_error{
450  "Malformed array: found element where sub-array was expected.",
451  loc};
452  assert(dim != outer);
453  ++extents.at(dim);
454  std::size_t end{};
455  switch (data.at(here))
456  {
457  case '\0':
458  throw conversion_error{"Unexpected zero byte in array.", loc};
459  case ',': throw conversion_error{"Array contains empty field.", loc};
460  case '"': {
461  // Double-quoted string. We parse it into a buffer before parsing
462  // the resulting string as an element. This seems wasteful: the
463  // string might not contain any special characters. So it's
464  // tempting to check, and try to use a string_view and avoid a
465  // useless copy step. But. Even besides the branch prediction
466  // risk, the very fact that the back-end chose to quote the string
467  // indicates that there is some kind of special character in there.
468  // So in practice, this optimisation would only apply if the only
469  // special characters in the string were commas.
470  end =
471  pqxx::internal::scan_double_quoted_string<ENC>(data, here, loc);
472  // TODO: scan_double_quoted_string() with reusable buffer.
473  std::string const buf{
474  pqxx::internal::parse_double_quoted_string<ENC>(
475  data.substr(0, end), here, loc)};
476  m_elts.emplace_back(from_string<ELEMENT>(buf, c));
477  }
478  break;
479  default: {
480  // Unquoted string. An unquoted string is always literal, no
481  // escaping or encoding, so we don't need to parse it into a
482  // buffer. We can just read it as a string_view.
483  end = pqxx::internal::scan_unquoted_string<ENC, SEPARATOR, '}'>(
484  data, here, loc);
485  std::string_view const field{
486  std::string_view{std::data(data) + here, end - here}};
487  if (field == "NULL")
488  {
489  if constexpr (has_null<ELEMENT>())
490  m_elts.emplace_back(make_null<ELEMENT>());
491  else
492  throw unexpected_null{
493  std::format(
494  "Array contains a null {}. Consider making it an array of "
495  "std::optional<{}> instead.",
496  name_type<ELEMENT>(), name_type<ELEMENT>()),
497  loc};
498  }
499  else
500  m_elts.emplace_back(from_string<ELEMENT>(field, c));
501  }
502  }
503  here = end;
504  PQXX_ASSUME(here <= sz);
505  here = parse_field_end(data, here, loc);
506  }
507  }
508 
509  if (dim != outer)
510  throw conversion_error{"Malformed array; may be truncated.", loc};
511  assert(know_extents_from == 0);
512  PQXX_ASSUME(know_extents_from == 0);
513 
514  init_factors();
515  }
516 
518  void init_factors() noexcept
519  {
520  std::size_t factor{1};
521  for (std::size_t dim{DIMENSIONS - 1}; dim > 0; --dim)
522  {
523  factor *= m_extents.at(dim);
524  m_factors.at(dim - 1) = factor;
525  }
526  }
527 
529  template<typename... INDEX>
530  [[nodiscard]] std::size_t locate(INDEX... index) const noexcept
531  {
532  static_assert(
533  sizeof...(index) == DIMENSIONS,
534  "Indexing array with wrong number of dimensions.");
535  return add_index(index...);
536  }
537 
538  template<typename OUTER, typename... INDEX>
539  [[nodiscard]] constexpr std::size_t
540  add_index(OUTER outer, INDEX... indexes) const noexcept
541  {
542  std::size_t const first{
543  check_cast<std::size_t>(outer, "array index"sv, m_ctx.loc)};
544  if constexpr (sizeof...(indexes) == 0)
545  {
546  return first;
547  }
548  else
549  {
550  static_assert(sizeof...(indexes) < DIMENSIONS);
551  // (Offset by 1 here because the outer dimension is not in there.)
552  constexpr auto dimension{DIMENSIONS - (sizeof...(indexes) + 1)};
553  static_assert(dimension < DIMENSIONS);
554  return first * m_factors.at(dimension) + add_index(indexes...);
555  }
556  }
557 
559 
561  template<typename OUTER, std::integral... INDEX>
562  constexpr void check_bounds(OUTER outer, INDEX... indexes) const
563  {
564  std::size_t const first{
565  check_cast<std::size_t>(outer, "array index"sv, m_ctx.loc)};
566  static_assert(sizeof...(indexes) < DIMENSIONS);
567  // (Offset by 1 here because the outer dimension is not in there.)
568  constexpr auto dimension{DIMENSIONS - (sizeof...(indexes) + 1)};
569  static_assert(dimension < DIMENSIONS);
570  if (std::cmp_greater_equal(first, m_extents.at(dimension)))
571  throw range_error{
572  std::format(
573  "Array index for dimension {} is out of bounds: {} >= {}.",
574  dimension, first, m_extents.at(dimension)),
575  m_ctx.loc};
576 
577  // Now check the rest of the indexes, if any.
578  if constexpr (sizeof...(indexes) > 0)
579  check_bounds(indexes...);
580  }
581 
583  std::vector<ELEMENT> m_elts;
584 
586  std::array<std::size_t, DIMENSIONS> m_extents;
587 
589 
596  std::array<std::size_t, DIMENSIONS - 1> m_factors;
597 
599 
603  conversion_context m_ctx;
604 };
605 
606 
608 
611 template<typename ELEMENT, std::size_t DIMENSIONS>
613  array<ELEMENT, DIMENSIONS, array_separator<ELEMENT>>> final
614 {
615 private:
616  using elt_type = std::remove_cvref_t<ELEMENT>;
618  static constexpr zview s_null{"NULL"};
619 
620 public:
622 
623  static std::string_view
624  to_buf(std::span<char> buf, array_type const &value, ctx c = {})
625  {
626  auto const len{pqxx::internal::array_into_buf(buf, value, c)};
627  assert(len > 0);
628  assert(buf[len - 1] == '\0');
629  return {std::data(buf), len - 1};
630  }
631 
632  static std::size_t size_buffer(array_type const &value) noexcept
633  {
634  if constexpr (is_unquoted_safe<elt_type>)
635  return 3 + std::accumulate(
636  std::begin(value), std::end(value), std::size_t{},
637  [](std::size_t acc, elt_type const &elt) {
638  // Budget for each element includes a terminating zero.
639  // We won't actually be wanting those, but don't subtract
640  // that one byte: we want room for a separator instead.
641  // However, std::size(s_null) doesn't account for the
642  // terminating zero, so add one to make s_null pay for its
643  // own separator.
644  return acc + (pqxx::is_null(elt) ?
645  (std::size(s_null) + 1) :
647  });
648  else
649  return 3 + std::accumulate(
650  std::begin(value), std::end(value), std::size_t{},
651  [](std::size_t acc, elt_type const &elt) {
652  // Opening and closing quotes, plus worst-case escaping,
653  // and the one byte for the trailing zero becomes room
654  // for a separator. However, std::size(s_null) doesn't
655  // account for the terminating zero, so add one to make
656  // s_null pay for its own separator.
657  std::size_t const elt_size{
658  pqxx::is_null(elt) ? (std::size(s_null) + 1) :
660  return acc + 2 * elt_size + 2;
661  });
662  }
663 
664  static array_type from_string(std::string_view text, ctx c = {})
665  {
667  text, c.enc, c.loc};
668  }
669 };
670 
672 template<typename ELEMENT, std::size_t DIMENSIONS>
673 struct nullness<array<ELEMENT, DIMENSIONS, array_separator<ELEMENT>>> final
674  : no_null<array<ELEMENT, DIMENSIONS, array_separator<ELEMENT>>>
675 {};
676 } // namespace pqxx
677 
678 
679 namespace pqxx::internal
680 {
681 template<typename CONT>
682 concept containerlike = requires(CONT con) { CONT{con.begin(), con.end()}; };
683 
684 
685 template<typename CONT>
686 concept nonbinary_container = nonbinary_range<CONT> and containerlike<CONT>;
687 } // namespace pqxx::internal
688 
689 
690 namespace pqxx
691 {
693 template<pqxx::internal::nonbinary_container CONT>
694 struct nullness<CONT> final : no_null<CONT>
695 {};
696 
697 
698 // TODO: Can we support multiple dimensions?
700 
703 template<pqxx::internal::nonbinary_container CONT>
705 {
706  [[nodiscard]] static CONT from_string(std::string_view text, ctx c = {})
707  {
708  using value_type = typename CONT::value_type;
709  using array_type = array<value_type>;
710  auto const arr = pqxx::from_string<array_type>(text, c);
711  return CONT{arr.cbegin(), arr.cend()};
712  }
713 };
714 
715 
717 
738 {
739 public:
741  enum class juncture
742  {
744  row_start,
746  row_end,
748  null_value,
750  string_value,
752  done,
753  };
754 
756 
760  [[deprecated("Use pqxx::array instead.")]]
761  explicit array_parser(
762  std::string_view input, encoding_group = encoding_group::ascii_safe);
763 
765 
771  std::pair<juncture, std::string> get_next(sl loc = sl::current())
772  {
773  return (this->*m_impl)(loc);
774  }
775 
776 private:
777  std::string_view m_input;
778 
780  std::size_t m_pos = 0u;
781 
783 
788  using implementation =
789  std::pair<juncture, std::string> (array_parser::*)(sl);
790 
792  static implementation specialize_for_encoding(encoding_group enc, sl loc);
793 
795  implementation m_impl;
796 
798  template<encoding_group>
799  std::pair<juncture, std::string> parse_array_step(sl loc);
800 
801  template<encoding_group>
802  [[nodiscard]] std::size_t scan_double_quoted_string(sl loc) const;
803  template<encoding_group>
804  [[nodiscard]] std::string
805  parse_double_quoted_string(std::size_t end, sl loc) const;
806  template<encoding_group>
807  [[nodiscard]] std::size_t scan_unquoted_string(sl loc) const;
808  template<encoding_group>
809  [[nodiscard]] std::string_view
810  parse_unquoted_string(std::size_t end, sl loc) const;
811 };
812 } // namespace pqxx
813 #endif
Low-level parser for C++ arrays.
Definition: array.hxx:738
juncture
What's the latest thing found in the array?
Definition: array.hxx:742
std::pair< juncture, std::string > get_next(sl loc=sl::current())
Parse the next step in the array.
Definition: array.hxx:771
An SQL array received from the database.
Definition: array.hxx:57
constexpr PQXX_PURE auto rbegin() const noexcept
Definition: array.hxx:197
constexpr auto const & front() const noexcept
Refer to the first element, if any.
Definition: array.hxx:241
constexpr PQXX_PURE auto end() const noexcept
Return endpoint of iteration.
Definition: array.hxx:183
ELEMENT value_type
The element type of values in this array.
Definition: array.hxx:94
constexpr std::size_t size() const noexcept
Number of elements in the array.
Definition: array.hxx:212
constexpr auto const & back() const noexcept
Refer to the last element, if any.
Definition: array.hxx:249
array(std::string_view data, connection const &cx, sl loc=sl::current())
Parse an SQL array, read as text from a pqxx::result or stream.
Definition: array.hxx:69
constexpr auto ssize() const noexcept
Number of elements in the array (as a signed number).
Definition: array.hxx:233
PQXX_PURE ELEMENT const & operator[](INDEX... index) const
Access element (without bounds check).
Definition: array.hxx:147
PQXX_PURE std::array< std::size_t, DIMENSIONS > const & sizes() const noexcept
Return the sizes of this array in each of its dimensions.
Definition: array.hxx:118
array(std::string_view data, encoding_group enc, sl loc=sl::current())
Definition: array.hxx:73
ELEMENT const & at(INDEX... index) const
Definition: array.hxx:124
constexpr PQXX_PURE auto rend() const noexcept
Return end point of reverse iteration.
Definition: array.hxx:202
constexpr PQXX_PURE auto cend() const noexcept
Return end point of iteration.
Definition: array.hxx:173
constexpr PQXX_PURE auto crend() const noexcept
Return end point of reverse iteration.
Definition: array.hxx:193
constexpr PQXX_PURE auto cbegin() const noexcept
Begin iteration of individual elements.
Definition: array.hxx:168
static constexpr PQXX_PURE std::size_t dimensions() noexcept
How many dimensions does this array have?
Definition: array.hxx:99
constexpr PQXX_PURE auto begin() const noexcept
Begin iteration of individual elements.
Definition: array.hxx:178
static constexpr PQXX_PURE char separator() noexcept
What is the separator used for parsing this array's values?
Definition: array.hxx:107
constexpr PQXX_PURE auto crbegin() const noexcept
Begin reverse iteration.
Definition: array.hxx:188
Connection to a database.
Definition: connection.hxx:273
Marker-type wrapper: zero-terminated std::string_view.
Definition: zview.hxx:55
Error in usage of libpqxx library, similar to std::logic_error.
Definition: except.hxx:580
#define PQXX_LIBEXPORT
Definition: header-pre.hxx:206
#define PQXX_PURE
Definition: header-pre.hxx:64
#define PQXX_ASSUME(condition)
Definition: header-pre.hxx:228
Private namespace for libpqxx's internal use; do not access.
Definition: connection.cxx:333
constexpr PQXX_PURE pqxx::encoding_group get_encoding_group(encoding_group const &enc, sl=sl::current()) noexcept
Identity function for encoding_group, for regularity.
Definition: params.hxx:29
concept nonbinary_container
Definition: array.hxx:686
concept containerlike
Definition: array.hxx:682
constexpr PQXX_INLINE_ONLY std::string_view parse_unquoted_string(std::string_view input, std::size_t pos, sl)
Parse an unquoted array entry or cfield of a composite-type field.
Definition: array-composite.hxx:193
constexpr PQXX_INLINE_COV std::string parse_double_quoted_string(std::string_view input, std::size_t pos, sl loc)
Un-quote and un-escape a double-quoted SQL string.
Definition: array-composite.hxx:106
constexpr PQXX_INLINE_COV std::size_t scan_unquoted_string(std::string_view input, std::size_t pos, sl loc)
Find the end of an unquoted string in an array or composite-type value.
Definition: array-composite.hxx:179
PQXX_INLINE_COV std::size_t array_into_buf(std::span< char > buf, TYPE const &value, std::size_t budget, ctx c)
Write an SQL array representation into buf.
Definition: array-composite.hxx:417
constexpr PQXX_INLINE_COV std::size_t scan_double_quoted_string(std::string_view input, std::size_t pos, sl loc)
Definition: array-composite.hxx:27
The home of all libpqxx classes, functions, templates, etc.
Definition: array.cxx:26
concept not_borrowed
Concept: A value that's not just a reference to values elsewhere.
Definition: types.hxx:206
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
encoding_group
Definition: encoding_group.hxx:40
@ ascii_safe
"ASCII-safe" encodings.
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
std::remove_cvref_t< std::ranges::range_value_t< CONTAINER > > value_type
The type of a container's elements.
Definition: types.hxx:138
constexpr bool is_null(TYPE const &value) noexcept
Is value a null?
Definition: strconv.hxx:764
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
conversion_context const & ctx
Convenience alias: const reference to a pqxx::conversion_context.
Definition: strconv.hxx:201
format
Format code: is data text or binary?
Definition: types.hxx:121
sl loc
A std::source_location for the call.
Definition: strconv.hxx:183
encoding_group enc
Encoding group describing the client text encoding.
Definition: strconv.hxx:172
Base class for string_traits specialisations for nonbinary ranges.
Definition: conversions.hxx:894
Nullness traits describing a type which does not have a null value.
Definition: strconv.hxx:93
Traits describing a type's "null value," if any.
Definition: strconv.hxx:70
static CONT from_string(std::string_view text, ctx c={})
Definition: array.hxx:706
static std::string_view to_buf(std::span< char > buf, array_type const &value, ctx c={})
Definition: array.hxx:624
static std::size_t size_buffer(array_type const &value) noexcept
Definition: array.hxx:632
static array_type from_string(std::string_view text, ctx c={})
Definition: array.hxx:664
Traits class for use in string conversions.
Definition: strconv.hxx:213
#define PQXX_UNREACHABLE
Definition: util.hxx:51