Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <userver/storages/postgres/detail/query_parameters.hpp>
#include <userver/storages/postgres/detail/time_types.hpp>

#include <userver/storages/postgres/io/struct_view.hpp>

USERVER_NAMESPACE_BEGIN

namespace storages::postgres::detail {
Expand Down Expand Up @@ -47,6 +49,13 @@ class NonTransaction {
return DoExecute(query, detail::QueryParameters{params}, statement_cmd_ctl);
}

template <io::traits::RequiresStructView T>
ResultSet Execute(OptionalCommandControl statement_cmd_ctl, const Query& query, const T& struct_arg) {
detail::StaticQueryParameters<T::size> params;
params.WriteTuple(GetConnectionUserTypes(), struct_arg.Params());
return DoExecute(query, detail::QueryParameters{params}, statement_cmd_ctl);
}

/// Execute statement with stored arguments.
///
/// Suspends coroutine for execution.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ class QueryParameters {
types_(ph.ParamTypesBuffer()),
values_(ph.ParamBuffers()),
lengths_(ph.ParamLengthsBuffer()),
formats_(ph.ParamFormatsBuffer())
{}
formats_(ph.ParamFormatsBuffer()) {}

bool Empty() const { return size_ == 0; }
std::size_t Size() const { return size_; }
Expand Down Expand Up @@ -85,6 +84,12 @@ class StaticQueryParameters {
(Write(index++, types, args), ...);
}

template <typename... T>
void WriteTuple(const UserTypes& types, const std::tuple<T...>& args) {
using TupleIndexes = std::make_index_sequence<sizeof...(T)>;
WriteTuple(types, args, TupleIndexes{});
}

private:
template <typename T>
void WriteParamType(std::size_t index, const UserTypes& types, const T&) {
Expand Down Expand Up @@ -118,6 +123,11 @@ class StaticQueryParameters {
}
}

template <typename... T, std::size_t... Indexes>
void WriteTuple(const UserTypes& types, const std::tuple<T...>& args, std::index_sequence<Indexes...>) {
(Write(Indexes, types, std::get<Indexes>(args)), ...);
}

using OidList = Oid[ParamsCount];
using BufferType = std::string;
using ParameterList = BufferType[ParamsCount];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ USERVER_NAMESPACE_BEGIN

namespace storages::postgres {

template <typename T, typename ExtractionTag>
class TypedResultSet;

namespace detail {

template <typename T, typename ExtractionTag, IteratorDirection Direction = IteratorDirection::kForward>
template <
typename T,
typename ExtractionTag,
typename AsType = T,
IteratorDirection Direction = IteratorDirection::kForward>
class ConstTypedRowIterator : private Row {
public:
//@{
Expand All @@ -29,13 +30,14 @@ class ConstTypedRowIterator : private Row {
/// iterator.
using iterator_category = std::input_iterator_tag;
//@}
using CastType = AsType;
static constexpr ExtractionTag kExtractTag{};

//@{
/** @name Iterator dereferencing */
/// Read typed value from underlying postgres buffers and return it.
/// Please note there is no operator ->.
value_type operator*() const { return As<value_type>(kExtractTag); }
value_type operator*() const { return As<CastType>(kExtractTag); }
//@}
//@{
/** @name Iterator validity */
Expand Down Expand Up @@ -92,11 +94,9 @@ class ConstTypedRowIterator : private Row {
bool operator>=(const ConstTypedRowIterator& rhs) const { return this->Compare(rhs) >= 0; }
//@}
private:
friend class TypedResultSet<T, ExtractionTag>;
friend class TypedResultSet<T, ExtractionTag, AsType>;

ConstTypedRowIterator(detail::ResultWrapperPtr res, size_type row)
: Row{std::move(res), row}
{}
ConstTypedRowIterator(detail::ResultWrapperPtr res, size_type row) : Row{std::move(res), row} {}
ConstTypedRowIterator& DoAdvance(difference_type distance) {
this->Advance(distance * static_cast<int>(Direction));
return *this;
Expand Down
92 changes: 56 additions & 36 deletions postgresql/include/userver/storages/postgres/io/array_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
#include <array>
#include <iterator>
#include <set>
#include <tuple>
#include <type_traits>
#include <unordered_set>
#include <utility>
#include <vector>

#include <boost/pfr/core.hpp>
Expand All @@ -19,6 +22,7 @@
#include <userver/storages/postgres/io/buffer_io_base.hpp>
#include <userver/storages/postgres/io/field_buffer.hpp>
#include <userver/storages/postgres/io/row_types.hpp>
#include <userver/storages/postgres/io/struct_view.hpp>
#include <userver/storages/postgres/io/traits.hpp>
#include <userver/storages/postgres/io/type_mapping.hpp>
#include <userver/storages/postgres/io/type_traits.hpp>
Expand Down Expand Up @@ -488,6 +492,11 @@ class ContainerChunk {
template <typename Container>
class ContainerSplitter {
public:
static_assert(
!traits::IsViewContainer<Container>::value,
"A struct view container is sent column by column, one array parameter per member. "
"Use ExecuteDecomposeBulk instead of ExecuteBulk"
);
static_assert(
traits::IsCompatibleContainer<Container>{},
"Only containers explicitly declared as compatible are supported"
Expand Down Expand Up @@ -560,30 +569,45 @@ class ContainerSplitter {
const std::size_t chunk_size_;
};

template <
typename Container,
typename Seq = std::make_index_sequence<boost::pfr::tuple_size_v<typename Container::value_type>>>
struct ColumnsSplitterHelper;
template <typename View, std::size_t Index>
struct ColumnProjection {
using RowType = typename View::UnderlyingType;

/// Utility helper to iterate chunks of input array in column-wise way
template <typename Container, std::size_t... Indexes>
struct ColumnsSplitterHelper<Container, std::index_sequence<Indexes...>> final {
static_assert(sizeof...(Indexes) > 0, "The aggregate having 0 fields doesn't make sense");
decltype(auto) operator()(const RowType& value) const noexcept { return std::get<Index>(View::Tie(value)); }
};

template <std::size_t Index>
struct FieldProjection {
using RowType = typename Container::value_type;
using FieldType = boost::pfr::tuple_element_t<Index, typename Container::value_type>;
template <typename Container>
struct ColumnsSource {
using ContainerType = Container;
using ViewType = StructView<std::remove_cvref_t<typename Container::value_type>>;

const FieldType& operator()(const RowType& value) const noexcept { return boost::pfr::get<Index>(value); }
};
static const Container& GetContainer(const Container& container) noexcept { return container; }
};

template <typename View, typename Container>
struct ColumnsSource<ViewContainer<View, Container>> {
using ContainerType = Container;
using ViewType = View;

static const Container& GetContainer(const ViewContainer<View, Container>& view_container) noexcept {
return view_container.GetContainer();
}
};

template <typename Container, typename View, std::size_t Index>
using ColumnView = USERVER_NAMESPACE::utils::impl::ProjectingView<const Container, ColumnProjection<View, Index>>;

template <std::size_t Index>
using FieldView = USERVER_NAMESPACE::utils::impl::ProjectingView<const Container, FieldProjection<Index>>;
template <typename Container, typename View, typename Seq = std::make_index_sequence<View::size>>
struct ColumnsSplitterHelper;

/// Utility helper to iterate chunks of input array in column-wise way
template <typename Container, typename View, std::size_t... Indexes>
struct ColumnsSplitterHelper<Container, View, std::index_sequence<Indexes...>> final {
static_assert(sizeof...(Indexes) > 0, "The aggregate having 0 fields doesn't make sense");

template <typename Fn>
static void Perform(const Container& container, std::size_t chunk_elements, const Fn& fn) {
DoSplitByChunks(chunk_elements, fn, FieldView<Indexes>{container}...);
DoSplitByChunks(chunk_elements, fn, ColumnView<Container, View, Indexes>{container}...);
}

private:
Expand All @@ -603,29 +627,16 @@ struct ColumnsSplitterHelper<Container, std::index_sequence<Indexes...>> final {
}
};

template <
typename Container,
typename Seq = std::make_index_sequence<boost::pfr::tuple_size_v<typename Container::value_type>>>
template <typename Container, typename View, typename Seq = std::make_index_sequence<View::size>>
struct ColumnsDecomposerHelper;

template <typename Container, std::size_t... Indexes>
struct ColumnsDecomposerHelper<Container, std::index_sequence<Indexes...>> final {
template <typename Container, typename View, std::size_t... Indexes>
struct ColumnsDecomposerHelper<Container, View, std::index_sequence<Indexes...>> final {
static_assert(sizeof...(Indexes) > 0, "The aggregate having 0 fields doesn't make sense");

template <std::size_t Index>
struct FieldProjection {
using RowType = typename Container::value_type;
using FieldType = boost::pfr::tuple_element_t<Index, typename Container::value_type>;

const FieldType& operator()(const RowType& value) const noexcept { return boost::pfr::get<Index>(value); }
};

template <std::size_t Index>
using FieldView = USERVER_NAMESPACE::utils::impl::ProjectingView<const Container, FieldProjection<Index>>;

template <typename Fn>
static auto Perform(const Container& container, const Fn& fn) {
return fn(FieldView<Indexes>{container}...);
return fn(ColumnView<Container, View, Indexes>{container}...);
}
};

Expand All @@ -640,7 +651,12 @@ class ContainerByColumnsSplitter final {

template <typename Fn>
void Perform(const Fn& fn) {
ColumnsSplitterHelper<Container>::Perform(container_, chunk_elements_, fn);
using Source = ColumnsSource<Container>;
ColumnsSplitterHelper<typename Source::ContainerType, typename Source::ViewType>::Perform(
Source::GetContainer(container_),
chunk_elements_,
fn
);
}

private:
Expand All @@ -657,7 +673,11 @@ class ContainerByColumnsDecomposer final {

template <typename Fn>
auto Perform(const Fn& fn) {
return ColumnsDecomposerHelper<Container>::Perform(container_, fn);
using Source = ColumnsSource<Container>;
return ColumnsDecomposerHelper<typename Source::ContainerType, typename Source::ViewType>::Perform(
Source::GetContainer(container_),
fn
);
}

private:
Expand Down
Loading
Loading