76#include <fmt/format.h>
78#include <userver/storages/postgres/exceptions.hpp>
79#include <userver/storages/postgres/io/buffer_io_base.hpp>
80#include <userver/storages/postgres/io/field_buffer.hpp>
81#include <userver/storages/postgres/io/traits.hpp>
82#include <userver/storages/postgres/io/type_mapping.hpp>
83#include <userver/storages/postgres/io/type_traits.hpp>
84#include <userver/storages/postgres/io/user_types.hpp>
86#include <userver/utils/assert.hpp>
87#include <userver/utils/flags.hpp>
89USERVER_NAMESPACE_BEGIN
96enum class RangeBound {
100 kBoth = kLower | kUpper,
103using RangeBounds = USERVER_NAMESPACE::
utils::Flags<RangeBound>;
107 static constexpr bool kNothrowValueCtor =
108 std::is_nothrow_default_constructible_v<T>;
109 static constexpr bool kNothrowValueCopy =
110 std::is_nothrow_copy_constructible_v<T>;
111 static constexpr bool kNothrowValueMove =
112 std::is_nothrow_move_constructible_v<T>;
113 static constexpr bool kIsDiscreteValue = std::is_integral_v<T>;
116 using OptionalValue = std::optional<T>;
141 Range(
const OptionalValue& lower,
const OptionalValue& upper,
147 template <
typename U,
151 bool operator==(
const Range& rhs)
const;
153 bool operator!=(
const Range& rhs)
const {
return !(*
this == rhs); }
155 bool Empty()
const {
return !data; }
160 bool HasLowerBound()
const {
161 return !!data && data->HasBound(RangeBound::kLower);
163 bool HasUpperBound()
const {
164 return !!data && data->HasBound(RangeBound::kUpper);
170 return data->GetOptionalValue(RangeBound::kLower);
178 return data->GetOptionalValue(RangeBound::kUpper);
183 bool IsLowerBoundIncluded()
const {
184 return !!data && data->IsBoundIncluded(RangeBound::kLower);
186 bool IsUpperBoundIncluded()
const {
187 return !!data && data->IsBoundIncluded(RangeBound::kUpper);
191 template <
typename U>
196 RangeData()
noexcept =
default;
198 template <
typename U>
199 RangeData(U&& lower, U&& upper, RangeBounds bounds)
200 : RangeData{OptionalValue{std::forward<U>(lower)},
201 OptionalValue{std::forward<U>(upper)}, bounds} {}
203 template <
typename U>
205 RangeBounds bounds)
noexcept(kNothrowValueCopy)
206 : RangeData{OptionalValue{std::forward<U>(lower)}, OptionalValue{},
209 template <
typename U>
211 RangeBounds bounds)
noexcept(kNothrowValueCopy)
212 : RangeData{OptionalValue{}, OptionalValue{std::forward<U>(upper)},
215 RangeData(OptionalValue low, OptionalValue up, RangeBounds bounds)
216 : bounds{bounds}, lower{std::move(low)}, upper{std::move(up)} {
217 if (lower && upper && *upper < *lower) {
218 throw LogicError(
"Range lower bound is greater than upper");
222 bool operator==(
const RangeData& rhs)
const {
223 return BoundEqual(rhs, RangeBound::kLower) &&
224 BoundEqual(rhs, RangeBound::kUpper);
227 bool operator!=(
const RangeData& rhs)
const {
return !(*
this == rhs); }
229 bool HasBound(RangeBounds side)
const;
231 bool IsBoundIncluded(RangeBounds side)
const {
232 return HasBound(side) && (bounds & side);
235 bool BoundEqual(
const RangeData& rhs, RangeBounds side)
const;
238 const T& GetBoundValue(RangeBounds side)
const {
239 if (side == RangeBound::kLower)
return *lower;
241 "Invalid bounds side argument value");
245 const OptionalValue& GetOptionalValue(RangeBounds side)
const {
246 if (side == RangeBound::kLower)
return lower;
248 "Invalid bounds side argument value");
252 RangeBounds bounds = RangeBound::kNone;
257 template <
typename U>
258 static OptionalValue ConvertBound(
const std::optional<U>& rhs) {
259 if (!rhs)
return OptionalValue{};
260 return OptionalValue{*rhs};
263 template <
typename U>
264 static std::optional<RangeData> ConvertData(
const Range<U>& rhs) {
265 if (!rhs.data)
return {};
266 return RangeData{ConvertBound(rhs.data->lower),
267 ConvertBound(rhs.data->upper), rhs.data->bounds};
270 std::optional<RangeData> data;
272 static const inline OptionalValue kNoValue{};
276auto MakeRange(T&& lower, T&& upper, RangeBounds bounds = RangeBound::kLower) {
277 using ElementType = std::decay_t<T>;
278 return Range<ElementType>{std::forward<T>(lower), std::forward<T>(upper),
284 RangeBounds bounds = RangeBound::kLower) {
285 using ElementType = std::decay_t<T>;
286 return Range<ElementType>{std::forward<T>(lower), kUnbounded, bounds};
291 RangeBounds bounds = RangeBound::kNone) {
292 using ElementType = std::decay_t<T>;
293 return Range<ElementType>{kUnbounded, std::forward<T>(upper), bounds};
296using IntegerRange =
Range<Integer>;
297using BigintRange =
Range<Bigint>;
301 static constexpr bool kNothrowValueCtor =
302 std::is_nothrow_default_constructible_v<T>;
307 BoundedRange()
noexcept(kNothrowValueCtor);
309 template <
typename U,
typename = std::enable_if_t<
310 std::is_convertible_v<std::decay_t<U>, T>>>
311 BoundedRange(U&& lower, U&& upper, RangeBounds bounds = RangeBound::kLower);
313 template <
typename U>
314 explicit BoundedRange(
Range<U>&&);
317 bool operator!=(
const BoundedRange& rhs)
const {
return !(*
this == rhs); }
319 const ValueType& GetLowerBound()
const {
return *value_.GetLowerBound(); }
320 bool IsLowerBoundIncluded()
const {
return value_.IsLowerBoundIncluded(); }
322 const ValueType& GetUpperBound()
const {
return *value_.GetUpperBound(); }
323 bool IsUpperBoundIncluded()
const {
return value_.IsUpperBoundIncluded(); }
325 const Range<T>& GetUnboundedRange()
const {
return value_; }
341enum class RangeFlag {
344 kLowerBoundInclusive = 0x02,
345 kUpperBoundInclusive = 0x04,
346 kLowerBoundInfinity = 0x08,
347 kUpperBoundInfinity = 0x10,
348 kLowerBoundNull = 0x20,
349 kUpperBoundNull = 0x40,
350 kContainEmpty = 0x80,
353using RangeFlags = USERVER_NAMESPACE::
utils::Flags<RangeFlag>;
355constexpr bool HasLowerBound(RangeFlags flags) {
356 return !(flags & RangeFlags{RangeFlag::kEmpty, RangeFlag::kLowerBoundNull,
357 RangeFlag::kLowerBoundInfinity});
360constexpr bool HasUpperBound(RangeFlags flags) {
361 return !(flags & RangeFlags{RangeFlag::kEmpty, RangeFlag::kUpperBoundNull,
362 RangeFlag::kUpperBoundInfinity});
366struct RangeBinaryParser : BufferParserBase<
Range<T>> {
367 using BaseType = BufferParserBase<
Range<T>>;
368 using ValueType =
typename BaseType::ValueType;
369 using ElementType = T;
370 using ElementParser =
typename traits::
IO<ElementType>::ParserType;
373 traits::kParserBufferCategory<ElementParser>;
375 using BaseType::BaseType;
377 void operator()(
FieldBuffer buffer,
const TypeBufferCategory& categories) {
378 char wire_range_flags{0};
381 RangeFlags range_flags(
static_cast<RangeFlag>(wire_range_flags));
383 ValueType wire_value;
384 if (range_flags != RangeFlag::kEmpty) {
385 RangeBounds bounds = RangeBound::kNone;
386 typename ValueType::OptionalValue lower;
387 typename ValueType::OptionalValue upper;
388 if (HasLowerBound(range_flags)) {
389 if (range_flags & RangeFlag::kLowerBoundInclusive) {
390 bounds |= RangeBound::kLower;
393 buffer.ReadRaw(tmp, categories, element_buffer_category);
396 if (HasUpperBound(range_flags)) {
397 if (range_flags & RangeFlag::kUpperBoundInclusive) {
398 bounds |= RangeBound::kUpper;
401 buffer.ReadRaw(tmp, categories, element_buffer_category);
404 wire_value = ValueType{lower, upper, bounds};
406 this->value = wire_value;
411struct RangeBinaryFormatter : BufferFormatterBase<
Range<T>> {
412 using BaseType = BufferFormatterBase<
Range<T>>;
414 using BaseType::BaseType;
416 template <
typename Buffer>
417 void operator()(
const UserTypes& types, Buffer& buffer)
const {
418 RangeFlags range_flags;
419 if (
this->value.Empty()) {
420 range_flags |= RangeFlag::kEmpty;
423 if (!
this->value.HasLowerBound()) {
424 range_flags |= RangeFlag::kLowerBoundInfinity;
425 }
else if (
this->value.IsLowerBoundIncluded()) {
426 range_flags |= RangeFlag::kLowerBoundInclusive;
428 if (!
this->value.HasUpperBound()) {
429 range_flags |= RangeFlag::kUpperBoundInfinity;
430 }
else if (
this->value.IsUpperBoundIncluded()) {
431 range_flags |= RangeFlag::kUpperBoundInclusive;
434 char wire_range_flags =
static_cast<
char>(range_flags.GetValue());
435 io::WriteBuffer(types, buffer, wire_range_flags);
436 if (!
this->value.Empty()) {
438 if (
this->value.HasLowerBound()) {
439 io::WriteRawBinary(types, buffer,
this->value.GetLowerBound());
441 if (
this->value.HasUpperBound()) {
442 io::WriteRawBinary(types, buffer,
this->value.GetUpperBound());
449struct BoundedRangeBinaryParser : BufferParserBase<
BoundedRange<T>> {
451 using ValueType =
typename BaseType::ValueType;
453 using BaseType::BaseType;
455 void operator()(
FieldBuffer buffer,
const TypeBufferCategory& categories) {
457 io::ReadBuffer(buffer, tmp, categories);
458 this->value = ValueType{std::move(tmp)};
463struct BoundedRangeBinaryFormatter : BufferFormatterBase<
BoundedRange<T>> {
466 using BaseType::BaseType;
468 template <
typename Buffer>
469 void operator()(
const UserTypes& types, Buffer& buffer)
const {
470 io::WriteBuffer(types, buffer,
this->value.GetUnboundedRange());
509struct CppToSystemPg<IntegerRange> : PredefinedOid<PredefinedOids::kInt4Range> {
512struct CppToSystemPg<BoundedIntegerRange>
513 : PredefinedOid<PredefinedOids::kInt4Range> {};
515struct CppToSystemPg<BigintRange> : PredefinedOid<PredefinedOids::kInt8Range> {
518struct CppToSystemPg<BoundedBigintRange>
519 : PredefinedOid<PredefinedOids::kInt8Range> {};
526template <
typename U,
typename>
529 if (lower == upper && bounds != RangeBound::kBoth) {
536template <
typename U,
typename>
542template <
typename U,
typename>
548Range<T>::Range(
const OptionalValue& lower,
const OptionalValue& upper,
550 : data{RangeData{lower, upper, bounds}} {}
553template <
typename U,
typename>
557bool Range<T>::operator==(
const Range& rhs)
const {
558 return (Empty() && rhs.Empty()) || (data == rhs.data);
562std::ostream& operator<<(std::ostream& os,
const Range<T>& val) {
563 if (val.Empty())
return os <<
"empty";
564 if (val.HasLowerBound() && val.IsLowerBoundIncluded())
568 if (val.HasLowerBound())
569 os << *val.GetLowerBound();
573 if (val.HasUpperBound())
574 os << *val.GetUpperBound();
577 if (val.HasUpperBound() && val.IsUpperBoundIncluded())
585bool Range<T>::RangeData::HasBound(RangeBounds side)
const {
586 if (side == RangeBound::kLower) {
589 if (side == RangeBound::kUpper) {
596bool Range<T>::RangeData::BoundEqual(
const RangeData& rhs,
597 RangeBounds side)
const {
598 bool has_bound = HasBound(side);
599 if (has_bound != rhs.HasBound(side)) {
605 const auto& lval = GetBoundValue(side);
606 const auto& rval = rhs.GetBoundValue(side);
607 if ((bounds & side) == (rhs.bounds & side)) {
611 if constexpr (kIsDiscreteValue) {
612 T diff = (side == RangeBound::kLower ? 1 : -1);
613 if (IsBoundIncluded(side)) {
614 return lval == rval + diff;
616 return lval + diff == rval;
623BoundedRange<T>::BoundedRange()
noexcept(kNothrowValueCtor)
624 : value_{T{}, T{}, RangeBound::kBoth} {}
627template <
typename U,
typename>
628BoundedRange<T>::BoundedRange(U&& lower, U&& upper, RangeBounds bounds)
629 : value_{std::forward<U>(lower), std::forward<U>(upper), bounds} {}
634 if (value_.Empty()) {
635 throw BoundedRangeError{
"empty range"};
637 if (!value_.HasLowerBound()) {
638 throw BoundedRangeError{
"lower bound is missing"};
640 if (!value_.HasUpperBound()) {
641 throw BoundedRangeError{
"upper bound is missing"};
647 return value_ == rhs.value_;
651std::ostream& operator<<(std::ostream& os,
const BoundedRange<T>& val) {
652 return os << val.GetUnboundedRange();