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 =
42 std::is_nothrow_default_constructible_v<T>;
43 static constexpr bool kNothrowValueCopy =
44 std::is_nothrow_copy_constructible_v<T>;
45 static constexpr bool kNothrowValueMove =
46 std::is_nothrow_move_constructible_v<T>;
47 static constexpr bool kIsDiscreteValue = std::is_integral_v<T>;
50 using OptionalValue = std::optional<T>;
75 Range(
const OptionalValue& lower,
const OptionalValue& upper,
85 bool operator==(
const Range& rhs)
const;
87 bool operator!=(
const Range& rhs)
const {
return !(*
this == rhs); }
89 bool Empty()
const {
return !data; }
94 bool HasLowerBound()
const {
95 return !!data && data->HasBound(RangeBound::kLower);
97 bool HasUpperBound()
const {
98 return !!data && data->HasBound(RangeBound::kUpper);
104 return data->GetOptionalValue(RangeBound::kLower);
112 return data->GetOptionalValue(RangeBound::kUpper);
117 bool IsLowerBoundIncluded()
const {
118 return !!data && data->IsBoundIncluded(RangeBound::kLower);
120 bool IsUpperBoundIncluded()
const {
121 return !!data && data->IsBoundIncluded(RangeBound::kUpper);
125 template <
typename U>
130 RangeData()
noexcept =
default;
132 template <
typename U>
133 RangeData(U&& lower, U&& upper, RangeBounds bounds)
134 : RangeData{OptionalValue{std::forward<U>(lower)},
135 OptionalValue{std::forward<U>(upper)}, bounds} {}
137 template <
typename U>
139 RangeBounds bounds)
noexcept(kNothrowValueCopy)
140 : RangeData{OptionalValue{std::forward<U>(lower)}, OptionalValue{},
143 template <
typename U>
145 RangeBounds bounds)
noexcept(kNothrowValueCopy)
146 : RangeData{OptionalValue{}, OptionalValue{std::forward<U>(upper)},
149 RangeData(OptionalValue low, OptionalValue up, RangeBounds bounds)
150 : bounds{bounds}, lower{std::move(low)}, upper{std::move(up)} {
151 if (lower && upper && *upper < *lower) {
152 throw LogicError(
"Range lower bound is greater than upper");
156 bool operator==(
const RangeData& rhs)
const {
157 return BoundEqual(rhs, RangeBound::kLower) &&
158 BoundEqual(rhs, RangeBound::kUpper);
161 bool operator!=(
const RangeData& rhs)
const {
return !(*
this == rhs); }
163 bool HasBound(RangeBounds side)
const;
165 bool IsBoundIncluded(RangeBounds side)
const {
166 return HasBound(side) && (bounds & side);
169 bool BoundEqual(
const RangeData& rhs, RangeBounds side)
const;
172 const T& GetBoundValue(RangeBounds side)
const {
173 if (side == RangeBound::kLower)
return *lower;
175 "Invalid bounds side argument value");
179 const OptionalValue& GetOptionalValue(RangeBounds side)
const {
180 if (side == RangeBound::kLower)
return lower;
182 "Invalid bounds side argument value");
186 RangeBounds bounds = RangeBound::kNone;
191 template <
typename U>
192 static OptionalValue ConvertBound(
const std::optional<U>& rhs) {
193 if (!rhs)
return OptionalValue{};
194 return OptionalValue{*rhs};
197 template <
typename U>
198 static std::optional<RangeData> ConvertData(
const Range<U>& rhs) {
199 if (!rhs.data)
return {};
200 return RangeData{ConvertBound(rhs.data->lower),
201 ConvertBound(rhs.data->upper), rhs.data->bounds};
204 std::optional<RangeData> data;
206 static const inline OptionalValue kNoValue{};
210auto MakeRange(T&& lower, T&& upper, RangeBounds bounds = RangeBound::kLower) {
211 using ElementType = std::decay_t<T>;
212 return Range<ElementType>{std::forward<T>(lower), std::forward<T>(upper),
218 RangeBounds bounds = RangeBound::kLower) {
219 using ElementType = std::decay_t<T>;
220 return Range<ElementType>{std::forward<T>(lower), kUnbounded, bounds};
225 RangeBounds bounds = RangeBound::kNone) {
226 using ElementType = std::decay_t<T>;
227 return Range<ElementType>{kUnbounded, std::forward<T>(upper), bounds};
230using IntegerRange =
Range<Integer>;
231using BigintRange =
Range<Bigint>;
235 static constexpr bool kNothrowValueCtor =
236 std::is_nothrow_default_constructible_v<T>;
241 BoundedRange()
noexcept(kNothrowValueCtor);
243 template <
typename U,
typename = std::enable_if_t<
244 std::is_convertible_v<std::decay_t<U>, T>>>
245 BoundedRange(U&& lower, U&& upper, RangeBounds bounds = RangeBound::kLower);
247 template <
typename U>
248 explicit BoundedRange(
Range<U>&&);
251 bool operator!=(
const BoundedRange& rhs)
const {
return !(*
this == rhs); }
253 const ValueType& GetLowerBound()
const {
return *value_.GetLowerBound(); }
254 bool IsLowerBoundIncluded()
const {
return value_.IsLowerBoundIncluded(); }
256 const ValueType& GetUpperBound()
const {
return *value_.GetUpperBound(); }
257 bool IsUpperBoundIncluded()
const {
return value_.IsUpperBoundIncluded(); }
259 const Range<T>& GetUnboundedRange()
const {
return value_; }
275enum class RangeFlag {
278 kLowerBoundInclusive = 0x02,
279 kUpperBoundInclusive = 0x04,
280 kLowerBoundInfinity = 0x08,
281 kUpperBoundInfinity = 0x10,
282 kLowerBoundNull = 0x20,
283 kUpperBoundNull = 0x40,
284 kContainEmpty = 0x80,
287using RangeFlags = USERVER_NAMESPACE::utils::Flags<RangeFlag>;
289constexpr bool HasLowerBound(RangeFlags flags) {
290 return !(flags & RangeFlags{RangeFlag::kEmpty, RangeFlag::kLowerBoundNull,
291 RangeFlag::kLowerBoundInfinity});
294constexpr bool HasUpperBound(RangeFlags flags) {
295 return !(flags & RangeFlags{RangeFlag::kEmpty, RangeFlag::kUpperBoundNull,
296 RangeFlag::kUpperBoundInfinity});
300struct RangeBinaryParser : BufferParserBase<
Range<T>> {
301 using BaseType = BufferParserBase<
Range<T>>;
302 using ValueType =
typename BaseType::ValueType;
303 using ElementType = T;
304 using ElementParser =
typename traits::
IO<ElementType>::ParserType;
307 traits::kParserBufferCategory<ElementParser>;
309 using BaseType::BaseType;
311 void operator()(
FieldBuffer buffer,
const TypeBufferCategory& categories) {
312 char wire_range_flags{0};
315 RangeFlags range_flags(
static_cast<RangeFlag>(wire_range_flags));
317 ValueType wire_value;
318 if (range_flags != RangeFlag::kEmpty) {
319 RangeBounds bounds = RangeBound::kNone;
320 typename ValueType::OptionalValue lower;
321 typename ValueType::OptionalValue upper;
322 if (HasLowerBound(range_flags)) {
323 if (range_flags & RangeFlag::kLowerBoundInclusive) {
324 bounds |= RangeBound::kLower;
327 buffer.ReadRaw(tmp, categories, element_buffer_category);
330 if (HasUpperBound(range_flags)) {
331 if (range_flags & RangeFlag::kUpperBoundInclusive) {
332 bounds |= RangeBound::kUpper;
335 buffer.ReadRaw(tmp, categories, element_buffer_category);
338 wire_value = ValueType{lower, upper, bounds};
340 this->value = wire_value;
345struct RangeBinaryFormatter : BufferFormatterBase<
Range<T>> {
346 using BaseType = BufferFormatterBase<
Range<T>>;
348 using BaseType::BaseType;
350 template <
typename Buffer>
351 void operator()(
const UserTypes& types, Buffer& buffer)
const {
352 RangeFlags range_flags;
353 if (
this->value.Empty()) {
354 range_flags |= RangeFlag::kEmpty;
357 if (!
this->value.HasLowerBound()) {
358 range_flags |= RangeFlag::kLowerBoundInfinity;
359 }
else if (
this->value.IsLowerBoundIncluded()) {
360 range_flags |= RangeFlag::kLowerBoundInclusive;
362 if (!
this->value.HasUpperBound()) {
363 range_flags |= RangeFlag::kUpperBoundInfinity;
364 }
else if (
this->value.IsUpperBoundIncluded()) {
365 range_flags |= RangeFlag::kUpperBoundInclusive;
368 char wire_range_flags =
static_cast<
char>(range_flags.GetValue());
369 io::WriteBuffer(types, buffer, wire_range_flags);
370 if (!
this->value.Empty()) {
372 if (
this->value.HasLowerBound()) {
373 io::WriteRawBinary(types, buffer,
this->value.GetLowerBound());
375 if (
this->value.HasUpperBound()) {
376 io::WriteRawBinary(types, buffer,
this->value.GetUpperBound());
383struct BoundedRangeBinaryParser : BufferParserBase<
BoundedRange<T>> {
385 using ValueType =
typename BaseType::ValueType;
387 using BaseType::BaseType;
389 void operator()(
FieldBuffer buffer,
const TypeBufferCategory& categories) {
391 io::ReadBuffer(buffer, tmp, categories);
392 this->value = ValueType{std::move(tmp)};
397struct BoundedRangeBinaryFormatter : BufferFormatterBase<
BoundedRange<T>> {
400 using BaseType::BaseType;
402 template <
typename Buffer>
403 void operator()(
const UserTypes& types, Buffer& buffer)
const {
404 io::WriteBuffer(types, buffer,
this->value.GetUnboundedRange());
443struct CppToSystemPg<IntegerRange> : PredefinedOid<PredefinedOids::kInt4Range> {
446struct CppToSystemPg<BoundedIntegerRange>
447 : PredefinedOid<PredefinedOids::kInt4Range> {};
449struct CppToSystemPg<BigintRange> : PredefinedOid<PredefinedOids::kInt8Range> {
452struct CppToSystemPg<BoundedBigintRange>
453 : PredefinedOid<PredefinedOids::kInt8Range> {};
460template <
typename U,
typename>
463 if (lower == upper && bounds != RangeBound::kBoth) {
470template <
typename U,
typename>
476template <
typename U,
typename>
482Range<T>::Range(
const OptionalValue& lower,
const OptionalValue& upper,
484 : data{RangeData{lower, upper, bounds}} {}
487template <
typename U,
typename>
491bool Range<T>::operator==(
const Range& rhs)
const {
492 return (Empty() && rhs.Empty()) || (data == rhs.data);
496std::ostream& operator<<(std::ostream& os,
const Range<T>& val) {
497 if (val.Empty())
return os <<
"empty";
498 if (val.HasLowerBound() && val.IsLowerBoundIncluded())
502 if (val.HasLowerBound())
503 os << *val.GetLowerBound();
507 if (val.HasUpperBound())
508 os << *val.GetUpperBound();
511 if (val.HasUpperBound() && val.IsUpperBoundIncluded())
519bool Range<T>::RangeData::HasBound(RangeBounds side)
const {
520 if (side == RangeBound::kLower) {
523 if (side == RangeBound::kUpper) {
530bool Range<T>::RangeData::BoundEqual(
const RangeData& rhs,
531 RangeBounds side)
const {
532 bool has_bound = HasBound(side);
533 if (has_bound != rhs.HasBound(side)) {
539 const auto& lval = GetBoundValue(side);
540 const auto& rval = rhs.GetBoundValue(side);
541 if ((bounds & side) == (rhs.bounds & side)) {
545 if constexpr (kIsDiscreteValue) {
546 T diff = (side == RangeBound::kLower ? 1 : -1);
547 if (IsBoundIncluded(side)) {
548 return lval == rval + diff;
550 return lval + diff == rval;
557BoundedRange<T>::BoundedRange()
noexcept(kNothrowValueCtor)
558 : value_{T{}, T{}, RangeBound::kBoth} {}
561template <
typename U,
typename>
562BoundedRange<T>::BoundedRange(U&& lower, U&& upper, RangeBounds bounds)
563 : value_{std::forward<U>(lower), std::forward<U>(upper), bounds} {}
568 if (value_.Empty()) {
569 throw BoundedRangeError{
"empty range"};
571 if (!value_.HasLowerBound()) {
572 throw BoundedRangeError{
"lower bound is missing"};
574 if (!value_.HasUpperBound()) {
575 throw BoundedRangeError{
"upper bound is missing"};
581 return value_ == rhs.value_;
585std::ostream& operator<<(std::ostream& os,
const BoundedRange<T>& val) {
586 return os << val.GetUnboundedRange();