libpqxx  7.8.0
range.hxx
1 #ifndef PQXX_H_RANGE
2 #define PQXX_H_RANGE
3 
4 #if !defined(PQXX_HEADER_PRE)
5 # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
6 #endif
7 
8 #include <utility>
9 #include <variant>
10 
11 #include "pqxx/internal/array-composite.hxx"
12 #include "pqxx/internal/concat.hxx"
13 
14 namespace pqxx
15 {
17 
23 struct no_bound
24 {
25  template<typename TYPE>
26  constexpr bool extends_down_to(TYPE const &) const noexcept
27  {
28  return true;
29  }
30  template<typename TYPE>
31  constexpr bool extends_up_to(TYPE const &) const noexcept
32  {
33  return true;
34  }
35 };
36 
37 
39 
42 template<typename TYPE> class inclusive_bound
43 {
44 // (Putting private section first to work around bug in gcc < 10: see #665.)
45 private:
46  TYPE m_value;
47 
48 public:
49  inclusive_bound() = delete;
50  constexpr explicit inclusive_bound(TYPE const &value) : m_value{value}
51  {
52  if (is_null(value))
53  throw argument_error{"Got null value as an inclusive range bound."};
54  }
55 
56  [[nodiscard]] constexpr TYPE const &get() const &noexcept { return m_value; }
57 
59  [[nodiscard]] constexpr bool extends_down_to(TYPE const &value) const
60  noexcept(noexcept(value < m_value))
61  {
62  return not(value < m_value);
63  }
64 
66  [[nodiscard]] constexpr bool extends_up_to(TYPE const &value) const
67  noexcept(noexcept(value < m_value))
68  {
69  return not(m_value < value);
70  }
71 };
72 
73 
75 
78 template<typename TYPE> class exclusive_bound
79 {
80 // (Putting private section first to work around bug in gcc < 10: see #665.)
81 private:
82  TYPE m_value;
83 
84 public:
85  exclusive_bound() = delete;
86  constexpr explicit exclusive_bound(TYPE const &value) : m_value{value}
87  {
88  if (is_null(value))
89  throw argument_error{"Got null value as an exclusive range bound."};
90  }
91 
92  [[nodiscard]] constexpr TYPE const &get() const &noexcept { return m_value; }
93 
95  [[nodiscard]] constexpr bool extends_down_to(TYPE const &value) const
96  noexcept(noexcept(m_value < value))
97  {
98  return m_value < value;
99  }
100 
102  [[nodiscard]] constexpr bool extends_up_to(TYPE const &value) const
103  noexcept(noexcept(value < m_value))
104  {
105  return value < m_value;
106  }
107 };
108 
109 
111 
114 template<typename TYPE> class range_bound
115 {
116 // (Putting private section first to work around bug in gcc < 10: see #665.)
117 private:
118  std::variant<no_bound, inclusive_bound<TYPE>, exclusive_bound<TYPE>> m_bound;
119 
120 public:
121  range_bound() = delete;
122  constexpr range_bound(no_bound) noexcept : m_bound{} {}
123 
124  constexpr range_bound(inclusive_bound<TYPE> const &bound) noexcept(
125  noexcept(inclusive_bound<TYPE>{bound})) :
126  m_bound{bound}
127  {}
128 
129  constexpr range_bound(exclusive_bound<TYPE> const &bound) noexcept(
130  noexcept(exclusive_bound{bound})) :
131  m_bound{bound}
132  {}
133 
134  constexpr range_bound(range_bound const &) noexcept(
135  noexcept(inclusive_bound<TYPE>{
136  std::declval<inclusive_bound<TYPE> const &>()})
137  and noexcept(exclusive_bound<TYPE>{
138  std::declval<exclusive_bound<TYPE> const &>()})) = default;
139 
140  constexpr range_bound(range_bound &&) = default;
141 
142  constexpr bool operator==(range_bound const &rhs) const
143  noexcept(noexcept(*this->value() == *rhs.value()))
144  {
145  if (this->is_limited())
146  return (
147  rhs.is_limited() and (this->is_inclusive() == rhs.is_inclusive()) and
148  (*this->value() == *rhs.value()));
149  else
150  return not rhs.is_limited();
151  }
152 
153  constexpr bool operator!=(range_bound const &rhs) const
154  noexcept(noexcept(*this == rhs))
155  {
156  return not(*this == rhs);
157  }
158  range_bound &operator=(range_bound const &) = default;
160 
162  constexpr bool is_limited() const noexcept
163  {
164  return not std::holds_alternative<no_bound>(m_bound);
165  }
166 
168  constexpr bool is_inclusive() const noexcept
169  {
170  return std::holds_alternative<inclusive_bound<TYPE>>(m_bound);
171  }
172 
174  constexpr bool is_exclusive() const noexcept
175  {
176  return std::holds_alternative<exclusive_bound<TYPE>>(m_bound);
177  }
178 
180  constexpr bool extends_down_to(TYPE const &value) const
181  {
182  return std::visit(
183  [&value](auto const &bound) noexcept(noexcept(bound.extends_down_to(
184  value))) { return bound.extends_down_to(value); },
185  m_bound);
186  }
187 
189  constexpr bool extends_up_to(TYPE const &value) const
190  {
191  return std::visit(
192  [&value](auto const &bound) noexcept(noexcept(
193  bound.extends_up_to(value))) { return bound.extends_up_to(value); },
194  m_bound);
195  }
196 
198  [[nodiscard]] constexpr TYPE const *value() const &noexcept
199  {
200  return std::visit(
201  [](auto const &bound) noexcept {
202  using bound_t = std::decay_t<decltype(bound)>;
203  if constexpr (std::is_same_v<bound_t, no_bound>)
204  return static_cast<TYPE const *>(nullptr);
205  else
206  return &bound.get();
207  },
208  m_bound);
209  }
210 };
211 
212 
213 // C++20: Concepts for comparisons, construction, etc.
215 
233 template<typename TYPE> class range
234 {
235 // (Putting private section first to work around bug in gcc < 10: see #665.)
236 private:
237  range_bound<TYPE> m_lower, m_upper;
238 
239 public:
241 
245  constexpr range(range_bound<TYPE> lower, range_bound<TYPE> upper) :
246  m_lower{lower}, m_upper{upper}
247  {
248  if (
249  lower.is_limited() and upper.is_limited() and
250  (*upper.value() < *lower.value()))
251  throw range_error{internal::concat(
252  "Range's lower bound (", *lower.value(),
253  ") is greater than its upper bound (", *upper.value(), ").")};
254  }
255 
257 
260  constexpr range() noexcept(noexcept(exclusive_bound<TYPE>{TYPE{}})) :
261  m_lower{exclusive_bound<TYPE>{TYPE{}}},
262  m_upper{exclusive_bound<TYPE>{TYPE{}}}
263  {}
264 
265  constexpr bool operator==(range const &rhs) const
266  noexcept(noexcept(this->lower_bound() == rhs.lower_bound()) and noexcept(
267  this->upper_bound() == rhs.upper_bound()) and noexcept(this->empty()))
268  {
269  return (this->lower_bound() == rhs.lower_bound() and
270  this->upper_bound() == rhs.upper_bound()) or
271  (this->empty() and rhs.empty());
272  }
273 
274  constexpr bool operator!=(range const &rhs) const
275  noexcept(noexcept(*this == rhs))
276  {
277  return not(*this == rhs);
278  }
279 
280  range(range const &) = default;
281  range(range &&) = default;
282  range &operator=(range const &) = default;
283  range &operator=(range &&) = default;
284 
286 
294  constexpr bool empty() const
295  noexcept(noexcept(m_lower.is_exclusive()) and noexcept(
296  m_lower.is_limited()) and noexcept(*m_lower.value() < *m_upper.value()))
297  {
298  return (m_lower.is_exclusive() or m_upper.is_exclusive()) and
299  m_lower.is_limited() and m_upper.is_limited() and
300  not(*m_lower.value() < *m_upper.value());
301  }
302 
304  constexpr bool contains(TYPE value) const noexcept(noexcept(
305  m_lower.extends_down_to(value)) and noexcept(m_upper.extends_up_to(value)))
306  {
307  return m_lower.extends_down_to(value) and m_upper.extends_up_to(value);
308  }
309 
311 
314  constexpr bool contains(range<TYPE> const &other) const
315  noexcept(noexcept((*this & other) == other))
316  {
317  return (*this & other) == other;
318  }
319 
320  [[nodiscard]] constexpr range_bound<TYPE> const &
321  lower_bound() const &noexcept
322  {
323  return m_lower;
324  }
325  [[nodiscard]] constexpr range_bound<TYPE> const &
326  upper_bound() const &noexcept
327  {
328  return m_upper;
329  }
330 
332 
334  constexpr range operator&(range const &other) const
335  {
336  range_bound<TYPE> lower{no_bound{}};
337  if (not this->lower_bound().is_limited())
338  lower = other.lower_bound();
339  else if (not other.lower_bound().is_limited())
340  lower = this->lower_bound();
341  else if (*this->lower_bound().value() < *other.lower_bound().value())
342  lower = other.lower_bound();
343  else if (*other.lower_bound().value() < *this->lower_bound().value())
344  lower = this->lower_bound();
345  else if (this->lower_bound().is_exclusive())
346  lower = this->lower_bound();
347  else
348  lower = other.lower_bound();
349 
350  range_bound<TYPE> upper{no_bound{}};
351  if (not this->upper_bound().is_limited())
352  upper = other.upper_bound();
353  else if (not other.upper_bound().is_limited())
354  upper = this->upper_bound();
355  else if (*other.upper_bound().value() < *this->upper_bound().value())
356  upper = other.upper_bound();
357  else if (*this->upper_bound().value() < *other.upper_bound().value())
358  upper = this->upper_bound();
359  else if (this->upper_bound().is_exclusive())
360  upper = this->upper_bound();
361  else
362  upper = other.upper_bound();
363 
364  if (
365  lower.is_limited() and upper.is_limited() and
366  (*upper.value() < *lower.value()))
367  return {};
368  else
369  return {lower, upper};
370  }
371 
373  template<typename DEST> operator range<DEST>() const
374  {
375  range_bound<DEST> lower{no_bound{}}, upper{no_bound{}};
376  if (lower_bound().is_inclusive())
377  lower = inclusive_bound<DEST>{*lower_bound().value()};
378  else if (lower_bound().is_exclusive())
379  lower = exclusive_bound<DEST>{*lower_bound().value()};
380 
381  if (upper_bound().is_inclusive())
382  upper = inclusive_bound<DEST>{*upper_bound().value()};
383  else if (upper_bound().is_exclusive())
384  upper = exclusive_bound<DEST>{*upper_bound().value()};
385 
386  return {lower, upper};
387  }
388 };
389 
390 
392 
395 template<typename TYPE> struct string_traits<range<TYPE>>
396 {
397  [[nodiscard]] static inline zview
398  to_buf(char *begin, char *end, range<TYPE> const &value)
399  {
400  return generic_to_buf(begin, end, value);
401  }
402 
403  static inline char *
404  into_buf(char *begin, char *end, range<TYPE> const &value)
405  {
406  if (value.empty())
407  {
408  if ((end - begin) <= internal::ssize(s_empty))
409  throw conversion_overrun{s_overrun.c_str()};
410  char *here = begin + s_empty.copy(begin, std::size(s_empty));
411  *here++ = '\0';
412  return here;
413  }
414  else
415  {
416  if (end - begin < 4)
417  throw conversion_overrun{s_overrun.c_str()};
418  char *here = begin;
419  *here++ =
420  (static_cast<char>(value.lower_bound().is_inclusive() ? '[' : '('));
421  TYPE const *lower{value.lower_bound().value()};
422  // Convert bound (but go back to overwrite that trailing zero).
423  if (lower != nullptr)
424  here = string_traits<TYPE>::into_buf(here, end, *lower) - 1;
425  *here++ = ',';
426  TYPE const *upper{value.upper_bound().value()};
427  // Convert bound (but go back to overwrite that trailing zero).
428  if (upper != nullptr)
429  here = string_traits<TYPE>::into_buf(here, end, *upper) - 1;
430  if ((end - here) < 2)
431  throw conversion_overrun{s_overrun.c_str()};
432  *here++ =
433  static_cast<char>(value.upper_bound().is_inclusive() ? ']' : ')');
434  *here++ = '\0';
435  return here;
436  }
437  }
438 
439  [[nodiscard]] static inline range<TYPE> from_string(std::string_view text)
440  {
441  if (std::size(text) < 3)
442  throw pqxx::conversion_error{err_bad_input(text)};
443  bool left_inc{false};
444  switch (text[0])
445  {
446  case '[': left_inc = true; break;
447 
448  case '(': break;
449 
450  case 'e':
451  case 'E':
452  if (
453  (std::size(text) != std::size(s_empty)) or
454  (text[1] != 'm' and text[1] != 'M') or
455  (text[2] != 'p' and text[2] != 'P') or
456  (text[3] != 't' and text[3] != 'T') or
457  (text[4] != 'y' and text[4] != 'Y'))
458  throw pqxx::conversion_error{err_bad_input(text)};
459  return {};
460  break;
461 
462  default: throw pqxx::conversion_error{err_bad_input(text)};
463  }
464 
465  // The field parser uses this to track which field it's parsing, and
466  // when not to expect a field separator.
467  std::size_t index{0};
468  // The last field we expect to see.
469  static constexpr std::size_t last{1};
470  // Current parsing position. We skip the opening parenthesis or bracket.
471  std::size_t pos{1};
472  // The string may leave out either bound to indicate that it's unlimited.
473  std::optional<TYPE> lower, upper;
474  // We reuse the same field parser we use for composite values and arrays.
475  auto const field_parser{
476  pqxx::internal::specialize_parse_composite_field<std::optional<TYPE>>(
477  pqxx::internal::encoding_group::UTF8)};
478  field_parser(index, text, pos, lower, last);
479  field_parser(index, text, pos, upper, last);
480 
481  // We need one more character: the closing parenthesis or bracket.
482  if (pos != std::size(text))
483  throw pqxx::conversion_error{err_bad_input(text)};
484  char const closing{text[pos - 1]};
485  if (closing != ')' and closing != ']')
486  throw pqxx::conversion_error{err_bad_input(text)};
487  bool const right_inc{closing == ']'};
488 
489  range_bound<TYPE> lower_bound{no_bound{}}, upper_bound{no_bound{}};
490  if (lower)
491  {
492  if (left_inc)
493  lower_bound = inclusive_bound{*lower};
494  else
495  lower_bound = exclusive_bound{*lower};
496  }
497  if (upper)
498  {
499  if (right_inc)
500  upper_bound = inclusive_bound{*upper};
501  else
502  upper_bound = exclusive_bound{*upper};
503  }
504 
505  return {lower_bound, upper_bound};
506  }
507 
508  [[nodiscard]] static inline constexpr std::size_t
509  size_buffer(range<TYPE> const &value) noexcept
510  {
511  TYPE const *lower{value.lower_bound().value()},
512  *upper{value.upper_bound().value()};
513  std::size_t const lsz{
514  lower == nullptr ? 0 : string_traits<TYPE>::size_buffer(*lower) - 1},
515  usz{upper == nullptr ? 0 : string_traits<TYPE>::size_buffer(*upper) - 1};
516 
517  if (value.empty())
518  return std::size(s_empty) + 1;
519  else
520  return 1 + lsz + 1 + usz + 2;
521  }
522 
523 private:
524  static constexpr zview s_empty{"empty"_zv};
525  static constexpr auto s_overrun{"Not enough space in buffer for range."_zv};
526 
528  static std::string err_bad_input(std::string_view text)
529  {
530  return internal::concat("Invalid range input: '", text, "'");
531  }
532 };
533 
534 
536 template<typename TYPE> struct nullness<range<TYPE>> : no_null<range<TYPE>>
537 {};
538 } // namespace pqxx
539 #endif
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:33
constexpr bool is_null(TYPE const &value) noexcept
Is value null?
Definition: strconv.hxx:508
zview generic_to_buf(char *begin, char *end, TYPE const &value)
Implement string_traits<TYPE>::to_buf by calling into_buf.
Definition: strconv.hxx:580
Invalid argument passed to libpqxx, similar to std::invalid_argument.
Definition: except.hxx:253
Value conversion failed, e.g. when converting "Hello" to int.
Definition: except.hxx:268
Could not convert value to string: not enough buffer space.
Definition: except.hxx:294
Something is out of range, similar to std::out_of_range.
Definition: except.hxx:305
An unlimited boundary value to a pqxx::range.
Definition: range.hxx:24
constexpr bool extends_down_to(TYPE const &) const noexcept
Definition: range.hxx:26
constexpr bool extends_up_to(TYPE const &) const noexcept
Definition: range.hxx:31
An inclusive boundary value to a pqxx::range.
Definition: range.hxx:43
constexpr inclusive_bound(TYPE const &value)
Definition: range.hxx:50
constexpr bool extends_down_to(TYPE const &value) const noexcept(noexcept(value< m_value))
Would this bound, as a lower bound, include value?
Definition: range.hxx:59
constexpr TYPE const & get() const &noexcept
Definition: range.hxx:56
constexpr bool extends_up_to(TYPE const &value) const noexcept(noexcept(value< m_value))
Would this bound, as an upper bound, include value?
Definition: range.hxx:66
An exclusive boundary value to a pqxx::range.
Definition: range.hxx:79
constexpr bool extends_up_to(TYPE const &value) const noexcept(noexcept(value< m_value))
Would this bound, as an upper bound, include value?
Definition: range.hxx:102
constexpr exclusive_bound(TYPE const &value)
Definition: range.hxx:86
constexpr bool extends_down_to(TYPE const &value) const noexcept(noexcept(m_value< value))
Would this bound, as a lower bound, include value?
Definition: range.hxx:95
constexpr TYPE const & get() const &noexcept
Definition: range.hxx:92
A range boundary value.
Definition: range.hxx:115
constexpr bool extends_up_to(TYPE const &value) const
Would this bound, as an upper bound, include value?
Definition: range.hxx:189
constexpr range_bound(range_bound const &) noexcept(noexcept(inclusive_bound< TYPE >{ std::declval< inclusive_bound< TYPE > const & >()}) and noexcept(exclusive_bound< TYPE >{ std::declval< exclusive_bound< TYPE > const & >()}))=default
constexpr bool is_exclusive() const noexcept
Is this boundary an exclusive one?
Definition: range.hxx:174
constexpr range_bound(inclusive_bound< TYPE > const &bound) noexcept(noexcept(inclusive_bound< TYPE >{bound}))
Definition: range.hxx:124
constexpr bool is_limited() const noexcept
Is this a finite bound?
Definition: range.hxx:162
constexpr bool operator!=(range_bound const &rhs) const noexcept(noexcept(*this==rhs))
Definition: range.hxx:153
constexpr bool extends_down_to(TYPE const &value) const
Would this bound, as a lower bound, include value?
Definition: range.hxx:180
range_bound & operator=(range_bound &&)=default
range_bound()=delete
constexpr range_bound(no_bound) noexcept
Definition: range.hxx:122
constexpr TYPE const * value() const &noexcept
Return bound value, or nullptr if it's not limited.
Definition: range.hxx:198
constexpr bool operator==(range_bound const &rhs) const noexcept(noexcept(*this->value()== *rhs.value()))
Definition: range.hxx:142
constexpr range_bound(exclusive_bound< TYPE > const &bound) noexcept(noexcept(exclusive_bound{bound}))
Definition: range.hxx:129
constexpr bool is_inclusive() const noexcept
Is this boundary an inclusive one?
Definition: range.hxx:168
constexpr range_bound(range_bound &&)=default
range_bound & operator=(range_bound const &)=default
A C++ equivalent to PostgreSQL's range types.
Definition: range.hxx:234
range & operator=(range const &)=default
constexpr range operator&(range const &other) const
Intersection of two ranges.
Definition: range.hxx:334
constexpr bool operator==(range const &rhs) const noexcept(noexcept(this->lower_bound()==rhs.lower_bound()) and noexcept(this->upper_bound()==rhs.upper_bound()) and noexcept(this->empty()))
Definition: range.hxx:265
constexpr bool contains(TYPE value) const noexcept(noexcept(m_lower.extends_down_to(value)) and noexcept(m_upper.extends_up_to(value)))
Does this range encompass value?
Definition: range.hxx:304
constexpr bool contains(range< TYPE > const &other) const noexcept(noexcept((*this &other)==other))
Does this range encompass all of other?
Definition: range.hxx:314
constexpr bool operator!=(range const &rhs) const noexcept(noexcept(*this==rhs))
Definition: range.hxx:274
range(range &&)=default
range(range const &)=default
constexpr range_bound< TYPE > const & upper_bound() const &noexcept
Definition: range.hxx:326
constexpr range(range_bound< TYPE > lower, range_bound< TYPE > upper)
Create a range.
Definition: range.hxx:245
range & operator=(range &&)=default
constexpr range_bound< TYPE > const & lower_bound() const &noexcept
Definition: range.hxx:321
constexpr bool empty() const noexcept(noexcept(m_lower.is_exclusive()) and noexcept(m_lower.is_limited()) and noexcept(*m_lower.value()< *m_upper.value()))
Is this range clearly empty?
Definition: range.hxx:294
constexpr range() noexcept(noexcept(exclusive_bound< TYPE >{TYPE{}}))
Create an empty range.
Definition: range.hxx:260
static range< TYPE > from_string(std::string_view text)
Definition: range.hxx:439
static constexpr std::size_t size_buffer(range< TYPE > const &value) noexcept
Definition: range.hxx:509
static char * into_buf(char *begin, char *end, range< TYPE > const &value)
Definition: range.hxx:404
static zview to_buf(char *begin, char *end, range< TYPE > const &value)
Definition: range.hxx:398
Traits describing a type's "null value," if any.
Definition: strconv.hxx:93
Nullness traits describing a type which does not have a null value.
Definition: strconv.hxx:115
Traits class for use in string conversions.
Definition: strconv.hxx:155
Marker-type wrapper: zero-terminated std::string_view.
Definition: zview.hxx:38