11 #ifndef PQXX_ARRAY_HXX
12 #define PQXX_ARRAY_HXX
14 #if !defined(PQXX_HEADER_PRE)
15 # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
23 #include <type_traits>
55 char SEPARATOR = array_separator<ELEMENT>>
81 "Tried to parse array without knowing its encoding.", loc};
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;
117 PQXX_PURE [[nodiscard]] std::array<std::size_t, DIMENSIONS>
const &
123 template<std::integral... INDEX>
124 [[nodiscard]] ELEMENT
const &
at(INDEX... index)
const
126 static_assert(
sizeof...(index) == DIMENSIONS);
127 check_bounds(index...);
128 return m_elts.at(locate(index...));
146 template<std::integral... INDEX>
149 static_assert(
sizeof...(index) == DIMENSIONS);
151 return m_elts.at(locate(index...));
170 return m_elts.cbegin();
175 return m_elts.cend();
190 return m_elts.crbegin();
195 return m_elts.crend();
212 [[nodiscard]] constexpr std::size_t
size() const noexcept
214 return m_elts.size();
233 [[nodiscard]] constexpr
auto ssize() const noexcept
235 return static_cast<std::ptrdiff_t
>(
size());
241 [[nodiscard]] constexpr
auto const &
front() const noexcept
243 return m_elts.front();
249 [[nodiscard]] constexpr
auto const &
back() const noexcept
251 return m_elts.back();
264 void check_dims(std::string_view data,
sl loc)
266 auto sz{std::size(data)};
267 if (sz < DIMENSIONS * 2)
268 throw conversion_error{
270 "Trying to parse a {}-dimensional array out of '{}'.", DIMENSIONS,
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{
289 "Expecting {}-dimensional array, but found {}.", DIMENSIONS, i),
291 if (data.at(DIMENSIONS) ==
'{')
292 throw conversion_error{
294 "Tried to parse {}-dimensional array from array data that has more "
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};
308 [[nodiscard]] std::size_t
309 parse_field_end(std::string_view data, std::size_t here,
sl loc)
const
311 auto const sz{std::size(data)};
313 switch (data.at(here))
318 throw conversion_error{
"Array looks truncated.", loc};
319 switch (data.at(here))
322 throw conversion_error{
"Array contains double separator.", loc};
324 throw conversion_error{
"Array contains trailing separator.", loc};
330 throw conversion_error{
332 "Unexpected character in array: {} where separator or closing "
334 static_cast<unsigned>(
static_cast<unsigned char>(data.at(here)))),
346 [[nodiscard]] constexpr std::size_t
347 estimate_elements(std::string_view data)
const noexcept
351 auto const separators{
352 std::count(std::begin(data), std::end(data), SEPARATOR)};
356 return static_cast<std::size_t
>(separators + 1);
359 template<encoding_group ENC>
void parse(std::string_view data,
sl loc)
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);
366 m_elts.reserve(estimate_elements(data));
372 std::size_t know_extents_from{DIMENSIONS};
380 constexpr std::size_t outer{std::size_t{0u} - std::size_t{1u}};
385 std::size_t dim{outer};
389 std::array<std::size_t, DIMENSIONS> extents{};
396 if (data.at(here) ==
'{')
401 if (know_extents_from != DIMENSIONS)
402 throw conversion_error{
403 "Array text representation closed and reopened its outside "
411 if (dim >= (DIMENSIONS - 1))
412 throw conversion_error{
413 "Array seems to have inconsistent number of dimensions.", loc};
418 extents.at(dim) = 0u;
421 else if (data.at(here) ==
'}')
424 throw conversion_error{
"Array has spurious '}'.", loc};
425 if (dim < know_extents_from)
429 m_extents.at(dim) = extents.at(dim);
430 know_extents_from = dim;
434 if (extents.at(dim) != m_extents.at(dim))
435 throw conversion_error{
436 "Rows in array have inconsistent sizes.", loc};
442 here = parse_field_end(data, here, loc);
448 if (dim != DIMENSIONS - 1)
449 throw conversion_error{
450 "Malformed array: found element where sub-array was expected.",
452 assert(dim != outer);
455 switch (data.at(here))
458 throw conversion_error{
"Unexpected zero byte in array.", loc};
459 case ',':
throw conversion_error{
"Array contains empty field.", loc};
471 pqxx::internal::scan_double_quoted_string<ENC>(data, here, loc);
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));
483 end = pqxx::internal::scan_unquoted_string<ENC, SEPARATOR, '}'>(
485 std::string_view
const field{
486 std::string_view{std::data(data) + here,
end - here}};
489 if constexpr (has_null<ELEMENT>())
490 m_elts.emplace_back(
make_null<ELEMENT>());
492 throw unexpected_null{
494 "Array contains a null {}. Consider making it an array of "
495 "std::optional<{}> instead.",
496 name_type<ELEMENT>(), name_type<ELEMENT>()),
500 m_elts.emplace_back(from_string<ELEMENT>(field, c));
505 here = parse_field_end(data, here, loc);
510 throw conversion_error{
"Malformed array; may be truncated.", loc};
511 assert(know_extents_from == 0);
518 void init_factors() noexcept
520 std::size_t factor{1};
521 for (std::size_t dim{DIMENSIONS - 1}; dim > 0; --dim)
523 factor *= m_extents.at(dim);
524 m_factors.at(dim - 1) = factor;
529 template<
typename... INDEX>
530 [[nodiscard]] std::size_t locate(INDEX... index)
const noexcept
533 sizeof...(index) == DIMENSIONS,
534 "Indexing array with wrong number of dimensions.");
535 return add_index(index...);
538 template<
typename OUTER,
typename... INDEX>
539 [[nodiscard]] constexpr std::size_t
540 add_index(OUTER outer, INDEX... indexes)
const noexcept
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)
550 static_assert(
sizeof...(indexes) < DIMENSIONS);
552 constexpr
auto dimension{DIMENSIONS - (
sizeof...(indexes) + 1)};
553 static_assert(dimension < DIMENSIONS);
554 return first * m_factors.at(dimension) + add_index(indexes...);
561 template<
typename OUTER, std::integral... INDEX>
562 constexpr
void check_bounds(OUTER outer, INDEX... indexes)
const
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);
568 constexpr
auto dimension{DIMENSIONS - (
sizeof...(indexes) + 1)};
569 static_assert(dimension < DIMENSIONS);
570 if (std::cmp_greater_equal(first, m_extents.at(dimension)))
573 "Array index for dimension {} is out of bounds: {} >= {}.",
574 dimension, first, m_extents.at(dimension)),
578 if constexpr (
sizeof...(indexes) > 0)
579 check_bounds(indexes...);
583 std::vector<ELEMENT> m_elts;
586 std::
array<std::
size_t, DIMENSIONS> m_extents;
596 std::
array<std::
size_t, DIMENSIONS - 1> m_factors;
603 conversion_context m_ctx;
611 template<typename ELEMENT, std::
size_t DIMENSIONS>
616 using elt_type = std::remove_cvref_t<ELEMENT>;
618 static constexpr
zview s_null{
"NULL"};
623 static std::string_view
628 assert(buf[len - 1] ==
'\0');
629 return {std::data(buf), len - 1};
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) {
645 (std::size(s_null) + 1) :
649 return 3 + std::accumulate(
650 std::begin(value), std::end(value), std::size_t{},
651 [](std::size_t acc, elt_type
const &elt) {
657 std::size_t
const elt_size{
660 return acc + 2 * elt_size + 2;
672 template<
typename ELEMENT, std::
size_t DIMENSIONS>
674 :
no_null<array<ELEMENT, DIMENSIONS, array_separator<ELEMENT>>>
681 template<
typename CONT>
685 template<
typename CONT>
693 template<pqxx::
internal::nonbinary_container CONT>
703 template<pqxx::
internal::nonbinary_container CONT>
710 auto const arr = pqxx::from_string<array_type>(text, c);
711 return CONT{arr.cbegin(), arr.cend()};
760 [[deprecated(
"Use pqxx::array instead.")]]
771 std::pair<juncture, std::string>
get_next(
sl loc = sl::current())
773 return (this->*m_impl)(loc);
777 std::string_view m_input;
780 std::size_t m_pos = 0u;
788 using implementation =
795 implementation m_impl;
798 template<encoding_group>
799 std::pair<juncture, std::string> parse_array_step(
sl loc);
801 template<encoding_group>
803 template<encoding_group>
804 [[nodiscard]] std::string
806 template<encoding_group>
808 template<encoding_group>
809 [[nodiscard]] std::string_view
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
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