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
14namespace pqxx
15{
17
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
42template<typename TYPE> class inclusive_bound
43{
44 // (Putting private section first to work around bug in gcc < 10: see #665.)
45private:
46 TYPE m_value;
47
48public:
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
81template<typename TYPE> class exclusive_bound
82{
83 // (Putting private section first to work around bug in gcc < 10: see #665.)
84private:
85 TYPE m_value;
86
87public:
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
120template<typename TYPE> class range_bound
121{
122 // (Putting private section first to work around bug in gcc < 10: see #665.)
123private:
124 std::variant<no_bound, inclusive_bound<TYPE>, exclusive_bound<TYPE>> m_bound;
125
126public:
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
239template<typename TYPE> class range
240{
241 // (Putting private section first to work around bug in gcc < 10: see #665.)
242private:
243 range_bound<TYPE> m_lower, m_upper;
244
245public:
247
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
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 &
328 {
329 return m_lower;
330 }
331 [[nodiscard]] constexpr range_bound<TYPE> const &
333 {
334 return m_upper;
335 }
336
338
340 constexpr range operator&(range const &other) const
341 {
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
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 {
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
401template<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
529private:
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
542template<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 char array_separator
Element separator between SQL array elements of this type.
Definition strconv.hxx:557
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 TYPE const & get() const &noexcept
Definition range.hxx:95
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
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 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==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()=delete
constexpr range_bound(no_bound) noexcept
Definition range.hxx:128
range_bound & operator=(range_bound const &)=default
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
range_bound & operator=(range_bound &&)=default
constexpr range_bound(range_bound &&)=default
A C++ equivalent to PostgreSQL's range types.
Definition range.hxx:240
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
range & operator=(range &&)=default
range & operator=(range const &)=default
constexpr range(range_bound< TYPE > lower, range_bound< TYPE > upper)
Create a range.
Definition range.hxx:251
constexpr range_bound< TYPE > const & upper_bound() const &noexcept
Definition range.hxx:332
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_bound< TYPE > const & lower_bound() const &noexcept
Definition range.hxx:327
constexpr range() noexcept(noexcept(exclusive_bound< TYPE >{TYPE{}}))
Create an empty range.
Definition range.hxx:266
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
static range< TYPE > from_string(std::string_view text)
Definition range.hxx:445
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