Added support for UNION and UNION ALL (#96)

This commit is contained in:
Dr. Patrick Urbanke (劉自成)
2025-11-23 23:55:42 +01:00
committed by GitHub
parent 7e4fa42bd9
commit 287ac58bf6
49 changed files with 2162 additions and 210 deletions

View File

@@ -45,6 +45,7 @@
#include "sqlgen/select_from.hpp"
#include "sqlgen/sqlgen_api.hpp"
#include "sqlgen/to.hpp"
#include "sqlgen/unite.hpp"
#include "sqlgen/update.hpp"
#include "sqlgen/where.hpp"
#include "sqlgen/write.hpp"

View File

@@ -72,10 +72,11 @@ class SQLGEN_API Connection {
}
template <class ContainerType>
auto read(const dynamic::SelectFrom &_query) {
auto read(const rfl::Variant<dynamic::SelectFrom, dynamic::Union> &_query) {
using ValueType = transpilation::value_t<ContainerType>;
const auto sql = _query.visit([&](const auto &_q) { return to_sql(_q); });
return internal::to_container<ContainerType, Iterator<ValueType>>(
Iterator<ValueType>(to_sql(_query), conn_));
Iterator<ValueType>(sql, conn_));
}
Result<Nothing> rollback() noexcept;

View File

@@ -18,7 +18,13 @@
namespace sqlgen::dynamic {
struct SelectFrom {
using TableOrQueryType = rfl::Variant<Table, Ref<SelectFrom>>;
struct Union {
std::vector<std::string> columns;
Ref<std::vector<SelectFrom>> selects;
bool all = false;
};
using TableOrQueryType = rfl::Variant<Table, Ref<SelectFrom>, Ref<Union>>;
struct Field {
Operation val;

View File

@@ -10,6 +10,7 @@
#include "Drop.hpp"
#include "Insert.hpp"
#include "SelectFrom.hpp"
#include "Union.hpp"
#include "Update.hpp"
#include "Write.hpp"
@@ -17,7 +18,7 @@ namespace sqlgen::dynamic {
using Statement =
rfl::TaggedUnion<"stmt", CreateAs, CreateIndex, CreateTable, DeleteFrom,
Drop, Insert, SelectFrom, Update, Write>;
Drop, Insert, SelectFrom, Union, Update, Write>;
} // namespace sqlgen::dynamic

View File

@@ -0,0 +1,12 @@
#ifndef SQLGEN_DYNAMIC_UNION_HPP_
#define SQLGEN_DYNAMIC_UNION_HPP_
#include "SelectFrom.hpp"
namespace sqlgen::dynamic {
using Union = SelectFrom::Union;
} // namespace sqlgen::dynamic
#endif

View File

@@ -17,12 +17,18 @@ struct GetColType {
static Type get_value(const T& _t) { return _t; }
};
template <rfl::internal::StringLiteral _name>
struct GetColType<Col<_name>> {
using Type = transpilation::Col<_name>;
static Type get_value(const auto&) { return transpilation::Col<_name>{}; }
template <rfl::internal::StringLiteral _name,
rfl::internal::StringLiteral _alias>
struct GetColType<Col<_name, _alias>> {
using Type = transpilation::Col<_name, _alias>;
static Type get_value(const auto&) {
return transpilation::Col<_name, _alias>{};
}
};
template <class T>
using get_col_type_t = typename GetColType<T>::Type;
} // namespace sqlgen::internal
#endif

View File

@@ -0,0 +1,23 @@
#include <rfl.hpp>
#include <tuple>
#include <type_traits>
namespace sqlgen::internal {
template <class TupleT>
struct AllSame;
template <class Head, class... Tail>
struct AllSame<std::tuple<Head, Tail...>> {
static constexpr bool value = std::conjunction_v<std::is_same<Head, Tail>...>;
};
template <class Head, class... Tail>
struct AllSame<rfl::Tuple<Head, Tail...>> {
static constexpr bool value = std::conjunction_v<std::is_same<Head, Tail>...>;
};
template <class TupleT>
constexpr bool all_same_v = AllSame<std::remove_cvref_t<TupleT>>::value;
} // namespace sqlgen::internal

View File

@@ -13,7 +13,10 @@
#include "../Result.hpp"
#include "../Transaction.hpp"
#include "../dynamic/Column.hpp"
#include "../dynamic/Insert.hpp"
#include "../dynamic/SelectFrom.hpp"
#include "../dynamic/Statement.hpp"
#include "../dynamic/Union.hpp"
#include "../dynamic/Write.hpp"
#include "../internal/to_container.hpp"
#include "../internal/write_or_insert.hpp"
@@ -54,7 +57,7 @@ class SQLGEN_API Connection {
}
template <class ContainerType>
auto read(const dynamic::SelectFrom& _query) {
auto read(const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query) {
using ValueType = transpilation::value_t<ContainerType>;
return internal::to_container<ContainerType>(
read_impl(_query).transform([](auto&& _it) {
@@ -94,7 +97,8 @@ class SQLGEN_API Connection {
const std::variant<dynamic::Insert, dynamic::Write>& _stmt)
const noexcept;
Result<Ref<Iterator>> read_impl(const dynamic::SelectFrom& _query);
Result<Ref<Iterator>> read_impl(
const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query);
Result<Nothing> write_impl(
const std::vector<std::vector<std::optional<std::string>>>& _data);

View File

@@ -4,16 +4,21 @@
#include <libpq-fe.h>
#include <memory>
#include <optional>
#include <rfl.hpp>
#include <stdexcept>
#include <string>
#include <vector>
#include "../Iterator.hpp"
#include "../Ref.hpp"
#include "../Result.hpp"
#include "../Transaction.hpp"
#include "../dynamic/Column.hpp"
#include "../dynamic/Insert.hpp"
#include "../dynamic/SelectFrom.hpp"
#include "../dynamic/Statement.hpp"
#include "../dynamic/Union.hpp"
#include "../dynamic/Write.hpp"
#include "../internal/iterator_t.hpp"
#include "../internal/to_container.hpp"
@@ -57,7 +62,7 @@ class SQLGEN_API Connection {
}
template <class ContainerType>
auto read(const dynamic::SelectFrom& _query) {
auto read(const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query) {
using ValueType = transpilation::value_t<ContainerType>;
return internal::to_container<ContainerType>(
read_impl(_query).transform([](auto&& _it) {
@@ -86,7 +91,8 @@ class SQLGEN_API Connection {
const std::vector<std::vector<std::optional<std::string>>>&
_data) noexcept;
Result<Ref<Iterator>> read_impl(const dynamic::SelectFrom& _query);
Result<Ref<Iterator>> read_impl(
const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query);
std::string to_buffer(
const std::vector<std::optional<std::string>>& _line) const noexcept;

View File

@@ -32,21 +32,14 @@
namespace sqlgen {
template <class TableTupleType, class AliasType, class FieldsType,
class TableOrQueryType, class JoinsType, class WhereType,
class GroupByType, class OrderByType, class LimitType,
class ContainerType, class Connection>
template <class SelectFromT, class ContainerType, class Connection>
requires is_connection<Connection>
auto select_from_impl(const Ref<Connection>& _conn, const FieldsType& _fields,
const TableOrQueryType& _table_or_query,
const JoinsType& _joins, const WhereType& _where,
const LimitType& _limit) {
auto select_from_impl(const Ref<Connection>& _conn, const auto& _fields,
const auto& _table_or_query, const auto& _joins,
const auto& _where, const auto& _limit) {
if constexpr (internal::is_range_v<ContainerType>) {
const auto query =
transpilation::to_select_from<TableTupleType, AliasType, FieldsType,
TableOrQueryType, JoinsType, WhereType,
GroupByType, OrderByType, LimitType>(
_fields, _table_or_query, _joins, _where, _limit);
const auto query = transpilation::to_select_from<SelectFromT>(
_fields, _table_or_query, _joins, _where, _limit);
return _conn->template read<ContainerType>(query);
} else {
@@ -64,43 +57,51 @@ auto select_from_impl(const Ref<Connection>& _conn, const FieldsType& _fields,
return container;
};
using IteratorType = internal::iterator_t<
transpilation::fields_to_named_tuple_t<TableTupleType, FieldsType>,
decltype(_conn)>;
using IteratorType =
internal::iterator_t<transpilation::fields_to_named_tuple_t<
typename SelectFromT::TableTupleType,
typename SelectFromT::FieldsType>,
decltype(_conn)>;
using RangeType = Range<IteratorType>;
return select_from_impl<TableTupleType, AliasType, FieldsType,
TableOrQueryType, JoinsType, WhereType, GroupByType,
OrderByType, LimitType, RangeType>(
return select_from_impl<SelectFromT, RangeType>(
_conn, _fields, _table_or_query, _joins, _where, _limit)
.and_then(to_container);
}
}
template <class TableTupleType, class AliasType, class FieldsType,
class TableOrQueryType, class JoinsType, class WhereType,
class GroupByType, class OrderByType, class LimitType,
class ContainerType, class Connection>
template <class SelectFromT, class ContainerType, class Connection>
requires is_connection<Connection>
auto select_from_impl(const Result<Ref<Connection>>& _res,
const FieldsType& _fields,
const TableOrQueryType& _table_or_query,
const JoinsType& _joins, const WhereType& _where,
const LimitType& _limit) {
auto select_from_impl(const Result<Ref<Connection>>& _res, const auto& _fields,
const auto& _table_or_query, const auto& _joins,
const auto& _where, const auto& _limit) {
return _res.and_then([&](const auto& _conn) {
return select_from_impl<TableTupleType, AliasType, FieldsType,
TableOrQueryType, JoinsType, WhereType, GroupByType,
OrderByType, LimitType, ContainerType>(
return select_from_impl<SelectFromT, ContainerType>(
_conn, _fields, _table_or_query, _joins, _where, _limit);
});
}
template <class TableOrQueryType, class AliasType, class FieldsType,
class JoinsType = Nothing, class WhereType = Nothing,
class GroupByType = Nothing, class OrderByType = Nothing,
class LimitType = Nothing, class ToType = Nothing>
template <class TableOrQueryT, class AliasT, class FieldsT,
class JoinsT = Nothing, class WhereT = Nothing,
class GroupByT = Nothing, class OrderByT = Nothing,
class LimitT = Nothing, class ToT = Nothing>
struct SelectFrom {
using TableOrQueryType = TableOrQueryT;
using AliasType = AliasT;
using FieldsType = FieldsT;
using JoinsType = JoinsT;
using WhereType = WhereT;
using GroupByType = GroupByT;
using OrderByType = OrderByT;
using LimitType = LimitT;
using ToType = ToT;
using SelectFromTypes =
transpilation::SelectFromTypes<AliasType, FieldsType, TableOrQueryType,
JoinsType, WhereType, GroupByType,
OrderByType, LimitType>;
auto operator()(const auto& _conn) const {
using TableTupleType =
transpilation::table_tuple_t<TableOrQueryType, AliasType, JoinsType>;
@@ -113,9 +114,7 @@ struct SelectFrom {
using ContainerType = std::conditional_t<std::is_same_v<ToType, Nothing>,
Range<IteratorType>, ToType>;
return select_from_impl<
TableTupleType, AliasType, FieldsType, TableOrQueryType, JoinsType,
WhereType, GroupByType, OrderByType, LimitType, ContainerType>(
return select_from_impl<SelectFromTypes, ContainerType>(
_conn, fields_, from_, joins_, where_, limit_);
} else {
@@ -129,9 +128,7 @@ struct SelectFrom {
return std::move(_vec[0]);
};
return select_from_impl<TableTupleType, AliasType, FieldsType,
TableOrQueryType, JoinsType, WhereType,
GroupByType, OrderByType, LimitType,
return select_from_impl<SelectFromTypes,
std::vector<std::remove_cvref_t<ToType>>>(
_conn, fields_, from_, joins_, where_, limit_)
.and_then(extract_result);
@@ -311,12 +308,9 @@ struct ToTableOrQuery<
SelectFrom<TableOrQueryType, AliasType, FieldsType, JoinsType, WhereType,
GroupByType, OrderByType, LimitType, ToType>> {
dynamic::SelectFrom::TableOrQueryType operator()(const auto& _query) {
using TableTupleType =
table_tuple_t<TableOrQueryType, AliasType, JoinsType>;
using QueryType = std::remove_cvref_t<decltype(_query)>;
return Ref<dynamic::SelectFrom>::make(
transpilation::to_select_from<TableTupleType, AliasType, FieldsType,
TableOrQueryType, JoinsType, WhereType,
GroupByType, OrderByType, LimitType>(
transpilation::to_select_from<typename QueryType::SelectFromTypes>(
_query.fields_, _query.from_, _query.joins_, _query.where_,
_query.limit_));
}
@@ -347,7 +341,7 @@ inline auto select_from(const FieldTypes&... _fields) {
.from_ = transpilation::TableWrapper<TableType>{}};
}
template <rfl::internal::StringLiteral _alias, class QueryType,
template <rfl::internal::StringLiteral _alias = "", class QueryType,
class... FieldTypes>
inline auto select_from(const QueryType& _query, const FieldTypes&... _fields) {
using FieldsType =

View File

@@ -13,6 +13,8 @@
#include "../Ref.hpp"
#include "../Result.hpp"
#include "../Transaction.hpp"
#include "../dynamic/SelectFrom.hpp"
#include "../dynamic/Union.hpp"
#include "../dynamic/Write.hpp"
#include "../internal/to_container.hpp"
#include "../internal/write_or_insert.hpp"
@@ -50,7 +52,7 @@ class SQLGEN_API Connection {
}
template <class ContainerType>
auto read(const dynamic::SelectFrom& _query) {
auto read(const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query) {
using ValueType = transpilation::value_t<ContainerType>;
return internal::to_container<ContainerType>(
read_impl(_query).transform([](auto&& _it) {
@@ -92,7 +94,8 @@ class SQLGEN_API Connection {
Result<StmtPtr> prepare_statement(const std::string& _sql) const noexcept;
/// Implements the actual read.
Result<Ref<Iterator>> read_impl(const dynamic::SelectFrom& _query);
Result<Ref<Iterator>> read_impl(
const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query);
/// Implements the actual write
Result<Nothing> write_impl(

View File

@@ -0,0 +1,14 @@
#ifndef SQLGEN_TRANSPILATION_UNION_HPP_
#define SQLGEN_TRANSPILATION_UNION_HPP_
#include <string>
#include <type_traits>
namespace sqlgen::transpilation {
template <class... SelectTs>
struct Union {};
} // namespace sqlgen::transpilation
#endif

View File

@@ -4,7 +4,10 @@
#include <rfl.hpp>
#include "../Literal.hpp"
#include "../internal/all_same_v.hpp"
#include "Union.hpp"
#include "make_field.hpp"
#include "table_tuple_t.hpp"
namespace sqlgen::transpilation {
@@ -38,6 +41,32 @@ struct FieldsToNamedTupleType<StructType, rfl::Tuple<FieldTypes...>> {
using Type = typename FieldsToNamedTupleType<StructType, FieldTypes...>::Type;
};
template <class... SelectTs>
struct FieldsToNamedTupleType<Union<SelectTs...>> {
using NamedTupleTypes = rfl::Tuple<typename FieldsToNamedTupleType<
table_tuple_t<typename SelectTs::TableOrQueryType,
typename SelectTs::AliasType, typename SelectTs::JoinsType>,
typename SelectTs::FieldsType>::Type...>;
static_assert(
sqlgen::internal::all_same_v<NamedTupleTypes>,
"All SELECT statements in a UNION must return the same columns with "
"the same types.");
using Type = rfl::tuple_element_t<0, NamedTupleTypes>;
};
template <class... SelectTs, class... FieldTypes>
struct FieldsToNamedTupleType<Union<SelectTs...>, FieldTypes...> {
using Type = typename FieldsToNamedTupleType<
typename FieldsToNamedTupleType<Union<SelectTs...>>::Type,
FieldTypes...>::Type;
};
template <class... SelectTs, class... FieldTypes>
struct FieldsToNamedTupleType<Union<SelectTs...>, rfl::Tuple<FieldTypes...>> {
using Type =
typename FieldsToNamedTupleType<Union<SelectTs...>, FieldTypes...>::Type;
};
template <class StructType, class... FieldTypes>
using fields_to_named_tuple_t =
typename FieldsToNamedTupleType<StructType, FieldTypes...>::Type;

View File

@@ -27,15 +27,18 @@ dynamic::CreateAs to_create_as(const dynamic::CreateAs::What _what,
const TableOrQueryType& _table_or_query,
const JoinsType& _joins, const WhereType& _where,
const LimitType& _limit) {
using SelectFromTypes =
transpilation::SelectFromTypes<AliasType, FieldsType, TableOrQueryType,
JoinsType, WhereType, GroupByType,
OrderByType, LimitType>;
return dynamic::CreateAs{
.what = _what,
.table_or_view = dynamic::Table{.alias = std::nullopt,
.name = get_tablename<T>(),
.schema = get_schema<T>()},
.query = to_select_from<TableTupleType, AliasType, FieldsType,
TableOrQueryType, JoinsType, WhereType,
GroupByType, OrderByType, LimitType>(
_fields, _table_or_query, _joins, _where, _limit),
.query = to_select_from<SelectFromTypes>(_fields, _table_or_query, _joins,
_where, _limit),
.or_replace = _or_replace,
.if_not_exists = _if_not_exists};
}

View File

@@ -20,6 +20,7 @@
#include "flatten_fields_t.hpp"
#include "get_table_t.hpp"
#include "make_fields.hpp"
#include "table_tuple_t.hpp"
#include "to_alias.hpp"
#include "to_condition.hpp"
#include "to_group_by.hpp"
@@ -30,14 +31,32 @@
namespace sqlgen::transpilation {
template <class TableTupleType, class AliasType, class FieldsType,
class TableOrQueryType, class JoinsType, class WhereType,
class GroupByType, class OrderByType, class LimitType>
dynamic::SelectFrom to_select_from(const FieldsType& _fields,
const TableOrQueryType& _table_or_query,
const JoinsType& _joins,
const WhereType& _where,
const LimitType& _limit) {
template <class AliasT, class FieldsT, class TableOrQueryT, class JoinsT,
class WhereT, class GroupByT, class OrderByT, class LimitT>
struct SelectFromTypes {
using AliasType = AliasT;
using FieldsType = FieldsT;
using TableOrQueryType = TableOrQueryT;
using JoinsType = JoinsT;
using WhereType = WhereT;
using GroupByType = GroupByT;
using OrderByType = OrderByT;
using LimitType = LimitT;
using TableTupleType = table_tuple_t<TableOrQueryType, AliasType, JoinsType>;
};
template <class SelectFromT>
dynamic::SelectFrom to_select_from(const auto& _fields,
const auto& _table_or_query,
const auto& _joins, const auto& _where,
const auto& _limit) {
using TableTupleType = typename SelectFromT::TableTupleType;
using AliasType = typename SelectFromT::AliasType;
using FieldsType = typename SelectFromT::FieldsType;
using GroupByType = typename SelectFromT::GroupByType;
using OrderByType = typename SelectFromT::OrderByType;
static_assert(check_aggregations<TableTupleType,
flatten_fields_t<TableTupleType, FieldsType>,
GroupByType>(),

View File

@@ -12,6 +12,7 @@
#include "../insert.hpp"
#include "../read.hpp"
#include "../select_from.hpp"
#include "../unite.hpp"
#include "../update.hpp"
#include "columns_t.hpp"
#include "read_to_select_from.hpp"
@@ -22,6 +23,7 @@
#include "to_drop.hpp"
#include "to_insert_or_write.hpp"
#include "to_select_from.hpp"
#include "to_union.hpp"
#include "to_update.hpp"
#include "value_t.hpp"
@@ -96,18 +98,11 @@ struct ToSQL<Read<ContainerType, WhereType, OrderByType, LimitType>> {
}
};
template <class TableOrQueryType, class AliasType, class FieldsType,
class JoinsType, class WhereType, class GroupByType,
class OrderByType, class LimitType, class ToType>
struct ToSQL<
SelectFrom<TableOrQueryType, AliasType, FieldsType, JoinsType, WhereType,
GroupByType, OrderByType, LimitType, ToType>> {
template <class... Ts>
struct ToSQL<SelectFrom<Ts...>> {
dynamic::Statement operator()(const auto& _select_from) const {
using TableTupleType =
table_tuple_t<TableOrQueryType, AliasType, JoinsType>;
return to_select_from<TableTupleType, AliasType, FieldsType,
TableOrQueryType, JoinsType, WhereType, GroupByType,
OrderByType, LimitType>(
using SelectFromTypes = typename SelectFrom<Ts...>::SelectFromTypes;
return to_select_from<SelectFromTypes>(
_select_from.fields_, _select_from.from_, _select_from.joins_,
_select_from.where_, _select_from.limit_);
}
@@ -120,6 +115,13 @@ struct ToSQL<Update<T, SetsType, WhereType>> {
}
};
template <class ContainerType, class... Selects>
struct ToSQL<sqlgen::Union<ContainerType, Selects...>> {
dynamic::Statement operator()(const auto& _union) const {
return to_union<ContainerType>(_union.selects_, _union.all_);
}
};
template <class T>
dynamic::Statement to_sql(const T& _t) {
return ToSQL<std::remove_cvref_t<T>>{}(_t);

View File

@@ -0,0 +1,40 @@
#ifndef SQLGEN_TRANSPILATION_TO_UNION_HPP_
#define SQLGEN_TRANSPILATION_TO_UNION_HPP_
#include <rfl.hpp>
#include <rfl/named_tuple_t.hpp>
#include <vector>
#include "../Ref.hpp"
#include "../dynamic/SelectFrom.hpp"
#include "../dynamic/Union.hpp"
#include "table_tuple_t.hpp"
#include "to_select_from.hpp"
#include "value_t.hpp"
namespace sqlgen::transpilation {
template <class ContainerType, class... Ts>
dynamic::Union to_union(const rfl::Tuple<Ts...>& _stmts,
const bool _all) noexcept {
using ValueType = value_t<ContainerType>;
using NamedTupleType = rfl::named_tuple_t<ValueType>;
const auto columns = NamedTupleType::Names::names();
const auto selects = rfl::apply(
[]<class... StmtTs>(const StmtTs... _stmt) {
auto vec = std::vector<dynamic::SelectFrom>(
{to_select_from<typename StmtTs::SelectFromTypes>(
_stmt.fields_, _stmt.from_, _stmt.joins_, _stmt.where_,
_stmt.limit_)...});
return Ref<std::vector<dynamic::SelectFrom>>::make(std::move(vec));
},
_stmts);
return dynamic::Union{.columns = columns, .selects = selects, .all = _all};
}
} // namespace sqlgen::transpilation
#endif

185
include/sqlgen/unite.hpp Normal file
View File

@@ -0,0 +1,185 @@
#ifndef SQLGEN_UNITE_HPP_
#define SQLGEN_UNITE_HPP_
#include <rfl.hpp>
#include <type_traits>
#include "Literal.hpp"
#include "Range.hpp"
#include "Ref.hpp"
#include "Result.hpp"
#include "dynamic/Union.hpp"
#include "internal/is_range.hpp"
#include "internal/iterator_t.hpp"
#include "is_connection.hpp"
#include "transpilation/Union.hpp"
#include "transpilation/fields_to_named_tuple_t.hpp"
#include "transpilation/get_table_t.hpp"
#include "transpilation/to_union.hpp"
#include "transpilation/value_t.hpp"
#include "transpilation/wrap_in_optional_t.hpp"
namespace sqlgen {
template <class ContainerType, class Connection, class... SelectTs>
requires is_connection<Connection>
auto unite_impl(const Ref<Connection>& _conn,
const rfl::Tuple<SelectTs...>& _stmts, const bool _all) {
if constexpr (internal::is_range_v<ContainerType>) {
const auto query = transpilation::to_union<ContainerType>(_stmts, _all);
return _conn->template read<ContainerType>(query);
} else {
const auto to_container = [](auto range) -> Result<ContainerType> {
using ValueType = transpilation::value_t<ContainerType>;
ContainerType container;
for (auto& res : range) {
if (res) {
container.emplace_back(
rfl::from_named_tuple<ValueType>(std::move(*res)));
} else {
return error("One of the results in the union was an error.");
}
}
return container;
};
using IteratorType =
internal::iterator_t<transpilation::fields_to_named_tuple_t<
transpilation::Union<SelectTs...>>,
decltype(_conn)>;
using RangeType = Range<IteratorType>;
return unite_impl<RangeType>(_conn, _stmts, _all).and_then(to_container);
}
}
template <class _ContainerType, class... SelectTs>
struct Union {
template <class Connection>
requires is_connection<Connection>
auto operator()(const Ref<Connection>& _conn) const {
using ContainerType = std::conditional_t<
std::is_same_v<std::remove_cvref_t<_ContainerType>, Nothing>,
Range<internal::iterator_t<transpilation::fields_to_named_tuple_t<
transpilation::Union<SelectTs...>>,
Connection>>,
_ContainerType>;
return unite_impl<ContainerType>(_conn, selects_, all_);
}
template <class Connection>
requires is_connection<Connection>
auto operator()(const Result<Ref<Connection>>& _res) const {
return _res.and_then([&](const auto& _conn) { return (*this)(_conn); });
}
rfl::Tuple<SelectTs...> selects_;
bool all_ = false;
};
namespace transpilation {
template <class ContainerType, class... SelectTs>
struct ExtractTable<sqlgen::Union<ContainerType, SelectTs...>, false> {
using Type = std::conditional_t<
std::is_same_v<std::remove_cvref_t<ContainerType>, Nothing>,
fields_to_named_tuple_t<Union<SelectTs...>>, value_t<ContainerType>>;
};
template <class ContainerType, class... SelectTs>
struct ExtractTable<sqlgen::Union<ContainerType, SelectTs...>, true> {
using Type = wrap_in_optional_t<typename ExtractTable<
sqlgen::Union<ContainerType, SelectTs...>, false>::Type>;
};
template <class ContainerType, class... SelectTs>
struct ToTableOrQuery<sqlgen::Union<ContainerType, SelectTs...>> {
dynamic::SelectFrom::TableOrQueryType operator()(const auto& _stmt) {
const auto query = to_union<ContainerType>(_stmt.selects_, _stmt.all_);
return Ref<dynamic::Union>::make(query);
}
};
template <class ContainerType, class... SelectTs, class... FieldTs>
struct FieldsToNamedTupleType<sqlgen::Union<ContainerType, SelectTs...>,
FieldTs...> {
using Type = fields_to_named_tuple_t<Union<SelectTs...>, FieldTs...>;
};
template <class ContainerType, class... SelectTs, class... FieldTs>
struct FieldsToNamedTupleType<sqlgen::Union<ContainerType, SelectTs...>,
rfl::Tuple<FieldTs...>> {
using Type = fields_to_named_tuple_t<Union<SelectTs...>, FieldTs...>;
};
template <rfl::internal::StringLiteral _alias, class ContainerType,
class... SelectTs>
struct GetTableType<Literal<_alias>,
sqlgen::Union<ContainerType, SelectTs...>> {
using TableType = get_table_t<
Literal<_alias>,
rfl::Tuple<std::pair<
extract_table_t<sqlgen::Union<ContainerType, SelectTs...>, false>,
Literal<_alias>>>>;
};
template <class ContainerType, class... SelectTs>
struct GetTableType<Literal<"">, sqlgen::Union<ContainerType, SelectTs...>> {
using TableType = get_table_t<
Literal<"">,
extract_table_t<sqlgen::Union<ContainerType, SelectTs...>, false>>;
};
template <size_t _i, class ContainerType, class... SelectTs>
struct GetTableType<std::integral_constant<size_t, _i>,
sqlgen::Union<ContainerType, SelectTs...>> {
using TableType = get_table_t<
std::integral_constant<size_t, _i>,
extract_table_t<sqlgen::Union<ContainerType, SelectTs...>, false>>;
};
template <class ContainerType, class... SelectTs, class AliasType>
struct TableTupleType<sqlgen::Union<ContainerType, SelectTs...>, AliasType,
Nothing> {
using Type = rfl::Tuple<std::pair<
extract_table_t<sqlgen::Union<ContainerType, SelectTs...>, false>,
AliasType>>;
};
template <class ContainerType, class... SelectTs>
struct TableTupleType<sqlgen::Union<ContainerType, SelectTs...>, Literal<"">,
Nothing> {
using Type =
extract_table_t<sqlgen::Union<ContainerType, SelectTs...>, false>;
};
} // namespace transpilation
template <class ContainerType, class... SelectTs>
auto unite(const SelectTs&... _stmts) {
return Union<ContainerType, SelectTs...>{
.selects_ = rfl::Tuple<SelectTs...>(_stmts...), .all_ = false};
}
template <class... SelectTs>
auto unite(const SelectTs&... _stmts) {
return unite<Nothing>(_stmts...);
}
template <class ContainerType, class... SelectTs>
auto unite_all(const SelectTs&... _stmts) {
return Union<ContainerType, SelectTs...>{
.selects_ = rfl::Tuple<SelectTs...>(_stmts...), .all_ = true};
}
template <class... SelectTs>
auto unite_all(const SelectTs&... _stmts) {
return unite_all<Nothing>(_stmts...);
}
} // namespace sqlgen
#endif