14 #if !defined(PQXX_HEADER_PRE)
15 # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
22 #include <type_traits>
26 #include "pqxx/connection.hxx"
27 #include "pqxx/internal/array-composite.hxx"
28 #include "pqxx/internal/encoding_group.hxx"
29 #include "pqxx/internal/encodings.hxx"
53 typename ELEMENT, std::size_t DIMENSIONS = 1u,
54 char SEPARATOR = array_separator<ELEMENT>>
75 constexpr std::size_t
dimensions() noexcept {
return DIMENSIONS; }
82 std::array<std::size_t, DIMENSIONS>
const &
sizes() noexcept
87 template<
typename... INDEX> ELEMENT
const &at(INDEX... index)
const
89 static_assert(
sizeof...(index) == DIMENSIONS);
90 check_bounds(index...);
91 return m_elts.at(locate(index...));
103 template<
typename... INDEX> ELEMENT
const &
operator[](INDEX... index)
const
105 static_assert(
sizeof...(index) == DIMENSIONS);
106 return m_elts[locate(index...)];
115 constexpr
auto cbegin() const noexcept {
return m_elts.cbegin(); }
117 constexpr
auto cend() const noexcept {
return m_elts.cend(); }
119 constexpr
auto crbegin() const noexcept {
return m_elts.crbegin(); }
121 constexpr
auto crend() const noexcept {
return m_elts.crend(); }
127 constexpr std::size_t
size() const noexcept {
return m_elts.size(); }
145 constexpr
auto ssize() const noexcept
147 return static_cast<std::ptrdiff_t
>(
size());
153 constexpr
auto front() const noexcept {
return m_elts.front(); }
158 constexpr
auto back() const noexcept {
return m_elts.back(); }
170 void check_dims(std::string_view data)
172 auto sz{std::size(data)};
173 if (sz < DIMENSIONS * 2)
175 "Trying to parse a ", DIMENSIONS,
"-dimensional array out of '", data,
188 throw conversion_error{
"Malformed array: does not start with '{'."};
189 for (std::size_t i{0}; i < DIMENSIONS; ++i)
192 "Expecting ", DIMENSIONS,
"-dimensional array, but found ", i,
".")};
193 if (data[DIMENSIONS] ==
'{')
195 "Tried to parse ", DIMENSIONS,
196 "-dimensional array from array data that has more dimensions.")};
197 for (std::size_t i{0}; i < DIMENSIONS; ++i)
198 if (data[sz - 1 - i] !=
'}')
199 throw conversion_error{
200 "Malformed array: does not end in the right number of '}'."};
205 friend class ::pqxx::field;
207 array(std::string_view data, pqxx::internal::encoding_group enc)
209 using group = pqxx::internal::encoding_group;
212 case group::MONOBYTE: parse<group::MONOBYTE>(data);
break;
213 case group::BIG5: parse<group::BIG5>(data);
break;
214 case group::EUC_CN: parse<group::EUC_CN>(data);
break;
215 case group::EUC_JP: parse<group::EUC_JP>(data);
break;
216 case group::EUC_KR: parse<group::EUC_KR>(data);
break;
217 case group::EUC_TW: parse<group::EUC_TW>(data);
break;
218 case group::GB18030: parse<group::GB18030>(data);
break;
219 case group::GBK: parse<group::GBK>(data);
break;
220 case group::JOHAB: parse<group::JOHAB>(data);
break;
221 case group::MULE_INTERNAL: parse<group::MULE_INTERNAL>(data);
break;
222 case group::SJIS: parse<group::SJIS>(data);
break;
223 case group::UHC: parse<group::UHC>(data);
break;
224 case group::UTF8: parse<group::UTF8>(data);
break;
225 default: PQXX_UNREACHABLE;
break;
233 std::size_t parse_field_end(std::string_view data, std::size_t here)
const
235 auto const sz{std::size(data)};
242 throw conversion_error{
"Array looks truncated."};
246 throw conversion_error{
"Array contains double separator."};
247 case '}':
throw conversion_error{
"Array contains trailing separator."};
254 "Unexpected character in array: ",
255 static_cast<unsigned>(static_cast<unsigned char>(data[here])),
256 " where separator or closing brace expected.")};
267 constexpr std::size_t estimate_elements(std::string_view data)
const noexcept
272 auto const separators{
273 std::count(std::begin(data), std::end(data), SEPARATOR)};
277 return static_cast<std::size_t
>(separators + 1);
280 template<pqxx::
internal::encoding_group ENC>
281 void parse(std::string_view data)
283 static_assert(DIMENSIONS > 0u,
"Can't create a zero-dimensional array.");
284 auto const sz{std::size(data)};
287 m_elts.reserve(estimate_elements(data));
293 std::size_t know_extents_from{DIMENSIONS};
301 constexpr std::size_t outer{std::size_t{0u} - std::size_t{1u}};
306 std::size_t dim{outer};
310 std::array<std::size_t, DIMENSIONS> extents{};
314 PQXX_ASSUME(here <= sz);
317 if (data[here] ==
'{')
322 if (know_extents_from != DIMENSIONS)
323 throw conversion_error{
324 "Array text representation closed and reopened its outside "
327 PQXX_ASSUME(here == 0);
331 if (dim >= (DIMENSIONS - 1))
332 throw conversion_error{
333 "Array seems to have inconsistent number of dimensions."};
341 else if (data[here] ==
'}')
344 throw conversion_error{
"Array has spurious '}'."};
345 if (dim < know_extents_from)
349 m_extents[dim] = extents[dim];
350 know_extents_from = dim;
354 if (extents[dim] != m_extents[dim])
355 throw conversion_error{
"Rows in array have inconsistent sizes."};
361 here = parse_field_end(data, here);
367 if (dim != DIMENSIONS - 1)
368 throw conversion_error{
369 "Malformed array: found element where sub-array was expected."};
370 assert(dim != outer);
375 case '\0':
throw conversion_error{
"Unexpected zero byte in array."};
376 case ',':
throw conversion_error{
"Array contains empty field."};
387 end = pqxx::internal::scan_double_quoted_string<ENC>(
388 std::data(data), std::size(data), here);
390 std::string
const buf{
391 pqxx::internal::parse_double_quoted_string<ENC>(
392 std::data(data), end, here)};
393 m_elts.emplace_back(from_string<ELEMENT>(buf));
400 end = pqxx::internal::scan_unquoted_string<ENC, SEPARATOR, '}'>(
401 std::data(data), std::size(data), here);
402 std::string_view
const field{
403 std::string_view{std::data(data) + here, end - here}};
406 if constexpr (nullness<ELEMENT>::has_null)
407 m_elts.emplace_back(nullness<ELEMENT>::null());
410 "Array contains a null ", type_name<ELEMENT>,
411 ". Consider making it an array of std::optional<",
412 type_name<ELEMENT>,
"> instead.")};
415 m_elts.emplace_back(from_string<ELEMENT>(field));
419 PQXX_ASSUME(here <= sz);
420 here = parse_field_end(data, here);
425 throw conversion_error{
"Malformed array; may be truncated."};
426 assert(know_extents_from == 0);
427 PQXX_ASSUME(know_extents_from == 0);
433 void init_factors() noexcept
435 std::size_t factor{1};
436 for (std::size_t dim{DIMENSIONS - 1}; dim > 0; --dim)
438 factor *= m_extents[dim];
439 m_factors[dim - 1] = factor;
444 template<
typename... INDEX> std::size_t locate(INDEX... index) const noexcept
447 sizeof...(index) == DIMENSIONS,
448 "Indexing array with wrong number of dimensions.");
449 return add_index(index...);
452 template<
typename OUTER,
typename... INDEX>
453 constexpr std::size_t add_index(OUTER outer, INDEX... indexes) const noexcept
455 std::size_t
const first{
check_cast<std::size_t>(outer,
"array index"sv)};
456 if constexpr (
sizeof...(indexes) == 0)
462 static_assert(
sizeof...(indexes) < DIMENSIONS);
464 constexpr
auto dimension{DIMENSIONS - (
sizeof...(indexes) + 1)};
465 static_assert(dimension < DIMENSIONS);
466 return first * m_factors[dimension] + add_index(indexes...);
473 template<
typename OUTER,
typename... INDEX>
474 constexpr
void check_bounds(OUTER outer, INDEX... indexes)
const
476 std::size_t
const first{
check_cast<std::size_t>(outer,
"array index"sv)};
477 static_assert(
sizeof...(indexes) < DIMENSIONS);
479 constexpr
auto dimension{DIMENSIONS - (
sizeof...(indexes) + 1)};
480 static_assert(dimension < DIMENSIONS);
481 if (first >= m_extents[dimension])
483 "Array index for dimension ", dimension,
" is out of bounds: ", first,
484 " >= ", m_extents[dimension])};
487 if constexpr (
sizeof...(indexes) > 0)
488 check_bounds(indexes...);
492 std::vector<ELEMENT> m_elts;
495 std::
array<std::
size_t, DIMENSIONS> m_extents;
505 std::
array<std::
size_t, DIMENSIONS - 1> m_factors;
553 explicit array_parser(
554 std::string_view input,
555 internal::encoding_group = internal::encoding_group::MONOBYTE);
564 std::pair<juncture, std::string>
get_next() {
return (this->*m_impl)(); }
567 std::string_view m_input;
570 std::size_t m_pos = 0u;
578 using implementation = std::pair<juncture, std::string> (array_parser::*)();
581 static implementation
582 specialize_for_encoding(pqxx::internal::encoding_group enc);
585 implementation m_impl;
588 template<pqxx::
internal::encoding_group>
589 std::pair<juncture, std::string> parse_array_step();
591 template<pqxx::
internal::encoding_group>
592 std::string::size_type scan_double_quoted_string()
const;
593 template<pqxx::
internal::encoding_group>
594 std::string parse_double_quoted_string(std::string::size_type end)
const;
595 template<pqxx::
internal::encoding_group>
596 std::string::size_type scan_unquoted_string()
const;
597 template<pqxx::
internal::encoding_group>
598 std::string_view parse_unquoted_string(std::string::size_type end)
const;
600 template<pqxx::
internal::encoding_group>
601 std::string::size_type scan_glyph(std::string::size_type pos)
const;
602 template<pqxx::
internal::encoding_group>
603 std::string::size_type
604 scan_glyph(std::string::size_type pos, std::string::size_type end)
const;
std::string concat(TYPE...item)
Efficiently combine a bunch of items into one big string.
Definition: concat.hxx:31
constexpr std::size_t size() const noexcept
Number of elements in the array.
Definition: array.hxx:127
TO check_cast(FROM value, std::string_view description)
Cast a numeric value to another type, or throw if it underflows/overflows.
Definition: util.hxx:153
constexpr auto crend() const noexcept
Return end point of reverse iteration.
Definition: array.hxx:121
array(std::string_view data, connection const &cx)
Parse an SQL array, read as text from a pqxx::result or stream.
Definition: array.hxx:68
Low-level parser for C++ arrays.
Definition: array.hxx:530
constexpr auto back() const noexcept
Refer to the last element, if any.
Definition: array.hxx:158
constexpr auto front() const noexcept
Refer to the first element, if any.
Definition: array.hxx:153
constexpr auto crbegin() const noexcept
Begin reverse iteration.
Definition: array.hxx:119
constexpr auto cend() const noexcept
Return end point of iteration.
Definition: array.hxx:117
juncture
What's the latest thing found in the array?
Definition: array.hxx:534
The home of all libpqxx classes, functions, templates, etc.
Definition: array.cxx:26
constexpr auto cbegin() const noexcept
Begin iteration of individual elements.
Definition: array.hxx:115
constexpr std::size_t dimensions() noexcept
How many dimensions does this array have?
Definition: array.hxx:75
constexpr auto ssize() const noexcept
Number of elements in the array (as a signed number).
Definition: array.hxx:145
pqxx::internal::encoding_group enc_group(std::string_view encoding_name)
Convert libpq encoding name to its libpqxx encoding group.
Definition: encodings.cxx:35
Connection to a database.
Definition: connection.hxx:278
std::pair< juncture, std::string > get_next()
Parse the next step in the array.
Definition: array.hxx:564
std::array< std::size_t, DIMENSIONS > const & sizes() noexcept
Return the sizes of this array in each of its dimensions.
Definition: array.hxx:82
int encoding_id() const
Get the connection's encoding, as a PostgreSQL-defined code.
Definition: connection.cxx:1160
ELEMENT const & operator[](INDEX...index) const
Access element (without bounds check).
Definition: array.hxx:103
An SQL array received from the database.
Definition: array.hxx:55