userver: userver/storages/clickhouse/io/impl/validate.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
validate.hpp
1#pragma once
2
3#include <type_traits>
4#include <vector>
5
6#include <boost/pfr/core.hpp>
7
8#include <userver/utils/assert.hpp>
9#include <userver/utils/meta_light.hpp>
10
11#include <userver/storages/clickhouse/io/columns/base_column.hpp>
12#include <userver/storages/clickhouse/io/columns/common_columns.hpp>
13#include <userver/storages/clickhouse/io/columns/nullable_column.hpp>
14#include <userver/storages/clickhouse/io/io_fwd.hpp>
15#include <userver/storages/clickhouse/io/type_traits.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace storages::clickhouse::io::impl {
20
21template <typename T>
22constexpr void EnsureInstantiationOfVector([[maybe_unused]] const T& t) {
23 static_assert(meta::kIsInstantiationOf<std::vector, T>);
24}
25
26template <typename T>
27struct EnsureInstantiationOfColumn {
28 ~EnsureInstantiationOfColumn() {
29 static_assert(!std::is_same_v<columns::ClickhouseColumn<T>, T>);
30 static_assert(std::is_base_of_v<columns::ClickhouseColumn<T>, T>);
31 }
32};
33
34template <typename T>
35struct EnsureInstantiationOfColumn<columns::NullableColumn<T>> {
36 ~EnsureInstantiationOfColumn() {
37 static_assert(!std::is_same_v<columns::ClickhouseColumn<T>, T>);
38 static_assert(std::is_base_of_v<columns::ClickhouseColumn<T>, T>);
39 }
40};
41
42template <typename T,
43 typename Seq = std::make_index_sequence<std::tuple_size_v<T>>>
44struct TupleColumnsValidate;
45
46template <typename T, size_t... S>
47struct TupleColumnsValidate<T, std::index_sequence<S...>> {
48 ~TupleColumnsValidate() {
49 (...,
50 (void)impl::EnsureInstantiationOfColumn<std::tuple_element_t<S, T>>{});
51 }
52};
53
54template <size_t I, typename Row>
55using CppType = boost::pfr::tuple_element_t<I, Row>;
56
57template <typename T>
58using MappedType = typename CppToClickhouse<T>::mapped_type;
59
60template <size_t I, typename Row>
61using ClickhouseType =
62 typename std::tuple_element_t<I, MappedType<Row>>::cpp_type;
63
64template <typename T>
65inline constexpr auto kCppTypeColumnsCount = boost::pfr::tuple_size_v<T>;
66
67template <typename T>
68inline constexpr auto kClickhouseTypeColumnsCount =
69 std::tuple_size_v<MappedType<T>>;
70
71template <typename T>
72constexpr void CommonValidateMapping() {
73 static_assert(traits::kIsMappedToClickhouse<T>, "not mapped to clickhouse");
74 static_assert(kCppTypeColumnsCount<T> == kClickhouseTypeColumnsCount<T>);
75
76 [[maybe_unused]] TupleColumnsValidate<MappedType<T>> validator{};
77}
78
79template <typename T>
80constexpr void ValidateColumnsMapping(const T& t) {
81 boost::pfr::for_each_field(
82 t, [](const auto& field) { impl::EnsureInstantiationOfVector(field); });
83
84 impl::CommonValidateMapping<T>();
85}
86
87template <size_t I>
88struct FailIndexAssertion : std::false_type {};
89
90template <typename Row, size_t... I>
91constexpr size_t FieldTypeFindMismatch(std::index_sequence<I...>) {
92 constexpr bool results[] = {
93 std::is_same_v<CppType<I, Row>, ClickhouseType<I, Row>>...};
94
95 size_t i = 0;
96 for (bool v : results) {
97 if (!v) return i;
98 ++i;
99 }
100
101 return i;
102}
103
104template <typename T>
105constexpr void ValidateRowsMapping() {
106 impl::CommonValidateMapping<T>();
107
108 constexpr auto columns_count = kClickhouseTypeColumnsCount<T>;
109 constexpr auto type_mismatch_index =
110 FieldTypeFindMismatch<T>(std::make_index_sequence<columns_count>());
111 if constexpr (type_mismatch_index != columns_count) {
112 static_assert(std::is_same_v<CppType<type_mismatch_index, T>,
113 ClickhouseType<type_mismatch_index, T>>,
114 "Make sure your ClickHouse mapping is correct.");
115 static_assert(FailIndexAssertion<type_mismatch_index>::value,
116 "Recheck your mapping at this index.");
117 }
118}
119
120template <typename T>
121void ValidateRowsCount(const T& t) {
122 std::optional<size_t> rows_count;
123 boost::pfr::for_each_field(t, [&rows_count](const auto& field) {
124 if (!rows_count.has_value()) {
125 rows_count.emplace(field.size());
126 }
127 UINVARIANT(*rows_count == field.size(),
128 "All rows should have same number of elements");
129 });
130}
131
132template <typename T>
133void ValidateColumnsCount(size_t expected) {
134 constexpr auto columns_count = kCppTypeColumnsCount<T>;
135 UINVARIANT(columns_count == expected, "Columns count mismatch.");
136}
137
138} // namespace storages::clickhouse::io::impl
139
140USERVER_NAMESPACE_END