10#include <fmt/format.h>
12#include <userver/storages/postgres/exceptions.hpp>
13#include <userver/storages/postgres/io/buffer_io_base.hpp>
14#include <userver/storages/postgres/io/field_buffer.hpp>
15#include <userver/storages/postgres/io/traits.hpp>
16#include <userver/storages/postgres/io/type_mapping.hpp>
17#include <userver/storages/postgres/io/type_traits.hpp>
18#include <userver/storages/postgres/io/user_types.hpp>
20#include <userver/utils/assert.hpp>
21#include <userver/utils/flags.hpp>
23USERVER_NAMESPACE_BEGIN
30enum class RangeBound {
34 kBoth = kLower | kUpper,
37using RangeBounds = USERVER_NAMESPACE::utils::Flags<RangeBound>;
41 static constexpr bool kNothrowValueCtor = std::is_nothrow_default_constructible_v<T>;
42 static constexpr bool kNothrowValueCopy = std::is_nothrow_copy_constructible_v<T>;
43 static constexpr bool kNothrowValueMove = std::is_nothrow_move_constructible_v<T>;
44 static constexpr bool kIsDiscreteValue = std::is_integral_v<T>;
47 using OptionalValue = std::optional<T>;
56 template <
typename U,
typename = std::enable_if_t<std::is_convertible_v<std::decay_t<U>, T>>>
57 Range(U&& lower, U&& upper, RangeBounds bounds = RangeBound::kLower);
60 template <
typename U,
typename = std::enable_if_t<std::is_convertible_v<std::decay_t<U>, T>>>
64 template <
typename U,
typename = std::enable_if_t<std::is_convertible_v<std::decay_t<U>, T>>>
67 Range(
const OptionalValue& lower,
const OptionalValue& upper, RangeBounds bounds);
72 template <
typename U,
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
75 bool operator==(
const Range& rhs)
const;
77 bool operator!=(
const Range& rhs)
const {
return !(*
this == rhs); }
79 bool Empty()
const {
return !data_; }
84 bool HasLowerBound()
const {
return !!data_ && data_->HasBound(RangeBound::kLower); }
85 bool HasUpperBound()
const {
return !!data_ && data_->HasBound(RangeBound::kUpper); }
90 return data_->GetOptionalValue(RangeBound::kLower);
98 return data_->GetOptionalValue(RangeBound::kUpper);
103 bool IsLowerBoundIncluded()
const {
return !!data_ && data_->IsBoundIncluded(RangeBound::kLower); }
104 bool IsUpperBoundIncluded()
const {
return !!data_ && data_->IsBoundIncluded(RangeBound::kUpper); }
107 template <
typename U>
112 RangeData()
noexcept =
default;
114 template <
typename U>
115 RangeData(U&& lower, U&& upper, RangeBounds bounds)
116 : RangeData{OptionalValue{std::forward<U>(lower)}, OptionalValue{std::forward<U>(upper)}, bounds}
119 template <
typename U>
120 RangeData(U&& lower,
UnboundedType, RangeBounds bounds)
noexcept(kNothrowValueCopy)
121 : RangeData{OptionalValue{std::forward<U>(lower)}, OptionalValue{}, bounds}
124 template <
typename U>
125 RangeData(
UnboundedType, U&& upper, RangeBounds bounds)
noexcept(kNothrowValueCopy)
126 : RangeData{OptionalValue{}, OptionalValue{std::forward<U>(upper)}, bounds}
129 RangeData(OptionalValue low, OptionalValue up, RangeBounds bounds)
131 lower{std::move(low)},
134 if (lower && upper && *upper < *lower) {
135 throw LogicError(
"Range lower bound is greater than upper");
139 bool operator==(
const RangeData& rhs)
const {
140 return BoundEqual(rhs, RangeBound::kLower) && BoundEqual(rhs, RangeBound::kUpper);
143 bool operator!=(
const RangeData& rhs)
const {
return !(*
this == rhs); }
145 bool HasBound(RangeBounds side)
const;
147 bool IsBoundIncluded(RangeBounds side)
const {
return HasBound(side) && (bounds & side); }
149 bool BoundEqual(
const RangeData& rhs, RangeBounds side)
const;
152 const T& GetBoundValue(RangeBounds side)
const {
153 if (side == RangeBound::kLower) {
156 UASSERT_MSG(side == RangeBound::kUpper,
"Invalid bounds side argument value");
160 const OptionalValue& GetOptionalValue(RangeBounds side)
const {
161 if (side == RangeBound::kLower) {
164 UASSERT_MSG(side == RangeBound::kUpper,
"Invalid bounds side argument value");
168 RangeBounds bounds = RangeBound::kNone;
173 template <
typename U>
174 static OptionalValue ConvertBound(
const std::optional<U>& rhs) {
176 return OptionalValue{};
178 return OptionalValue{*rhs};
181 template <
typename U>
182 static std::optional<RangeData> ConvertData(
const Range<U>& rhs) {
186 return RangeData{ConvertBound(rhs.data_->lower), ConvertBound(rhs.data_->upper), rhs.data_->bounds};
189 std::optional<RangeData> data_;
191 static const inline OptionalValue kNoValue{};
195auto MakeRange(T&& lower, T&& upper, RangeBounds bounds = RangeBound::kLower) {
196 using ElementType = std::decay_t<T>;
197 return Range<ElementType>{std::forward<T>(lower), std::forward<T>(upper), bounds};
201auto MakeRange(T&& lower,
UnboundedType, RangeBounds bounds = RangeBound::kLower) {
202 using ElementType = std::decay_t<T>;
203 return Range<ElementType>{std::forward<T>(lower), kUnbounded, bounds};
207auto MakeRange(
UnboundedType, T&& upper, RangeBounds bounds = RangeBound::kNone) {
208 using ElementType = std::decay_t<T>;
209 return Range<ElementType>{kUnbounded, std::forward<T>(upper), bounds};
212using IntegerRange =
Range<Integer>;
213using BigintRange =
Range<Bigint>;
217 static constexpr bool kNothrowValueCtor = std::is_nothrow_default_constructible_v<T>;
222 BoundedRange()
noexcept(kNothrowValueCtor);
224 template <
typename U,
typename = std::enable_if_t<std::is_convertible_v<std::decay_t<U>, T>>>
225 BoundedRange(U&& lower, U&& upper, RangeBounds bounds = RangeBound::kLower);
227 template <
typename U>
228 explicit BoundedRange(
Range<U>&&);
231 bool operator!=(
const BoundedRange& rhs)
const {
return !(*
this == rhs); }
233 const ValueType& GetLowerBound()
const {
return *value_.GetLowerBound(); }
234 bool IsLowerBoundIncluded()
const {
return value_.IsLowerBoundIncluded(); }
236 const ValueType& GetUpperBound()
const {
return *value_.GetUpperBound(); }
237 bool IsUpperBoundIncluded()
const {
return value_.IsUpperBoundIncluded(); }
239 const Range<T>& GetUnboundedRange()
const {
return value_; }
255enum class RangeFlag {
258 kLowerBoundInclusive = 0x02,
259 kUpperBoundInclusive = 0x04,
260 kLowerBoundInfinity = 0x08,
261 kUpperBoundInfinity = 0x10,
262 kLowerBoundNull = 0x20,
263 kUpperBoundNull = 0x40,
264 kContainEmpty = 0x80,
267using RangeFlags = USERVER_NAMESPACE::utils::Flags<RangeFlag>;
269constexpr bool HasLowerBound(RangeFlags flags) {
270 return !(flags & RangeFlags{RangeFlag::kEmpty, RangeFlag::kLowerBoundNull, RangeFlag::kLowerBoundInfinity});
273constexpr bool HasUpperBound(RangeFlags flags) {
274 return !(flags & RangeFlags{RangeFlag::kEmpty, RangeFlag::kUpperBoundNull, RangeFlag::kUpperBoundInfinity});
278struct RangeBinaryParser : BufferParserBase<
Range<T>> {
279 using BaseType = BufferParserBase<
Range<T>>;
280 using ValueType =
typename BaseType::ValueType;
281 using ElementType = T;
282 using ElementParser =
typename traits::
IO<ElementType>::ParserType;
284 static constexpr BufferCategory element_buffer_category =
traits::kParserBufferCategory<ElementParser>;
286 using BaseType::BaseType;
288 void operator()(
FieldBuffer buffer,
const TypeBufferCategory& categories) {
289 char wire_range_flags{0};
292 const RangeFlags range_flags(
static_cast<RangeFlag>(wire_range_flags));
294 ValueType wire_value;
295 if (range_flags != RangeFlag::kEmpty) {
296 RangeBounds bounds = RangeBound::kNone;
297 typename ValueType::OptionalValue lower;
298 typename ValueType::OptionalValue upper;
299 if (HasLowerBound(range_flags)) {
300 if (range_flags & RangeFlag::kLowerBoundInclusive) {
301 bounds |= RangeBound::kLower;
304 buffer.ReadRaw(tmp, categories, element_buffer_category);
307 if (HasUpperBound(range_flags)) {
308 if (range_flags & RangeFlag::kUpperBoundInclusive) {
309 bounds |= RangeBound::kUpper;
312 buffer.ReadRaw(tmp, categories, element_buffer_category);
315 wire_value = ValueType{lower, upper, bounds};
317 this->value = wire_value;
322struct RangeBinaryFormatter : BufferFormatterBase<
Range<T>> {
323 using BaseType = BufferFormatterBase<
Range<T>>;
325 using BaseType::BaseType;
327 template <
typename Buffer>
328 void operator()(
const UserTypes& types, Buffer& buffer)
const {
329 RangeFlags range_flags;
330 if (
this->value.Empty()) {
331 range_flags |= RangeFlag::kEmpty;
334 if (!
this->value.HasLowerBound()) {
335 range_flags |= RangeFlag::kLowerBoundInfinity;
336 }
else if (
this->value.IsLowerBoundIncluded()) {
337 range_flags |= RangeFlag::kLowerBoundInclusive;
339 if (!
this->value.HasUpperBound()) {
340 range_flags |= RangeFlag::kUpperBoundInfinity;
341 }
else if (
this->value.IsUpperBoundIncluded()) {
342 range_flags |= RangeFlag::kUpperBoundInclusive;
345 const char wire_range_flags =
static_cast<
char>(range_flags.GetValue());
346 io::WriteBuffer(types, buffer, wire_range_flags);
347 if (!
this->value.Empty()) {
349 if (
this->value.HasLowerBound()) {
350 io::WriteRawBinary(types, buffer,
this->value.GetLowerBound());
352 if (
this->value.HasUpperBound()) {
353 io::WriteRawBinary(types, buffer,
this->value.GetUpperBound());
360struct BoundedRangeBinaryParser : BufferParserBase<
BoundedRange<T>> {
362 using ValueType =
typename BaseType::ValueType;
364 using BaseType::BaseType;
366 void operator()(
FieldBuffer buffer,
const TypeBufferCategory& categories) {
368 io::ReadBuffer(buffer, tmp, categories);
369 this->value = ValueType{std::move(tmp)};
374struct BoundedRangeBinaryFormatter : BufferFormatterBase<
BoundedRange<T>> {
377 using BaseType::BaseType;
379 template <
typename Buffer>
380 void operator()(
const UserTypes& types, Buffer& buffer)
const {
381 io::WriteBuffer(types, buffer,
this->value.GetUnboundedRange());
391 using type = io::detail::RangeBinaryParser<T>;
400 using type = io::detail::RangeBinaryFormatter<T>;
405 using type = io::detail::BoundedRangeBinaryParser<T>;
414 using type = io::detail::BoundedRangeBinaryFormatter<T>;
420struct CppToSystemPg<IntegerRange> : PredefinedOid<
PredefinedOids::kInt4Range> {};
422struct CppToSystemPg<BoundedIntegerRange> : PredefinedOid<
PredefinedOids::kInt4Range> {};
426struct CppToSystemPg<BoundedBigintRange> : PredefinedOid<
PredefinedOids::kInt8Range> {};
433template <
typename U,
typename>
435 : data_{RangeData{std::forward<U>(lower), std::forward<U>(upper), bounds}}
437 if (lower == upper && bounds != RangeBound::kBoth) {
444template <
typename U,
typename>
446 : data_{RangeData{std::forward<U>(lower), ub, bounds}}
450template <
typename U,
typename>
452 : data_{RangeData{ub, std::forward<U>(upper), bounds}}
456Range<T>::Range(
const OptionalValue& lower,
const OptionalValue& upper, RangeBounds bounds)
457 : data_{RangeData{lower, upper, bounds}}
461template <
typename U,
typename>
463 : data_{ConvertData(rhs)}
467bool Range<T>::operator==(
const Range& rhs)
const {
468 return (Empty() && rhs.Empty()) || (data_ == rhs.data_);
472std::ostream& operator<<(std::ostream& os,
const Range<T>& val) {
474 return os <<
"empty";
476 if (val.HasLowerBound() && val.IsLowerBoundIncluded()) {
481 if (val.HasLowerBound()) {
482 os << *val.GetLowerBound();
487 if (val.HasUpperBound()) {
488 os << *val.GetUpperBound();
492 if (val.HasUpperBound() && val.IsUpperBoundIncluded()) {
501bool Range<T>::RangeData::HasBound(RangeBounds side)
const {
502 if (side == RangeBound::kLower) {
505 if (side == RangeBound::kUpper) {
512bool Range<T>::RangeData::BoundEqual(
const RangeData& rhs, RangeBounds side)
const {
513 const bool has_bound = HasBound(side);
514 if (has_bound != rhs.HasBound(side)) {
520 const auto& lval = GetBoundValue(side);
521 const auto& rval = rhs.GetBoundValue(side);
522 if ((bounds & side) == (rhs.bounds & side)) {
526 if constexpr (kIsDiscreteValue) {
527 T diff = (side == RangeBound::kLower ? 1 : -1);
528 if (IsBoundIncluded(side)) {
529 return lval == rval + diff;
531 return lval + diff == rval;
538BoundedRange<T>::BoundedRange()
noexcept(kNothrowValueCtor)
539 : value_{T{}, T{}, RangeBound::kBoth}
543template <
typename U,
typename>
544BoundedRange<T>::BoundedRange(U&& lower, U&& upper, RangeBounds bounds)
545 : value_{std::forward<U>(lower), std::forward<U>(upper), bounds}
551 : value_{std::move(rhs)}
553 if (value_.Empty()) {
556 if (!value_.HasLowerBound()) {
559 if (!value_.HasUpperBound()) {
566 return value_ == rhs.value_;
570std::ostream& operator<<(std::ostream& os,
const BoundedRange<T>& val) {
571 return os << val.GetUnboundedRange();