libpqxx 7.8.1
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 { 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
78template<typename TYPE> class exclusive_bound
79{
80 // (Putting private section first to work around bug in gcc < 10: see #665.)
81private:
82 TYPE m_value;
83
84public:
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
114template<typename TYPE> class range_bound
115{
116 // (Putting private section first to work around bug in gcc < 10: see #665.)
117private:
118 std::variant<no_bound, inclusive_bound<TYPE>, exclusive_bound<TYPE>> m_bound;
119
120public:
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
233template<typename TYPE> class range
234{
235 // (Putting private section first to work around bug in gcc < 10: see #665.)
236private:
237 range_bound<TYPE> m_lower, m_upper;
238
239public:
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 {
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
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
395template<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
523private:
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
536template<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
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: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 TYPE const & get() const &noexcept
Definition range.hxx:92
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
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 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==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()=delete
constexpr range_bound(no_bound) noexcept
Definition range.hxx:122
range_bound & operator=(range_bound const &)=default
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
range_bound & operator=(range_bound &&)=default
constexpr range_bound(range_bound &&)=default
A C++ equivalent to PostgreSQL's range types.
Definition range.hxx:234
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
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:245
constexpr range_bound< TYPE > const & upper_bound() const &noexcept
Definition range.hxx:326
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_bound< TYPE > const & lower_bound() const &noexcept
Definition range.hxx:321
constexpr range() noexcept(noexcept(exclusive_bound< TYPE >{TYPE{}}))
Create an empty range.
Definition range.hxx:260
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
static range< TYPE > from_string(std::string_view text)
Definition range.hxx:439
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