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>;
67 Range(
const OptionalValue& lower,
const OptionalValue& upper, RangeBounds bounds);
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} {}
118 template <
typename U>
119 RangeData(U&& lower,
UnboundedType, RangeBounds bounds)
noexcept(kNothrowValueCopy)
120 : RangeData{OptionalValue{std::forward<U>(lower)}, OptionalValue{}, bounds} {}
122 template <
typename U>
123 RangeData(
UnboundedType, U&& upper, RangeBounds bounds)
noexcept(kNothrowValueCopy)
124 : RangeData{OptionalValue{}, OptionalValue{std::forward<U>(upper)}, bounds} {}
126 RangeData(OptionalValue low, OptionalValue up, RangeBounds bounds)
127 : bounds{bounds}, lower{std::move(low)}, upper{std::move(up)} {
128 if (lower && upper && *upper < *lower) {
129 throw LogicError(
"Range lower bound is greater than upper");
133 bool operator==(
const RangeData& rhs)
const {
134 return BoundEqual(rhs, RangeBound::kLower) && BoundEqual(rhs, RangeBound::kUpper);
137 bool operator!=(
const RangeData& rhs)
const {
return !(*
this == rhs); }
139 bool HasBound(RangeBounds side)
const;
141 bool IsBoundIncluded(RangeBounds side)
const {
return HasBound(side) && (bounds & side); }
143 bool BoundEqual(
const RangeData& rhs, RangeBounds side)
const;
146 const T& GetBoundValue(RangeBounds side)
const {
147 if (side == RangeBound::kLower)
return *lower;
148 UASSERT_MSG(side == RangeBound::kUpper,
"Invalid bounds side argument value");
152 const OptionalValue& GetOptionalValue(RangeBounds side)
const {
153 if (side == RangeBound::kLower)
return lower;
154 UASSERT_MSG(side == RangeBound::kUpper,
"Invalid bounds side argument value");
158 RangeBounds bounds = RangeBound::kNone;
163 template <
typename U>
164 static OptionalValue ConvertBound(
const std::optional<U>& rhs) {
165 if (!rhs)
return OptionalValue{};
166 return OptionalValue{*rhs};
169 template <
typename U>
170 static std::optional<RangeData> ConvertData(
const Range<U>& rhs) {
171 if (!rhs.data)
return {};
172 return RangeData{ConvertBound(rhs.data->lower), ConvertBound(rhs.data->upper), rhs.data->bounds};
175 std::optional<RangeData> data;
177 static const inline OptionalValue kNoValue{};
181auto MakeRange(T&& lower, T&& upper, RangeBounds bounds = RangeBound::kLower) {
182 using ElementType = std::decay_t<T>;
183 return Range<ElementType>{std::forward<T>(lower), std::forward<T>(upper), bounds};
187auto MakeRange(T&& lower,
UnboundedType, RangeBounds bounds = RangeBound::kLower) {
188 using ElementType = std::decay_t<T>;
189 return Range<ElementType>{std::forward<T>(lower), kUnbounded, bounds};
193auto MakeRange(
UnboundedType, T&& upper, RangeBounds bounds = RangeBound::kNone) {
194 using ElementType = std::decay_t<T>;
195 return Range<ElementType>{kUnbounded, std::forward<T>(upper), bounds};
198using IntegerRange =
Range<Integer>;
199using BigintRange =
Range<Bigint>;
203 static constexpr bool kNothrowValueCtor = std::is_nothrow_default_constructible_v<T>;
208 BoundedRange()
noexcept(kNothrowValueCtor);
210 template <
typename U,
typename = std::enable_if_t<std::is_convertible_v<std::decay_t<U>, T>>>
211 BoundedRange(U&& lower, U&& upper, RangeBounds bounds = RangeBound::kLower);
213 template <
typename U>
214 explicit BoundedRange(
Range<U>&&);
217 bool operator!=(
const BoundedRange& rhs)
const {
return !(*
this == rhs); }
219 const ValueType& GetLowerBound()
const {
return *value_.GetLowerBound(); }
220 bool IsLowerBoundIncluded()
const {
return value_.IsLowerBoundIncluded(); }
222 const ValueType& GetUpperBound()
const {
return *value_.GetUpperBound(); }
223 bool IsUpperBoundIncluded()
const {
return value_.IsUpperBoundIncluded(); }
225 const Range<T>& GetUnboundedRange()
const {
return value_; }
241enum class RangeFlag {
244 kLowerBoundInclusive = 0x02,
245 kUpperBoundInclusive = 0x04,
246 kLowerBoundInfinity = 0x08,
247 kUpperBoundInfinity = 0x10,
248 kLowerBoundNull = 0x20,
249 kUpperBoundNull = 0x40,
250 kContainEmpty = 0x80,
253using RangeFlags = USERVER_NAMESPACE::
utils::Flags<RangeFlag>;
255constexpr bool HasLowerBound(RangeFlags flags) {
256 return !(flags & RangeFlags{RangeFlag::kEmpty, RangeFlag::kLowerBoundNull, RangeFlag::kLowerBoundInfinity});
259constexpr bool HasUpperBound(RangeFlags flags) {
260 return !(flags & RangeFlags{RangeFlag::kEmpty, RangeFlag::kUpperBoundNull, RangeFlag::kUpperBoundInfinity});
264struct RangeBinaryParser : BufferParserBase<
Range<T>> {
265 using BaseType = BufferParserBase<
Range<T>>;
266 using ValueType =
typename BaseType::ValueType;
267 using ElementType = T;
268 using ElementParser =
typename traits::
IO<ElementType>::ParserType;
270 static constexpr BufferCategory element_buffer_category =
traits::kParserBufferCategory<ElementParser>;
272 using BaseType::BaseType;
274 void operator()(
FieldBuffer buffer,
const TypeBufferCategory& categories) {
275 char wire_range_flags{0};
278 RangeFlags range_flags(
static_cast<RangeFlag>(wire_range_flags));
280 ValueType wire_value;
281 if (range_flags != RangeFlag::kEmpty) {
282 RangeBounds bounds = RangeBound::kNone;
283 typename ValueType::OptionalValue lower;
284 typename ValueType::OptionalValue upper;
285 if (HasLowerBound(range_flags)) {
286 if (range_flags & RangeFlag::kLowerBoundInclusive) {
287 bounds |= RangeBound::kLower;
290 buffer.ReadRaw(tmp, categories, element_buffer_category);
293 if (HasUpperBound(range_flags)) {
294 if (range_flags & RangeFlag::kUpperBoundInclusive) {
295 bounds |= RangeBound::kUpper;
298 buffer.ReadRaw(tmp, categories, element_buffer_category);
301 wire_value = ValueType{lower, upper, bounds};
303 this->value = wire_value;
308struct RangeBinaryFormatter : BufferFormatterBase<
Range<T>> {
309 using BaseType = BufferFormatterBase<
Range<T>>;
311 using BaseType::BaseType;
313 template <
typename Buffer>
314 void operator()(
const UserTypes& types, Buffer& buffer)
const {
315 RangeFlags range_flags;
316 if (
this->value.Empty()) {
317 range_flags |= RangeFlag::kEmpty;
320 if (!
this->value.HasLowerBound()) {
321 range_flags |= RangeFlag::kLowerBoundInfinity;
322 }
else if (
this->value.IsLowerBoundIncluded()) {
323 range_flags |= RangeFlag::kLowerBoundInclusive;
325 if (!
this->value.HasUpperBound()) {
326 range_flags |= RangeFlag::kUpperBoundInfinity;
327 }
else if (
this->value.IsUpperBoundIncluded()) {
328 range_flags |= RangeFlag::kUpperBoundInclusive;
331 char wire_range_flags =
static_cast<
char>(range_flags.GetValue());
332 io::WriteBuffer(types, buffer, wire_range_flags);
333 if (!
this->value.Empty()) {
335 if (
this->value.HasLowerBound()) {
336 io::WriteRawBinary(types, buffer,
this->value.GetLowerBound());
338 if (
this->value.HasUpperBound()) {
339 io::WriteRawBinary(types, buffer,
this->value.GetUpperBound());
346struct BoundedRangeBinaryParser : BufferParserBase<
BoundedRange<T>> {
348 using ValueType =
typename BaseType::ValueType;
350 using BaseType::BaseType;
352 void operator()(
FieldBuffer buffer,
const TypeBufferCategory& categories) {
354 io::ReadBuffer(buffer, tmp, categories);
355 this->value = ValueType{std::move(tmp)};
360struct BoundedRangeBinaryFormatter : BufferFormatterBase<
BoundedRange<T>> {
363 using BaseType::BaseType;
365 template <
typename Buffer>
366 void operator()(
const UserTypes& types, Buffer& buffer)
const {
367 io::WriteBuffer(types, buffer,
this->value.GetUnboundedRange());
406struct CppToSystemPg<IntegerRange> : PredefinedOid<PredefinedOids::kInt4Range> {};
408struct CppToSystemPg<BoundedIntegerRange> : PredefinedOid<PredefinedOids::kInt4Range> {};
410struct CppToSystemPg<BigintRange> : PredefinedOid<PredefinedOids::kInt8Range> {};
412struct CppToSystemPg<BoundedBigintRange> : PredefinedOid<PredefinedOids::kInt8Range> {};
419template <
typename U,
typename>
422 if (lower == upper && bounds != RangeBound::kBoth) {
429template <
typename U,
typename>
434template <
typename U,
typename>
439Range<T>::Range(
const OptionalValue& lower,
const OptionalValue& upper, RangeBounds bounds)
440 : data{RangeData{lower, upper, bounds}} {}
443template <
typename U,
typename>
447bool Range<T>::operator==(
const Range& rhs)
const {
448 return (Empty() && rhs.Empty()) || (data == rhs.data);
452std::ostream& operator<<(std::ostream& os,
const Range<T>& val) {
453 if (val.Empty())
return os <<
"empty";
454 if (val.HasLowerBound() && val.IsLowerBoundIncluded())
458 if (val.HasLowerBound())
459 os << *val.GetLowerBound();
463 if (val.HasUpperBound())
464 os << *val.GetUpperBound();
467 if (val.HasUpperBound() && val.IsUpperBoundIncluded())
475bool Range<T>::RangeData::HasBound(RangeBounds side)
const {
476 if (side == RangeBound::kLower) {
479 if (side == RangeBound::kUpper) {
486bool Range<T>::RangeData::BoundEqual(
const RangeData& rhs, RangeBounds side)
const {
487 bool has_bound = HasBound(side);
488 if (has_bound != rhs.HasBound(side)) {
494 const auto& lval = GetBoundValue(side);
495 const auto& rval = rhs.GetBoundValue(side);
496 if ((bounds & side) == (rhs.bounds & side)) {
500 if constexpr (kIsDiscreteValue) {
501 T diff = (side == RangeBound::kLower ? 1 : -1);
502 if (IsBoundIncluded(side)) {
503 return lval == rval + diff;
505 return lval + diff == rval;
512BoundedRange<T>::BoundedRange()
noexcept(kNothrowValueCtor) : value_{T{}, T{}, RangeBound::kBoth} {}
515template <
typename U,
typename>
516BoundedRange<T>::BoundedRange(U&& lower, U&& upper, RangeBounds bounds)
517 : value_{std::forward<U>(lower), std::forward<U>(upper), bounds} {}
522 if (value_.Empty()) {
523 throw BoundedRangeError{
"empty range"};
525 if (!value_.HasLowerBound()) {
526 throw BoundedRangeError{
"lower bound is missing"};
528 if (!value_.HasUpperBound()) {
529 throw BoundedRangeError{
"upper bound is missing"};
535 return value_ == rhs.value_;
539std::ostream& operator<<(std::ostream& os,
const BoundedRange<T>& val) {
540 return os << val.GetUnboundedRange();