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