mirror of
https://github.com/getml/sqlgen.git
synced 2026-01-06 01:19:58 -06:00
Added support for UNION and UNION ALL (#96)
This commit is contained in:
committed by
GitHub
parent
7e4fa42bd9
commit
287ac58bf6
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
12
include/sqlgen/dynamic/Union.hpp
Normal file
12
include/sqlgen/dynamic/Union.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
23
include/sqlgen/internal/all_same_v.hpp
Normal file
23
include/sqlgen/internal/all_same_v.hpp
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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(
|
||||
|
||||
14
include/sqlgen/transpilation/Union.hpp
Normal file
14
include/sqlgen/transpilation/Union.hpp
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
|
||||
@@ -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>(),
|
||||
|
||||
@@ -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);
|
||||
|
||||
40
include/sqlgen/transpilation/to_union.hpp
Normal file
40
include/sqlgen/transpilation/to_union.hpp
Normal 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
185
include/sqlgen/unite.hpp
Normal 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
|
||||
Reference in New Issue
Block a user