libpqxx  7.7.2
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 <variant>
9 
10 #include "pqxx/internal/array-composite.hxx"
11 #include "pqxx/internal/concat.hxx"
12 
13 namespace pqxx
14 {
16 
22 struct no_bound
23 {
24  template<typename TYPE> constexpr bool extends_down_to(TYPE const &) const
25  {
26  return true;
27  }
28  template<typename TYPE> constexpr bool extends_up_to(TYPE const &) const
29  {
30  return true;
31  }
32 };
33 
34 
36 
39 template<typename TYPE> class inclusive_bound
40 {
41 public:
42  inclusive_bound() = delete;
43  explicit inclusive_bound(TYPE const &value) : m_value{value}
44  {
45  if (is_null(value))
46  throw argument_error{"Got null value as an inclusive range bound."};
47  }
48 
49  [[nodiscard]] constexpr TYPE const &get() const &noexcept { return m_value; }
50 
51  // TODO: constexpr and/or noexcept if underlying operator supports it.
53  [[nodiscard]] bool extends_down_to(TYPE const &value) const
54  {
55  return not(value < m_value);
56  }
57 
58  // TODO: constexpr and/or noexcept if underlying operator supports it.
60  [[nodiscard]] bool extends_up_to(TYPE const &value) const
61  {
62  return not(m_value < value);
63  }
64 
65 private:
66  TYPE m_value;
67 };
68 
69 
71 
74 template<typename TYPE> class exclusive_bound
75 {
76 public:
77  exclusive_bound() = delete;
78  explicit exclusive_bound(TYPE const &value) : m_value{value}
79  {
80  if (is_null(value))
81  throw argument_error{"Got null value as an exclusive range bound."};
82  }
83 
84  [[nodiscard]] constexpr TYPE const &get() const &noexcept { return m_value; }
85 
86  // TODO: constexpr and/or noexcept if underlying operator supports it.
88  [[nodiscard]] bool extends_down_to(TYPE const &value) const
89  {
90  return m_value < value;
91  }
92 
93  // TODO: constexpr and/or noexcept if underlying operator supports it.
95  [[nodiscard]] bool extends_up_to(TYPE const &value) const
96  {
97  return value < m_value;
98  }
99 
100 private:
101  TYPE m_value;
102 };
103 
104 
106 
109 template<typename TYPE> class range_bound
110 {
111 public:
112  range_bound() = delete;
113  // TODO: constexpr and/or noexcept if underlying constructor supports it.
114  range_bound(no_bound) : m_bound{} {}
115  // TODO: constexpr and/or noexcept if underlying constructor supports it.
116  range_bound(inclusive_bound<TYPE> const &bound) : m_bound{bound} {}
117  // TODO: constexpr and/or noexcept if underlying constructor supports it.
118  range_bound(exclusive_bound<TYPE> const &bound) : m_bound{bound} {}
119  // TODO: constexpr and/or noexcept if underlying constructor supports it.
120  range_bound(range_bound const &) = default;
121  // TODO: constexpr and/or noexcept if underlying constructor supports it.
122  range_bound(range_bound &&) = default;
123 
124  // TODO: constexpr and/or noexcept if underlying operators support it.
125  bool operator==(range_bound const &rhs) const
126  {
127  if (this->is_limited())
128  return (
129  rhs.is_limited() and (this->is_inclusive() == rhs.is_inclusive()) and
130  (*this->value() == *rhs.value()));
131  else
132  return not rhs.is_limited();
133  }
134 
135  // TODO: constexpr and/or noexcept if underlying operator supports it.
136  bool operator!=(range_bound const &rhs) const { return not(*this == rhs); }
137  range_bound &operator=(range_bound const &) = default;
138  range_bound &operator=(range_bound &&) = default;
139 
141  constexpr bool is_limited() const noexcept
142  {
143  return not std::holds_alternative<no_bound>(m_bound);
144  }
145 
147  constexpr bool is_inclusive() const noexcept
148  {
149  return std::holds_alternative<inclusive_bound<TYPE>>(m_bound);
150  }
151 
153  constexpr bool is_exclusive() const noexcept
154  {
155  return std::holds_alternative<exclusive_bound<TYPE>>(m_bound);
156  }
157 
158  // TODO: constexpr/noexcept if underlying function supports it.
160  bool extends_down_to(TYPE const &value) const
161  {
162  return std::visit(
163  [&value](auto const &bound) { return bound.extends_down_to(value); },
164  m_bound);
165  }
166 
167  // TODO: constexpr/noexcept if underlying function supports it.
169  bool extends_up_to(TYPE const &value) const
170  {
171  return std::visit(
172  [&value](auto const &bound) { return bound.extends_up_to(value); },
173  m_bound);
174  }
175 
177  [[nodiscard]] constexpr TYPE const *value() const &noexcept
178  {
179  return std::visit(
180  [](auto const &bound) noexcept {
181  using bound_t = std::decay_t<decltype(bound)>;
182  if constexpr (std::is_same_v<bound_t, no_bound>)
183  return static_cast<TYPE const *>(nullptr);
184  else
185  return &bound.get();
186  },
187  m_bound);
188  }
189 
190 private:
191  std::variant<no_bound, inclusive_bound<TYPE>, exclusive_bound<TYPE>> m_bound;
192 };
193 
194 
195 // C++20: Concepts for comparisons, construction, etc.
197 
215 template<typename TYPE> class range
216 {
217 public:
219 
224  m_lower{lower}, m_upper{upper}
225  {
226  if (
227  lower.is_limited() and upper.is_limited() and
228  (*upper.value() < *lower.value()))
229  throw range_error{internal::concat(
230  "Range's lower bound (", *lower.value(),
231  ") is greater than its upper bound (", *upper.value(), ").")};
232  }
233 
234  // TODO: constexpr and/or noexcept if underlying constructor supports it.
236 
239  range() :
240  m_lower{exclusive_bound<TYPE>{TYPE{}}},
241  m_upper{exclusive_bound<TYPE>{TYPE{}}}
242  {}
243 
244  // TODO: constexpr and/or noexcept if underlying operators support it.
245  bool operator==(range const &rhs) const
246  {
247  return (this->lower_bound() == rhs.lower_bound() and
248  this->upper_bound() == rhs.upper_bound()) or
249  (this->empty() and rhs.empty());
250  }
251 
252  // TODO: constexpr and/or noexcept if underlying operator supports it.
253  bool operator!=(range const &rhs) const { return !(*this == rhs); }
254 
255  range(range const &) = default;
256  range(range &&) = default;
257  range &operator=(range const &) = default;
258  range &operator=(range &&) = default;
259 
260  // TODO: constexpr and/or noexcept if underlying operator supports it.
262 
270  bool empty() const
271  {
272  return (m_lower.is_exclusive() or m_upper.is_exclusive()) and
273  m_lower.is_limited() and m_upper.is_limited() and
274  not(*m_lower.value() < *m_upper.value());
275  }
276 
277  // TODO: constexpr and/or noexcept if underlying functions support it.
279  bool contains(TYPE value) const
280  {
281  return m_lower.extends_down_to(value) and m_upper.extends_up_to(value);
282  }
283 
284  // TODO: constexpr and/or noexcept if underlying operators support it.
286 
289  bool contains(range<TYPE> const &other) const
290  {
291  return (*this & other) == other;
292  }
293 
294  [[nodiscard]] constexpr range_bound<TYPE> const &
295  lower_bound() const &noexcept
296  {
297  return m_lower;
298  }
299  [[nodiscard]] constexpr range_bound<TYPE> const &
300  upper_bound() const &noexcept
301  {
302  return m_upper;
303  }
304 
305  // TODO: constexpr and/or noexcept if underlying operators support it.
307 
309  range operator&(range const &other) const
310  {
311  range_bound<TYPE> lower{no_bound{}};
312  if (not this->lower_bound().is_limited())
313  lower = other.lower_bound();
314  else if (not other.lower_bound().is_limited())
315  lower = this->lower_bound();
316  else if (*this->lower_bound().value() < *other.lower_bound().value())
317  lower = other.lower_bound();
318  else if (*other.lower_bound().value() < *this->lower_bound().value())
319  lower = this->lower_bound();
320  else if (this->lower_bound().is_exclusive())
321  lower = this->lower_bound();
322  else
323  lower = other.lower_bound();
324 
325  range_bound<TYPE> upper{no_bound{}};
326  if (not this->upper_bound().is_limited())
327  upper = other.upper_bound();
328  else if (not other.upper_bound().is_limited())
329  upper = this->upper_bound();
330  else if (*other.upper_bound().value() < *this->upper_bound().value())
331  upper = other.upper_bound();
332  else if (*this->upper_bound().value() < *other.upper_bound().value())
333  upper = this->upper_bound();
334  else if (this->upper_bound().is_exclusive())
335  upper = this->upper_bound();
336  else
337  upper = other.upper_bound();
338 
339  if (
340  lower.is_limited() and upper.is_limited() and
341  (*upper.value() < *lower.value()))
342  return {};
343  else
344  return {lower, upper};
345  }
346 
348  template<typename DEST> operator range<DEST>() const
349  {
350  range_bound<DEST> lower{no_bound{}}, upper{no_bound{}};
351  if (lower_bound().is_inclusive())
352  lower = inclusive_bound<DEST>{*lower_bound().value()};
353  else if (lower_bound().is_exclusive())
354  lower = exclusive_bound<DEST>{*lower_bound().value()};
355 
356  if (upper_bound().is_inclusive())
357  upper = inclusive_bound<DEST>{*upper_bound().value()};
358  else if (upper_bound().is_exclusive())
359  upper = exclusive_bound<DEST>{*upper_bound().value()};
360 
361  return {lower, upper};
362  }
363 
364 private:
365  range_bound<TYPE> m_lower, m_upper;
366 };
367 
368 
370 
373 template<typename TYPE> struct string_traits<range<TYPE>>
374 {
375  [[nodiscard]] static inline zview
376  to_buf(char *begin, char *end, range<TYPE> const &value)
377  {
378  return generic_to_buf(begin, end, value);
379  }
380 
381  static inline char *
382  into_buf(char *begin, char *end, range<TYPE> const &value)
383  {
384  if (value.empty())
385  {
386  if ((end - begin) <= internal::ssize(s_empty))
387  throw conversion_overrun{s_overrun.c_str()};
388  char *here = begin + s_empty.copy(begin, std::size(s_empty));
389  *here++ = '\0';
390  return here;
391  }
392  else
393  {
394  if (end - begin < 4)
395  throw conversion_overrun{s_overrun.c_str()};
396  char *here = begin;
397  *here++ =
398  (static_cast<char>(value.lower_bound().is_inclusive() ? '[' : '('));
399  TYPE const *lower{value.lower_bound().value()};
400  // Convert bound (but go back to overwrite that trailing zero).
401  if (lower != nullptr)
402  here = string_traits<TYPE>::into_buf(here, end, *lower) - 1;
403  *here++ = ',';
404  TYPE const *upper{value.upper_bound().value()};
405  // Convert bound (but go back to overwrite that trailing zero).
406  if (upper != nullptr)
407  here = string_traits<TYPE>::into_buf(here, end, *upper) - 1;
408  if ((end - here) < 2)
409  throw conversion_overrun{s_overrun.c_str()};
410  *here++ =
411  static_cast<char>(value.upper_bound().is_inclusive() ? ']' : ')');
412  *here++ = '\0';
413  return here;
414  }
415  }
416 
417  [[nodiscard]] static inline range<TYPE> from_string(std::string_view text)
418  {
419  if (std::size(text) < 3)
420  throw pqxx::conversion_error{err_bad_input(text)};
421  bool left_inc{false};
422  switch (text[0])
423  {
424  case '[': left_inc = true; break;
425 
426  case '(': break;
427 
428  case 'e':
429  case 'E':
430  if (
431  (std::size(text) != std::size(s_empty)) or
432  (text[1] != 'm' and text[1] != 'M') or
433  (text[2] != 'p' and text[2] != 'P') or
434  (text[3] != 't' and text[3] != 'T') or
435  (text[4] != 'y' and text[4] != 'Y'))
436  throw pqxx::conversion_error{err_bad_input(text)};
437  return {};
438  break;
439 
440  default: throw pqxx::conversion_error{err_bad_input(text)};
441  }
442 
443  auto scan{internal::get_glyph_scanner(internal::encoding_group::UTF8)};
444  // The field parser uses this to track which field it's parsing, and
445  // when not to expect a field separator.
446  std::size_t index{0};
447  // The last field we expect to see.
448  static constexpr std::size_t last{1};
449  // Current parsing position. We skip the opening parenthesis or bracket.
450  std::size_t pos{1};
451  // The string may leave out either bound to indicate that it's unlimited.
452  std::optional<TYPE> lower, upper;
453  // We reuse the same field parser we use for composite values and arrays.
454  internal::parse_composite_field(index, text, pos, lower, scan, last);
455  internal::parse_composite_field(index, text, pos, upper, scan, last);
456 
457  // We need one more character: the closing parenthesis or bracket.
458  if (pos != std::size(text))
459  throw pqxx::conversion_error{err_bad_input(text)};
460  char const closing{text[pos - 1]};
461  if (closing != ')' and closing != ']')
462  throw pqxx::conversion_error{err_bad_input(text)};
463  bool const right_inc{closing == ']'};
464 
465  range_bound<TYPE> lower_bound{no_bound{}}, upper_bound{no_bound{}};
466  if (lower)
467  {
468  if (left_inc)
469  lower_bound = inclusive_bound{*lower};
470  else
471  lower_bound = exclusive_bound{*lower};
472  }
473  if (upper)
474  {
475  if (right_inc)
476  upper_bound = inclusive_bound{*upper};
477  else
478  upper_bound = exclusive_bound{*upper};
479  }
480 
481  return {lower_bound, upper_bound};
482  }
483 
484  [[nodiscard]] static inline constexpr std::size_t
485  size_buffer(range<TYPE> const &value) noexcept
486  {
487  TYPE const *lower{value.lower_bound().value()},
488  *upper{value.upper_bound().value()};
489  std::size_t const lsz{
490  lower == nullptr ? 0 : string_traits<TYPE>::size_buffer(*lower) - 1},
491  usz{upper == nullptr ? 0 : string_traits<TYPE>::size_buffer(*upper) - 1};
492 
493  if (value.empty())
494  return std::size(s_empty) + 1;
495  else
496  return 1 + lsz + 1 + usz + 2;
497  }
498 
499 private:
500  static constexpr zview s_empty{"empty"_zv};
501  static constexpr auto s_overrun{"Not enough space in buffer for range."_zv};
502 
504  static std::string err_bad_input(std::string_view text)
505  {
506  return internal::concat("Invalid range input: '", text, "'");
507  }
508 };
509 
510 
512 template<typename TYPE> struct nullness<range<TYPE>> : no_null<range<TYPE>>
513 {};
514 } // namespace pqxx
515 #endif
Something is out of range, similar to std::out_of_range.
Definition: except.hxx:201
A range boundary value.
Definition: range.hxx:109
Invalid argument passed to libpqxx, similar to std::invalid_argument.
Definition: except.hxx:180
An unlimited boundary value to a pqxx::range.
Definition: range.hxx:22
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:26
range()
Create an empty range.
Definition: range.hxx:239
constexpr bool is_exclusive() const noexcept
Is this boundary an exclusive one?
Definition: range.hxx:153
range(range_bound< TYPE > lower, range_bound< TYPE > upper)
Create a range.
Definition: range.hxx:223
bool extends_up_to(TYPE const &value) const
Would this bound, as an upper bound, include value?
Definition: range.hxx:95
bool contains(range< TYPE > const &other) const
Does this range encompass all of other?
Definition: range.hxx:289
auto ssize(T const &c)
Transitional: std::ssize(), or custom implementation if not available.
Definition: util.hxx:446
Traits class for use in string conversions.
Definition: strconv.hxx:154
bool operator==(range const &rhs) const
Definition: range.hxx:245
static std::size_t size_buffer(TYPE const &value) noexcept
Estimate how much buffer space is needed to represent value.
inclusive_bound(TYPE const &value)
Definition: range.hxx:43
bool operator==(range_bound const &rhs) const
Definition: range.hxx:125
bool operator!=(range_bound const &rhs) const
Definition: range.hxx:136
constexpr bool is_limited() const noexcept
Is this a finite bound?
Definition: range.hxx:141
bool extends_down_to(TYPE const &value) const
Would this bound, as a lower bound, include value?
Definition: range.hxx:160
Nullness traits describing a type which does not have a null value.
Definition: strconv.hxx:114
static char * into_buf(char *begin, char *end, range< TYPE > const &value)
Definition: range.hxx:382
bool extends_up_to(TYPE const &value) const
Would this bound, as an upper bound, include value?
Definition: range.hxx:60
constexpr bool extends_down_to(TYPE const &) const
Definition: range.hxx:24
bool extends_up_to(TYPE const &value) const
Would this bound, as an upper bound, include value?
Definition: range.hxx:169
constexpr range_bound< TYPE > const & upper_bound() const &noexcept
Definition: range.hxx:300
PQXX_PURE glyph_scanner_func * get_glyph_scanner(encoding_group enc)
Definition: encodings.cxx:796
Could not convert value to string: not enough buffer space.
Definition: except.hxx:194
bool operator!=(range const &rhs) const
Definition: range.hxx:253
An exclusive boundary value to a pqxx::range.
Definition: range.hxx:74
constexpr bool is_inclusive() const noexcept
Is this boundary an inclusive one?
Definition: range.hxx:147
constexpr range_bound< TYPE > const & lower_bound() const &noexcept
Definition: range.hxx:295
constexpr TYPE const * value() const &noexcept
Return bound value, or nullptr if it&#39;s not limited.
Definition: range.hxx:177
bool extends_down_to(TYPE const &value) const
Would this bound, as a lower bound, include value?
Definition: range.hxx:88
bool contains(TYPE value) const
Does this range encompass value?
Definition: range.hxx:279
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
constexpr bool extends_up_to(TYPE const &) const
Definition: range.hxx:28
Marker-type wrapper: zero-terminated std::string_view.
Definition: zview.hxx:37
A C++ equivalent to PostgreSQL&#39;s range types.
Definition: range.hxx:215
range_bound(inclusive_bound< TYPE > const &bound)
Definition: range.hxx:116
bool extends_down_to(TYPE const &value) const
Would this bound, as a lower bound, include value?
Definition: range.hxx:53
exclusive_bound(TYPE const &value)
Definition: range.hxx:78
static constexpr std::size_t size_buffer(range< TYPE > const &value) noexcept
Definition: range.hxx:485
Value conversion failed, e.g. when converting "Hello" to int.
Definition: except.hxx:187
static zview to_buf(char *begin, char *end, range< TYPE > const &value)
Definition: range.hxx:376
An inclusive boundary value to a pqxx::range.
Definition: range.hxx:39
bool empty() const
Is this range clearly empty?
Definition: range.hxx:270
range_bound(no_bound)
Definition: range.hxx:114
static range< TYPE > from_string(std::string_view text)
Definition: range.hxx:417
Traits describing a type&#39;s "null value," if any.
Definition: strconv.hxx:92
constexpr bool is_null(TYPE const &value) noexcept
Is value null?
Definition: strconv.hxx:367
range_bound(exclusive_bound< TYPE > const &bound)
Definition: range.hxx:118
static char * into_buf(char *begin, char *end, TYPE const &value)
Write value&#39;s string representation into buffer at begin.