11#include <unordered_set> 
   14#include <boost/pfr/core.hpp> 
   16#include <userver/utils/impl/projecting_view.hpp> 
   18#include <userver/storages/postgres/exceptions.hpp> 
   19#include <userver/storages/postgres/io/buffer_io_base.hpp> 
   20#include <userver/storages/postgres/io/field_buffer.hpp> 
   21#include <userver/storages/postgres/io/row_types.hpp> 
   22#include <userver/storages/postgres/io/traits.hpp> 
   23#include <userver/storages/postgres/io/type_mapping.hpp> 
   24#include <userver/storages/postgres/io/type_traits.hpp> 
   25#include <userver/storages/postgres/io/user_types.hpp> 
   27USERVER_NAMESPACE_BEGIN
 
   33template <
typename Container>
 
   34struct HasFixedDimensions;
 
   38template <
typename Container>
 
   39struct HasFixedDimensionsImpl {
 
   41      typename HasFixedDimensions<
typename Container::value_type>::type;
 
   47struct HasFixedDimensions
 
   48    : std::conditional_t<kIsFixedSizeContainer<T>,
 
   49                         detail::HasFixedDimensionsImpl<T>,
 
   50                         BoolConstant<!kIsCompatibleContainer<T>>>::type {};
 
   52template <
typename Container>
 
   53inline constexpr bool kHasFixedDimensions =
 
   54    HasFixedDimensions<Container>::value;
 
   57struct FixedDimensions;
 
   62template <
typename T, std::size_t N>
 
   67inline constexpr std::size_t kDimensionSize = DimensionSize<T>::value;
 
   71template <
typename A, 
typename B>
 
   74template <
typename T, T... U, T... V>
 
   75struct JoinSequences<std::integer_sequence<T, U...>,
 
   76                     std::integer_sequence<T, V...>> {
 
   77  using type = std::integer_sequence<T, U..., V...>;
 
   81struct FixedDimensionsImpl {
 
   82  static_assert(kIsFixedSizeContainer<T>, 
"Container must have fixed size");
 
   83  using type = 
typename JoinSequences<
 
   84      std::integer_sequence<std::size_t, kDimensionSize<T>>,
 
   85      typename FixedDimensions<
typename T::value_type>::type>::type;
 
   89struct FixedDimensionsNonContainer {
 
   90  using type = std::integer_sequence<std::size_t>;
 
   95template <
typename T, T... Values>
 
   96constexpr std::array<T, 
sizeof...(Values)> MakeArray(
 
   97    const std::integer_sequence<T, Values...>&) {
 
  102struct FixedDimensions
 
  103    : std::conditional_t<kIsFixedSizeContainer<T>,
 
  104                         detail::FixedDimensionsImpl<T>,
 
  105                         detail::FixedDimensionsNonContainer<T>> {};
 
  111template <
typename Element>
 
  112inline bool ForceInitElementMapping() {
 
  114  if constexpr (
io::
traits::kIsMappedToPg<Element> ||
 
  115                !
io::
traits::kIsCompositeType<Element>) {
 
  116    return ForceReference(CppToPg<Element>::init_);
 
  122template <
typename Container>
 
  123struct ArrayBinaryParser : BufferParserBase<Container> {
 
  124  using BaseType = BufferParserBase<Container>;
 
  125  using ValueType = 
typename BaseType::ValueType;
 
  126  using ElementType = 
typename traits::ContainerFinalElement<Container>::type;
 
  127  constexpr static std::size_t dimensions = 
traits::kDimensionCount<Container>;
 
  128  using Dimensions = std::array<std::size_t, dimensions>;
 
  129  using DimensionIterator = 
typename Dimensions::iterator;
 
  130  using DimensionConstIterator = 
typename Dimensions::const_iterator;
 
  131  using ElementMapping = CppToPg<ElementType>;
 
  133  using BaseType::BaseType;
 
  135  void operator()(
FieldBuffer buffer, 
const TypeBufferCategory& categories) {
 
  139    Integer dim_count{0};
 
  141    if (dim_count != 
static_cast<Integer>(dimensions) &&
 
  142        ForceInitElementMapping<ElementType>()) {
 
  143      if (dim_count == 0) {
 
  145        swap(
this->value, empty);
 
  160    auto elem_category = GetTypeBufferCategory(categories, elem_oid);
 
  163    Dimensions on_the_wire;
 
  164    for (
auto& dim : on_the_wire) {
 
  172    if (!CheckDimensions(on_the_wire)) {
 
  177    ReadDimension(buffer, on_the_wire.begin(), elem_category, categories, tmp);
 
  178    swap(
this->value, tmp);
 
  182  bool CheckDimensions(
const Dimensions& dims) 
const {
 
  183    if constexpr (
traits::kHasFixedDimensions<Container>) {
 
  184      return dims == 
traits::MakeArray(
 
  185                         typename traits::FixedDimensions<Container>::type{});
 
  187    return CheckDimensions<ValueType>(dims.begin());
 
  189  template <
typename Element>
 
  190  bool CheckDimensions([[maybe_unused]] DimensionConstIterator dim) 
const {
 
  191    if constexpr (
traits::kIsFixedSizeContainer<Element>) {
 
  193      if (*dim != 
traits::kDimensionSize<Element>) {
 
  196      if constexpr (
traits::kDimensionCount<Element> == 1) {
 
  199        return CheckDimensions<
typename Element::value_type>(dim + 1);
 
  201    } 
else if constexpr (
traits::kIsCompatibleContainer<Element>) {
 
  202      if constexpr (
traits::kDimensionCount<Element> == 1) {
 
  205        return CheckDimensions<
typename Element::value_type>(dim + 1);
 
  211  template <
typename T>
 
  212  auto GetInserter(T& value) {
 
  213    return std::inserter(value, value.end());
 
  216  template <
typename T, std::size_t n>
 
  217  auto GetInserter(std::array<T, n>& array) {
 
  218    return array.begin();
 
  221  template <
typename Element>
 
  222  void ReadDimension(
FieldBuffer& buffer, DimensionConstIterator dim,
 
  224                     const TypeBufferCategory& categories, Element& elem) {
 
  225    if constexpr (
traits::kIsCompatibleContainer<Element>) {
 
  226      if constexpr (
traits::kCanClear<Element>) {
 
  229      if constexpr (
traits::kCanReserve<Element>) {
 
  232      auto it = GetInserter(elem);
 
  233      for (std::size_t i = 0; i < *dim; ++i) {
 
  234        typename Element::value_type val;
 
  235        if constexpr (1 < 
traits::kDimensionCount<Element>) {
 
  237          ReadDimension(buffer, dim + 1, elem_category, categories, val);
 
  239          buffer.ReadRaw(val, categories, elem_category);
 
  241        *it++ = std::move(val);
 
  246  void ReadDimension(
FieldBuffer& buffer, DimensionConstIterator dim,
 
  248                     const TypeBufferCategory& categories,
 
  249                     std::vector<
bool>& elem) {
 
  252    auto value = elem.begin();
 
  253    for (std::size_t i = 0; i < *dim; ++i) {
 
  255      buffer.ReadRaw(val, categories, elem_category);
 
  261template <
typename Container>
 
  262struct ArrayBinaryFormatter : BufferFormatterBase<Container> {
 
  263  using BaseType = BufferFormatterBase<Container>;
 
  264  using ValueType = 
typename BaseType::ValueType;
 
  265  using ArrayMapping = CppToPg<Container>;
 
  266  using ElementType = 
typename traits::ContainerFinalElement<Container>::type;
 
  267  using ElementMapping = CppToPg<ElementType>;
 
  268  constexpr static std::size_t dimensions = 
traits::kDimensionCount<Container>;
 
  269  using Dimensions = std::array<std::size_t, dimensions>;
 
  270  using DimensionIterator = 
typename Dimensions::iterator;
 
  271  using DimensionConstIterator = 
typename Dimensions::const_iterator;
 
  273  using BaseType::BaseType;
 
  276  template <
typename Buffer>
 
  277  void operator()(
const UserTypes& types, Buffer& buffer,
 
  278                  Oid replace_oid = kInvalidOid) 
const {
 
  279    auto elem_type_oid = ElementMapping::GetOid(types);
 
  280    if (replace_oid != kInvalidOid &&
 
  281        replace_oid != ArrayMapping::GetOid(types)) {
 
  282      elem_type_oid = types.FindElementOid(replace_oid);
 
  286    if (
this->value.empty()) {
 
  287      io::WriteBuffer(types, buffer, 
static_cast<Integer>(0));  
 
  288      io::WriteBuffer(types, buffer, 
static_cast<Integer>(0));  
 
  289      io::WriteBuffer(types, buffer, 
static_cast<Integer>(elem_type_oid));
 
  294    io::WriteBuffer(types, buffer, 
static_cast<Integer>(dimensions));
 
  296    io::WriteBuffer(types, buffer, 
static_cast<Integer>(0));
 
  298    io::WriteBuffer(types, buffer, 
static_cast<Integer>(elem_type_oid));
 
  299    Dimensions dims = GetDimensions();
 
  301    WriteDimensionData(types, buffer, dims);
 
  303    WriteData(types, dims.begin(), buffer, 
this->value);
 
  307  template <
typename Element>
 
  308  void CalculateDimensions([[maybe_unused]] DimensionIterator dim,
 
  309                           const Element& element) 
const {
 
  310    if constexpr (
traits::kIsCompatibleContainer<Element>) {
 
  311      *dim = element.size();
 
  312      if (!element.empty()) {
 
  313        CalculateDimensions(dim + 1, *element.begin());
 
  317  Dimensions GetDimensions() 
const {
 
  318    if constexpr (
traits::kHasFixedDimensions<Container>) {
 
  320          typename traits::FixedDimensions<Container>::type{});
 
  323      CalculateDimensions(dims.begin(), 
this->value);
 
  327  template <
typename Buffer>
 
  328  void WriteDimensionData(
const UserTypes& types, Buffer& buffer,
 
  329                          const Dimensions& dims) 
const {
 
  330    for (
auto dim : dims) {
 
  331      io::WriteBuffer(types, buffer, 
static_cast<Integer>(dim));
 
  332      io::WriteBuffer(types, buffer, 
static_cast<Integer>(1));  
 
  336  template <
typename Buffer, 
typename Element>
 
  337  void WriteData(
const UserTypes& types, DimensionConstIterator dim,
 
  338                 Buffer& buffer, 
const Element& element) 
const {
 
  339    if (*dim != element.size()) {
 
  342    if constexpr (1 < 
traits::kDimensionCount<Element>) {
 
  344      for (
const auto& sub : element) {
 
  345        WriteData(types, dim + 1, buffer, sub);
 
  349      for (
const auto& sub : element) {
 
  350        io::WriteRawBinary(types, buffer, sub);
 
  355  template <
typename Buffer>
 
  356  void WriteData(
const UserTypes& types, DimensionConstIterator dim,
 
  357                 Buffer& buffer, 
const std::vector<
bool>& element) 
const {
 
  358    if (*dim != element.size()) {
 
  359      throw InvalidDimensions{*dim, element.size()};
 
  361    for (
bool sub : element) {
 
  362      io::WriteRawBinary(types, buffer, sub);
 
  367template <
typename Container, 
bool System>
 
  369  using Type = Container;
 
  370  using ElementType = 
typename traits::ContainerFinalElement<Container>::type;
 
  371  using ElementMapping = CppToPg<ElementType>;
 
  372  using Mapping = ArrayPgOid<Container, System>;
 
  374  static Oid GetOid(
const UserTypes& types) {
 
  375    return ElementMapping::GetArrayOid(types);
 
  379template <
typename Container>
 
  380struct ArrayPgOid<Container, 
true> {
 
  381  using Type = Container;
 
  382  using ElementType = 
typename traits::ContainerFinalElement<Container>::type;
 
  383  using ElementMapping = CppToPg<ElementType>;
 
  384  using Mapping = ArrayPgOid<Container, 
true>;
 
  386  static constexpr Oid GetOid(
const UserTypes&) {
 
  387    return static_cast<Oid>(ElementMapping::array_oid);
 
  391template <
typename Container>
 
  392constexpr bool IsElementMappedToSystem() {
 
  393  if constexpr (!
traits::kIsCompatibleContainer<Container>) {
 
  396    return IsTypeMappedToSystem<
 
  397        typename traits::ContainerFinalElement<Container>::type>();
 
  401template <
typename Container>
 
  402constexpr bool EnableArrayParser() {
 
  403  if constexpr (!
traits::kIsCompatibleContainer<Container>) {
 
  406    using ElementType = 
typename traits::ContainerFinalElement<Container>::type;
 
  407    return traits::kHasParser<ElementType>;
 
  410template <
typename Container>
 
  411inline constexpr bool kEnableArrayParser = EnableArrayParser<Container>();
 
  413template <
typename Container>
 
  414constexpr bool EnableArrayFormatter() {
 
  415  if constexpr (!
traits::kIsCompatibleContainer<Container>) {
 
  418    using ElementType = 
typename traits::ContainerFinalElement<Container>::type;
 
  419    return traits::kHasFormatter<ElementType>;
 
  422template <
typename Container>
 
  423inline constexpr bool kEnableArrayFormatter = EnableArrayFormatter<Container>();
 
  432constexpr bool IsTypeMappedToSystemArray() {
 
  433  return traits::kIsMappedToPg<T> &&
 
  435             typename CppToPg<T>::Mapping,
 
  436             io::detail::ArrayPgOid<
typename CppToPg<T>::Type, 
true>>::value;
 
  458template <
typename... T>
 
  462template <
typename T, std::size_t Size>
 
  466template <
typename... T>
 
  470template <
typename... T>
 
  481template <
typename Container>
 
  482class ContainerChunk {
 
  486      "Only containers explicitly declared as compatible are supported");
 
  488  using value_type = 
typename Container::value_type;
 
  489  using const_iterator_type = 
typename Container::const_iterator;
 
  491  ContainerChunk(const_iterator_type begin, std::size_t size)
 
  492      : begin_{begin}, end_{std::next(begin, size)}, size_{size} {}
 
  494  std::size_t size() 
const { 
return size_; }
 
  495  bool empty() 
const { 
return begin_ == end_; }
 
  497  const_iterator_type begin() 
const { 
return begin_; }
 
  498  const_iterator_type cbegin() 
const { 
return begin_; }
 
  500  const_iterator_type end() 
const { 
return end_; }
 
  501  const_iterator_type cend() 
const { 
return end_; }
 
  504  const_iterator_type begin_;
 
  505  const_iterator_type end_;
 
  510template <
typename Container>
 
  511class ContainerSplitter {
 
  515      "Only containers explicitly declared as compatible are supported");
 
  517  using value_type = ContainerChunk<Container>;
 
  519  class ChunkIterator {
 
  521    using UnderlyingIterator = 
typename Container::const_iterator;
 
  522    ChunkIterator(
const Container& container, UnderlyingIterator current,
 
  523                  std::size_t chunk_elements)
 
  524        : container_{container},
 
  525          chunk_size_{chunk_elements},
 
  527              static_cast<size_t>(std::distance(current, container_.end()))},
 
  530    bool operator==(
const ChunkIterator& rhs) 
const {
 
  531      return current_ == rhs.current_;
 
  534    bool operator!=(
const ChunkIterator& rhs) 
const { 
return !(*
this == rhs); }
 
  536    value_type operator*() 
const { 
return {current_, NextStep()}; }
 
  538    ChunkIterator& operator++() {
 
  539      auto step = NextStep();
 
  540      std::advance(current_, step);
 
  545    ChunkIterator operator++(
int) {
 
  546      ChunkIterator tmp{*
this};
 
  551    std::size_t TailSize() 
const { 
return tail_size_; }
 
  554    std::size_t NextStep() 
const { 
return std::min(chunk_size_, tail_size_); }
 
  556    const Container& container_;
 
  557    const std::size_t chunk_size_;
 
  558    std::size_t tail_size_;
 
  559    UnderlyingIterator current_;
 
  562  ContainerSplitter(
const Container& container, std::size_t chunk_elements)
 
  563      : container_{container}, chunk_size_{chunk_elements} {}
 
  565  std::size_t size() 
const {
 
  566    auto sz = container_.size();
 
  567    return sz / chunk_size_ + (sz % chunk_size_ ? 1 : 0);
 
  569  bool empty() 
const { 
return container_.empty(); }
 
  571  ChunkIterator begin() 
const {
 
  572    return {container_, container_.begin(), chunk_size_};
 
  575  ChunkIterator end() 
const {
 
  576    return {container_, container_.end(), chunk_size_};
 
  579  std::size_t ChunkSize() 
const { 
return chunk_size_; }
 
  580  const Container& GetContainer() 
const { 
return container_; }
 
  583  const Container& container_;
 
  584  const std::size_t chunk_size_;
 
  587template <
typename Container,
 
  588          typename Seq = std::make_index_sequence<
 
  589              boost::pfr::tuple_size_v<
typename Container::value_type>>>
 
  590struct ColumnsSplitterHelper;
 
  593template <
typename Container, std::size_t... Indexes>
 
  594struct ColumnsSplitterHelper<Container, std::index_sequence<Indexes...>> final {
 
  595  static_assert(
sizeof...(Indexes) > 0,
 
  596                "The aggregate having 0 fields doesn't make sense");
 
  598  template <std::size_t Index>
 
  599  struct FieldProjection {
 
  600    using RowType = 
typename Container::value_type;
 
  602        boost::pfr::tuple_element_t<Index, 
typename Container::value_type>;
 
  604    const FieldType& operator()(
const RowType& value) 
const noexcept {
 
  605      return boost::pfr::get<Index>(value);
 
  609  template <std::size_t Index>
 
  611      USERVER_NAMESPACE::utils::impl::ProjectingView<
const Container,
 
  612                                                     FieldProjection<Index>>;
 
  614  template <
typename Fn>
 
  615  static void Perform(
const Container& container, std::size_t chunk_elements,
 
  617    DoSplitByChunks(chunk_elements, fn, FieldView<Indexes>{container}...);
 
  621  template <
typename Fn, 
typename... Views>
 
  622  static void DoSplitByChunks(std::size_t chunk_elements, 
const Fn& fn,
 
  623                              const Views&... views) {
 
  624    DoIterateByChunks(fn, ContainerSplitter{views, chunk_elements}.begin()...);
 
  627  template <
typename Fn, 
typename FirstChunkIterator,
 
  628            typename... ChunkIterators>
 
  629  static void DoIterateByChunks(
const Fn& fn, FirstChunkIterator first,
 
  630                                ChunkIterators... chunks) {
 
  631    while (first.TailSize() > 0) {
 
  632      fn(*first, *chunks...);
 
  641template <
typename Container>
 
  642class ContainerByColumnsSplitter final {
 
  644  ContainerByColumnsSplitter(
const Container& container,
 
  645                             std::size_t chunk_elements)
 
  646      : container_{container}, chunk_elements_{chunk_elements} {}
 
  648  template <
typename Fn>
 
  649  void Perform(
const Fn& fn) {
 
  650    ColumnsSplitterHelper<Container>::Perform(container_, chunk_elements_, fn);
 
  654  const Container& container_;
 
  655  const std::size_t chunk_elements_;
 
  661template <
typename Container>
 
  665template <
typename Container, 
typename Projection>
 
  667    USERVER_NAMESPACE::utils::impl::ProjectingView<
const Container, Projection>>
 
  671template <
typename Container>
 
  672detail::ContainerSplitter<Container> SplitContainer(
 
  673    const Container& container, std::size_t chunk_elements) {
 
  674  return {container, chunk_elements};
 
  677template <
typename Container>
 
  678detail::ContainerByColumnsSplitter<Container> SplitContainerByColumns(
 
  679    const Container& container, std::size_t chunk_elements) {
 
  680  return {container, chunk_elements};