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();