libpqxx  7.7.0
range.hxx
1 #ifndef PQXX_H_RANGE
2 #define PQXX_H_RANGE
3 
4 #include <variant>
5 
6 #include "pqxx/internal/array-composite.hxx"
7 #include "pqxx/internal/concat.hxx"
8 
9 namespace pqxx
10 {
12 
18 struct no_bound
19 {
20  template<typename TYPE> constexpr bool extends_down_to(TYPE const &) const
21  {
22  return true;
23  }
24  template<typename TYPE> constexpr bool extends_up_to(TYPE const &) const
25  {
26  return true;
27  }
28 };
29 
30 
32 
35 template<typename TYPE> class inclusive_bound
36 {
37 public:
38  inclusive_bound() = delete;
39  explicit inclusive_bound(TYPE const &value) : m_value{value}
40  {
41  if (is_null(value))
42  throw argument_error{"Got null value as an inclusive range bound."};
43  }
44 
45  [[nodiscard]] TYPE const &get() const &noexcept { return m_value; }
46 
48  [[nodiscard]] bool extends_down_to(TYPE const &value) const
49  {
50  return not(value < m_value);
51  }
52 
54  [[nodiscard]] bool extends_up_to(TYPE const &value) const
55  {
56  return not(m_value < value);
57  }
58 
59 private:
60  TYPE m_value;
61 };
62 
63 
65 
68 template<typename TYPE> class exclusive_bound
69 {
70 public:
71  exclusive_bound() = delete;
72  explicit exclusive_bound(TYPE const &value) : m_value{value}
73  {
74  if (is_null(value))
75  throw argument_error{"Got null value as an exclusive range bound."};
76  }
77 
78  [[nodiscard]] TYPE const &get() const &noexcept { return m_value; }
79 
81  [[nodiscard]] bool extends_down_to(TYPE const &value) const
82  {
83  return m_value < value;
84  }
85 
87  [[nodiscard]] bool extends_up_to(TYPE const &value) const
88  {
89  return value < m_value;
90  }
91 
92 private:
93  TYPE m_value;
94 };
95 
96 
98 
101 template<typename TYPE> class range_bound
102 {
103 public:
104  range_bound() = delete;
105  range_bound(no_bound) : m_bound{} {}
106  range_bound(inclusive_bound<TYPE> const &bound) : m_bound{bound} {}
107  range_bound(exclusive_bound<TYPE> const &bound) : m_bound{bound} {}
108  range_bound(range_bound const &) = default;
109  range_bound(range_bound &&) = default;
110 
111  bool operator==(range_bound const &rhs) const
112  {
113  if (this->is_limited())
114  return (
115  rhs.is_limited() and (this->is_inclusive() == rhs.is_inclusive()) and
116  (*this->value() == *rhs.value()));
117  else
118  return not rhs.is_limited();
119  }
120 
121  bool operator!=(range_bound const &rhs) const { return not(*this == rhs); }
122  range_bound &operator=(range_bound const &) = default;
124 
126  bool is_limited() const noexcept
127  {
128  return not std::holds_alternative<no_bound>(m_bound);
129  }
130 
132  bool is_inclusive() const noexcept
133  {
134  return std::holds_alternative<inclusive_bound<TYPE>>(m_bound);
135  }
136 
138  bool is_exclusive() const noexcept
139  {
140  return std::holds_alternative<exclusive_bound<TYPE>>(m_bound);
141  }
142 
144  bool extends_down_to(TYPE const &value) const
145  {
146  return std::visit(
147  [&value](auto const &bound) { return bound.extends_down_to(value); },
148  m_bound);
149  }
150 
152  bool extends_up_to(TYPE const &value) const
153  {
154  return std::visit(
155  [&value](auto const &bound) { return bound.extends_up_to(value); },
156  m_bound);
157  }
158 
160  [[nodiscard]] TYPE const *value() const &noexcept
161  {
162  return std::visit(
163  [](auto const &bound) noexcept {
164  using bound_t = std::decay_t<decltype(bound)>;
165  if constexpr (std::is_same_v<bound_t, no_bound>)
166  return static_cast<TYPE const *>(nullptr);
167  else
168  return &bound.get();
169  },
170  m_bound);
171  }
172 
173 private:
174  std::variant<no_bound, inclusive_bound<TYPE>, exclusive_bound<TYPE>> m_bound;
175 };
176 
177 
178 // C++20: Concepts for comparisons, construction, etc.
180 
198 template<typename TYPE> class range
199 {
200 public:
202 
207  m_lower{lower}, m_upper{upper}
208  {
209  if (
210  lower.is_limited() and upper.is_limited() and
211  (*upper.value() < *lower.value()))
212  throw range_error{internal::concat(
213  "Range's lower bound (", *lower.value(),
214  ") is greater than its upper bound (", *upper.value(), ").")};
215  }
216 
218 
221  range() :
222  m_lower{exclusive_bound<TYPE>{TYPE{}}},
223  m_upper{exclusive_bound<TYPE>{TYPE{}}}
224  {}
225 
226  bool operator==(range const &rhs) const
227  {
228  return (this->lower_bound() == rhs.lower_bound() and
229  this->upper_bound() == rhs.upper_bound()) or
230  (this->empty() and rhs.empty());
231  }
232 
233  bool operator!=(range const &rhs) const { return !(*this == rhs); }
234 
235  range(range const &) = default;
236  range(range &&) = default;
237  range &operator=(range const &) = default;
238  range &operator=(range &&) = default;
239 
241 
249  bool empty() const
250  {
251  return (m_lower.is_exclusive() or m_upper.is_exclusive()) and
252  m_lower.is_limited() and m_upper.is_limited() and
253  not(*m_lower.value() < *m_upper.value());
254  }
255 
257  bool contains(TYPE value) const
258  {
259  return m_lower.extends_down_to(value) and m_upper.extends_up_to(value);
260  }
261 
263 
266  bool contains(range<TYPE> const &other) const
267  {
268  return (*this & other) == other;
269  }
270 
271  [[nodiscard]] range_bound<TYPE> const &lower_bound() const &noexcept
272  {
273  return m_lower;
274  }
275  [[nodiscard]] range_bound<TYPE> const &upper_bound() const &noexcept
276  {
277  return m_upper;
278  }
279 
281 
283  range operator&(range const &other) const
284  {
285  range_bound<TYPE> lower{no_bound{}};
286  if (not this->lower_bound().is_limited())
287  lower = other.lower_bound();
288  else if (not other.lower_bound().is_limited())
289  lower = this->lower_bound();
290  else if (*this->lower_bound().value() < *other.lower_bound().value())
291  lower = other.lower_bound();
292  else if (*other.lower_bound().value() < *this->lower_bound().value())
293  lower = this->lower_bound();
294  else if (this->lower_bound().is_exclusive())
295  lower = this->lower_bound();
296  else
297  lower = other.lower_bound();
298 
299  range_bound<TYPE> upper{no_bound{}};
300  if (not this->upper_bound().is_limited())
301  upper = other.upper_bound();
302  else if (not other.upper_bound().is_limited())
303  upper = this->upper_bound();
304  else if (*other.upper_bound().value() < *this->upper_bound().value())
305  upper = other.upper_bound();
306  else if (*this->upper_bound().value() < *other.upper_bound().value())
307  upper = this->upper_bound();
308  else if (this->upper_bound().is_exclusive())
309  upper = this->upper_bound();
310  else
311  upper = other.upper_bound();
312 
313  if (
314  lower.is_limited() and upper.is_limited() and
315  (*upper.value() < *lower.value()))
316  return {};
317  else
318  return {lower, upper};
319  }
320 
322  template<typename DEST> operator range<DEST>() const
323  {
324  range_bound<DEST> lower{no_bound{}}, upper{no_bound{}};
325  if (lower_bound().is_inclusive())
326  lower = inclusive_bound<DEST>{*lower_bound().value()};
327  else if (lower_bound().is_exclusive())
328  lower = exclusive_bound<DEST>{*lower_bound().value()};
329 
330  if (upper_bound().is_inclusive())
331  upper = inclusive_bound<DEST>{*upper_bound().value()};
332  else if (upper_bound().is_exclusive())
333  upper = exclusive_bound<DEST>{*upper_bound().value()};
334 
335  return {lower, upper};
336  }
337 
338 private:
339  range_bound<TYPE> m_lower, m_upper;
340 };
341 
342 
344 
347 template<typename TYPE> struct string_traits<range<TYPE>>
348 {
349  [[nodiscard]] static inline zview
350  to_buf(char *begin, char *end, range<TYPE> const &value)
351  {
352  return generic_to_buf(begin, end, value);
353  }
354 
355  static inline char *
356  into_buf(char *begin, char *end, range<TYPE> const &value)
357  {
358  if (value.empty())
359  {
360  if ((end - begin) <= internal::ssize(s_empty))
361  throw conversion_overrun{s_overrun.c_str()};
362  char *here = begin + s_empty.copy(begin, std::size(s_empty));
363  *here++ = '\0';
364  return here;
365  }
366  else
367  {
368  if (end - begin < 4)
369  throw conversion_overrun{s_overrun.c_str()};
370  char *here = begin;
371  *here++ =
372  (static_cast<char>(value.lower_bound().is_inclusive() ? '[' : '('));
373  TYPE const *lower{value.lower_bound().value()};
374  // Convert bound (but go back to overwrite that trailing zero).
375  if (lower != nullptr)
376  here = string_traits<TYPE>::into_buf(here, end, *lower) - 1;
377  *here++ = ',';
378  TYPE const *upper{value.upper_bound().value()};
379  // Convert bound (but go back to overwrite that trailing zero).
380  if (upper != nullptr)
381  here = string_traits<TYPE>::into_buf(here, end, *upper) - 1;
382  if ((end - here) < 2)
383  throw conversion_overrun{s_overrun.c_str()};
384  *here++ =
385  static_cast<char>(value.upper_bound().is_inclusive() ? ']' : ')');
386  *here++ = '\0';
387  return here;
388  }
389  }
390 
391  [[nodiscard]] static inline range<TYPE> from_string(std::string_view text)
392  {
393  if (std::size(text) < 3)
394  throw pqxx::conversion_error{err_bad_input(text)};
395  bool left_inc{false};
396  switch (text[0])
397  {
398  case '[': left_inc = true; break;
399 
400  case '(': break;
401 
402  case 'e':
403  case 'E':
404  if (
405  (std::size(text) != std::size(s_empty)) or
406  (text[1] != 'm' and text[1] != 'M') or
407  (text[2] != 'p' and text[2] != 'P') or
408  (text[3] != 't' and text[3] != 'T') or
409  (text[4] != 'y' and text[4] != 'Y'))
410  throw pqxx::conversion_error{err_bad_input(text)};
411  return {};
412  break;
413 
414  default: throw pqxx::conversion_error{err_bad_input(text)};
415  }
416 
417  auto scan{internal::get_glyph_scanner(internal::encoding_group::UTF8)};
418  // The field parser uses this to track which field it's parsing, and
419  // when not to expect a field separator.
420  std::size_t index{0};
421  // C++20: constinit.
422  // The last field we expect to see.
423  constexpr std::size_t last{1};
424  // Current parsing position. We skip the opening parenthesis or bracket.
425  std::size_t pos{1};
426  // The string may leave out either bound to indicate that it's unlimited.
427  std::optional<TYPE> lower, upper;
428  // We reuse the same field parser we use for composite values and arrays.
429  internal::parse_composite_field(index, text, pos, lower, scan, last);
430  internal::parse_composite_field(index, text, pos, upper, scan, last);
431 
432  // We need one more character: the closing parenthesis or bracket.
433  if (pos != std::size(text))
434  throw pqxx::conversion_error{err_bad_input(text)};
435  char const closing{text[pos - 1]};
436  if (closing != ')' and closing != ']')
437  throw pqxx::conversion_error{err_bad_input(text)};
438  bool const right_inc{closing == ']'};
439 
440  range_bound<TYPE> lower_bound{no_bound{}}, upper_bound{no_bound{}};
441  if (lower)
442  {
443  if (left_inc)
444  lower_bound = inclusive_bound{*lower};
445  else
446  lower_bound = exclusive_bound{*lower};
447  }
448  if (upper)
449  {
450  if (right_inc)
451  upper_bound = inclusive_bound{*upper};
452  else
453  upper_bound = exclusive_bound{*upper};
454  }
455 
456  return {lower_bound, upper_bound};
457  }
458 
459  [[nodiscard]] static inline std::size_t
460  size_buffer(range<TYPE> const &value) noexcept
461  {
462  TYPE const *lower{value.lower_bound().value()},
463  *upper{value.upper_bound().value()};
464  std::size_t const lsz{
465  lower == nullptr ? 0 : string_traits<TYPE>::size_buffer(*lower) - 1},
466  usz{upper == nullptr ? 0 : string_traits<TYPE>::size_buffer(*upper) - 1};
467 
468  if (value.empty())
469  return std::size(s_empty) + 1;
470  else
471  return 1 + lsz + 1 + usz + 2;
472  }
473 
474 private:
475  // C++20: constinit.
476  static constexpr zview s_empty{"empty"_zv};
477  // C++20: constinit.
478  static constexpr auto s_overrun{"Not enough space in buffer for range."_zv};
479 
481  static std::string err_bad_input(std::string_view text)
482  {
483  return internal::concat("Invalid range input: '", text, "'");
484  }
485 };
486 
487 
489 template<typename TYPE> struct nullness<range<TYPE>> : no_null<range<TYPE>>
490 {};
491 } // namespace pqxx
492 #endif
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:23
zview generic_to_buf(char *begin, char *end, TYPE const &value)
Implement string_traits<TYPE>::to_buf by calling into_buf.
Definition: strconv.hxx:439
bool is_null(TYPE const &value) noexcept
Is value null?
Definition: strconv.hxx:364
Invalid argument passed to libpqxx, similar to std::invalid_argument.
Definition: except.hxx:169
Value conversion failed, e.g. when converting "Hello" to int.
Definition: except.hxx:176
Could not convert value to string: not enough buffer space.
Definition: except.hxx:183
Something is out of range, similar to std::out_of_range.
Definition: except.hxx:190
An unlimited boundary value to a pqxx::range.
Definition: range.hxx:19
constexpr bool extends_down_to(TYPE const &) const
Definition: range.hxx:20
constexpr bool extends_up_to(TYPE const &) const
Definition: range.hxx:24
An inclusive boundary value to a pqxx::range.
Definition: range.hxx:36
bool extends_up_to(TYPE const &value) const
Would this bound, as an upper bound, include value?
Definition: range.hxx:54
bool extends_down_to(TYPE const &value) const
Would this bound, as a lower bound, include value?
Definition: range.hxx:48
TYPE const & get() const &noexcept
Definition: range.hxx:45
inclusive_bound(TYPE const &value)
Definition: range.hxx:39
An exclusive boundary value to a pqxx::range.
Definition: range.hxx:69
exclusive_bound(TYPE const &value)
Definition: range.hxx:72
bool extends_down_to(TYPE const &value) const
Would this bound, as a lower bound, include value?
Definition: range.hxx:81
TYPE const & get() const &noexcept
Definition: range.hxx:78
bool extends_up_to(TYPE const &value) const
Would this bound, as an upper bound, include value?
Definition: range.hxx:87
A range boundary value.
Definition: range.hxx:102
bool extends_down_to(TYPE const &value) const
Would this bound, as a lower bound, include value?
Definition: range.hxx:144
bool is_exclusive() const noexcept
Is this boundary an exclusive one?
Definition: range.hxx:138
range_bound(range_bound &&)=default
TYPE const * value() const &noexcept
Return bound value, or nullptr if it's not limited.
Definition: range.hxx:160
bool extends_up_to(TYPE const &value) const
Would this bound, as an upper bound, include value?
Definition: range.hxx:152
range_bound(inclusive_bound< TYPE > const &bound)
Definition: range.hxx:106
range_bound & operator=(range_bound &&)=default
range_bound()=delete
range_bound(range_bound const &)=default
range_bound(exclusive_bound< TYPE > const &bound)
Definition: range.hxx:107
bool is_limited() const noexcept
Is this a finite bound?
Definition: range.hxx:126
range_bound(no_bound)
Definition: range.hxx:105
bool operator==(range_bound const &rhs) const
Definition: range.hxx:111
bool is_inclusive() const noexcept
Is this boundary an inclusive one?
Definition: range.hxx:132
bool operator!=(range_bound const &rhs) const
Definition: range.hxx:121
range_bound & operator=(range_bound const &)=default
A C++ equivalent to PostgreSQL's range types.
Definition: range.hxx:199
range(range_bound< TYPE > lower, range_bound< TYPE > upper)
Create a range.
Definition: range.hxx:206
range & operator=(range const &)=default
range(range &&)=default
range(range const &)=default
bool contains(range< TYPE > const &other) const
Does this range encompass all of other?
Definition: range.hxx:266
bool operator==(range const &rhs) const
Definition: range.hxx:226
bool empty() const
Is this range clearly empty?
Definition: range.hxx:249
bool contains(TYPE value) const
Does this range encompass value?
Definition: range.hxx:257
range & operator=(range &&)=default
range()
Create an empty range.
Definition: range.hxx:221
bool operator!=(range const &rhs) const
Definition: range.hxx:233
range_bound< TYPE > const & upper_bound() const &noexcept
Definition: range.hxx:275
range operator&(range const &other) const
Intersection of two ranges.
Definition: range.hxx:283
range_bound< TYPE > const & lower_bound() const &noexcept
Definition: range.hxx:271
static std::size_t size_buffer(range< TYPE > const &value) noexcept
Definition: range.hxx:460
static range< TYPE > from_string(std::string_view text)
Definition: range.hxx:391
static char * into_buf(char *begin, char *end, range< TYPE > const &value)
Definition: range.hxx:356
static zview to_buf(char *begin, char *end, range< TYPE > const &value)
Definition: range.hxx:350
Traits describing a type's "null value," if any.
Definition: strconv.hxx:89
Nullness traits describing a type which does not have a null value.
Definition: strconv.hxx:111
Traits class for use in string conversions.
Definition: strconv.hxx:153
Marker-type wrapper: zero-terminated std::string_view.
Definition: zview.hxx:38