Skip to content

Commit cd7a5b6

Browse files
Added support for unions inside SELECT FROM
1 parent 5238fdf commit cd7a5b6

8 files changed

Lines changed: 95 additions & 42 deletions

File tree

include/sqlgen/dynamic/SelectFrom.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@
1818
namespace sqlgen::dynamic {
1919

2020
struct SelectFrom {
21-
using TableOrQueryType = rfl::Variant<Table, Ref<SelectFrom>>;
21+
struct Union {
22+
std::vector<std::string> columns;
23+
Ref<std::vector<SelectFrom>> selects;
24+
};
25+
26+
using TableOrQueryType = rfl::Variant<Table, Ref<SelectFrom>, Ref<Union>>;
2227

2328
struct Field {
2429
Operation val;

include/sqlgen/dynamic/Union.hpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
#ifndef SQLGEN_DYNAMIC_UNION_HPP_
22
#define SQLGEN_DYNAMIC_UNION_HPP_
33

4-
#include <string>
5-
#include <vector>
6-
74
#include "SelectFrom.hpp"
85

96
namespace sqlgen::dynamic {
107

11-
struct Union {
12-
std::vector<std::string> columns;
13-
Ref<std::vector<SelectFrom>> selects;
14-
};
8+
using Union = SelectFrom::Union;
159

1610
} // namespace sqlgen::dynamic
1711

include/sqlgen/transpilation/fields_to_named_tuple_t.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
#include <rfl.hpp>
55

66
#include "../Literal.hpp"
7+
#include "../internal/all_same_v.hpp"
78
#include "make_field.hpp"
9+
#include "table_tuple_t.hpp"
810

911
namespace sqlgen::transpilation {
1012

@@ -38,6 +40,19 @@ struct FieldsToNamedTupleType<StructType, rfl::Tuple<FieldTypes...>> {
3840
using Type = typename FieldsToNamedTupleType<StructType, FieldTypes...>::Type;
3941
};
4042

43+
template <class... SelectTs>
44+
struct FieldsToNamedTupleType<rfl::Tuple<SelectTs...>> {
45+
using NamedTupleTypes = rfl::Tuple<typename FieldsToNamedTupleType<
46+
table_tuple_t<typename SelectTs::TableOrQueryType,
47+
typename SelectTs::AliasType, typename SelectTs::JoinsType>,
48+
typename SelectTs::FieldsType>::Type...>;
49+
static_assert(
50+
sqlgen::internal::all_same_v<NamedTupleTypes>,
51+
"All SELECT statements in a UNION must return the same columns with "
52+
"the same types.");
53+
using Type = rfl::tuple_element_t<0, NamedTupleTypes>;
54+
};
55+
4156
template <class StructType, class... FieldTypes>
4257
using fields_to_named_tuple_t =
4358
typename FieldsToNamedTupleType<StructType, FieldTypes...>::Type;

include/sqlgen/transpilation/to_union.hpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
namespace sqlgen::transpilation {
1616

17-
template <class ContainerType, class... Selects>
18-
dynamic::Union to_union(const rfl::Tuple<Selects...>& _selects) noexcept {
17+
template <class ContainerType, class... SelectTs>
18+
dynamic::Union to_union(const rfl::Tuple<SelectTs...>& _selects) noexcept {
1919
using ValueType = value_t<ContainerType>;
2020
using NamedTupleType = rfl::named_tuple_t<ValueType>;
2121

@@ -25,14 +25,15 @@ dynamic::Union to_union(const rfl::Tuple<Selects...>& _selects) noexcept {
2525
[](const auto... _s) {
2626
return Ref<std::vector<dynamic::SelectFrom>>::make(
2727
std::vector<dynamic::SelectFrom>({to_select_from<
28-
table_tuple_t<typename Selects::TableOrQueryType,
29-
typename Selects::AliasType,
30-
typename Selects::JoinsType>,
31-
typename Selects::AliasType, typename Selects::FieldsType,
32-
typename Selects::TableOrQueryType, typename Selects::JoinsType,
33-
typename Selects::WhereType, typename Selects::GroupByType,
34-
typename Selects::OrderByType, typename Selects::LimitType>(
35-
_s.fields_, _s.from_, _s.joins_, _s.where_, _s.limit_)...}));
28+
table_tuple_t<typename SelectTs::TableOrQueryType,
29+
typename SelectTs::AliasType,
30+
typename SelectTs::JoinsType>,
31+
typename SelectTs::AliasType, typename SelectTs::FieldsType,
32+
typename SelectTs::TableOrQueryType,
33+
typename SelectTs::JoinsType, typename SelectTs::WhereType,
34+
typename SelectTs::GroupByType, typename SelectTs::OrderByType,
35+
typename SelectTs::LimitType>(_s.fields_, _s.from_, _s.joins_,
36+
_s.where_, _s.limit_)...}));
3637
},
3738
_selects);
3839

include/sqlgen/unite.hpp

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,19 @@
88
#include "Ref.hpp"
99
#include "Result.hpp"
1010
#include "dynamic/Union.hpp"
11-
#include "internal/all_same_v.hpp"
1211
#include "internal/is_range.hpp"
1312
#include "internal/iterator_t.hpp"
1413
#include "is_connection.hpp"
1514
#include "transpilation/fields_to_named_tuple_t.hpp"
16-
#include "transpilation/table_tuple_t.hpp"
1715
#include "transpilation/to_union.hpp"
1816
#include "transpilation/value_t.hpp"
1917

2018
namespace sqlgen {
2119

22-
template <class ContainerType, class Connection, class... Selects>
20+
template <class ContainerType, class Connection, class... SelectTs>
2321
requires is_connection<Connection>
2422
auto unite_impl(const Ref<Connection>& _conn,
25-
const rfl::Tuple<Selects...>& _selects) {
23+
const rfl::Tuple<SelectTs...>& _selects) {
2624
if constexpr (internal::is_range_v<ContainerType>) {
2725
const auto query = transpilation::to_union<ContainerType>(_selects);
2826
return _conn->template read<ContainerType>(query);
@@ -42,32 +40,28 @@ auto unite_impl(const Ref<Connection>& _conn,
4240
return container;
4341
};
4442

45-
using NamedTupleTypes = rfl::Tuple<transpilation::fields_to_named_tuple_t<
46-
transpilation::table_tuple_t<typename Selects::TableOrQueryType,
47-
typename Selects::AliasType,
48-
typename Selects::JoinsType>,
49-
typename Selects::FieldsType>...>;
50-
51-
static_assert(
52-
internal::all_same_v<NamedTupleTypes>,
53-
"All SELECT statements in a UNION must return the same columns with "
54-
"the same types.");
55-
56-
using IteratorType =
57-
internal::iterator_t<rfl::tuple_element_t<0, NamedTupleTypes>,
58-
decltype(_conn)>;
43+
using IteratorType = internal::iterator_t<
44+
transpilation::fields_to_named_tuple_t<rfl::Tuple<SelectTs...>>,
45+
decltype(_conn)>;
5946

6047
using RangeType = Range<IteratorType>;
6148

6249
return unite_impl<RangeType>(_conn, _selects).and_then(to_container);
6350
}
6451
}
6552

66-
template <class ContainerType, class... Selects>
53+
template <class _ContainerType, class... SelectTs>
6754
struct Union {
6855
template <class Connection>
6956
requires is_connection<Connection>
7057
auto operator()(const Ref<Connection>& _conn) const {
58+
using ContainerType = std::conditional_t<
59+
std::is_same_v<std::remove_cvref_t<_ContainerType>, Nothing>,
60+
Range<internal::iterator_t<
61+
transpilation::fields_to_named_tuple_t<rfl::Tuple<SelectTs...>>,
62+
Connection>>,
63+
_ContainerType>;
64+
7165
return unite_impl<ContainerType>(_conn, selects_);
7266
}
7367

@@ -77,12 +71,37 @@ struct Union {
7771
return _res.and_then([&](const auto& _conn) { return (*this)(_conn); });
7872
}
7973

80-
rfl::Tuple<Selects...> selects_;
74+
rfl::Tuple<SelectTs...> selects_;
75+
};
76+
77+
namespace transpilation {
78+
79+
template <class ContainerType, class... SelectTs>
80+
struct ExtractTable<Union<ContainerType, SelectTs...>, false> {
81+
using Type = std::conditional_t<
82+
std::is_same_v<std::remove_cvref_t<ContainerType>, Nothing>,
83+
transpilation::fields_to_named_tuple_t<rfl::Tuple<SelectTs...>>,
84+
transpilation::value_t<ContainerType>>;
8185
};
8286

83-
template <class ContainerType, class... Selects>
84-
auto unite(const Selects&... _selects) {
85-
return Union<ContainerType, Selects...>{rfl::Tuple<Selects...>(_selects...)};
87+
template <class ContainerType, class... SelectTs>
88+
struct ToTableOrQuery<Union<ContainerType, SelectTs...>> {
89+
dynamic::SelectFrom::TableOrQueryType operator()(const auto& _query) {
90+
return transpilation::to_union<ContainerType>(_query.selects_);
91+
}
92+
};
93+
94+
} // namespace transpilation
95+
96+
template <class ContainerType, class... SelectTs>
97+
auto unite(const SelectTs&... _selects) {
98+
return Union<ContainerType, SelectTs...>{
99+
rfl::Tuple<SelectTs...>(_selects...)};
100+
}
101+
102+
template <class... SelectTs>
103+
auto unite(const SelectTs&... _selects) {
104+
return unite<Nothing>(_selects...);
86105
}
87106

88107
} // namespace sqlgen

src/sqlgen/sqlite/to_sql.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,10 @@ std::string table_or_query_to_sql(
722722
return wrap_in_quotes(*_t.schema) + "." + wrap_in_quotes(_t.name);
723723
}
724724
return wrap_in_quotes(_t.name);
725+
726+
} else if constexpr (std::is_same_v<Type, Ref<dynamic::Union>>) {
727+
return "(" + union_to_sql(*_t) + ")";
728+
725729
} else {
726730
return "(" + select_from_to_sql(*_t) + ")";
727731
}

tests/sqlite/test_union.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ TEST(sqlite, test_union) {
2929
using namespace sqlgen::literals;
3030

3131
// Connect to SQLite database
32-
const auto conn = sqlgen::sqlite::connect("test.db");
32+
const auto conn = sqlgen::sqlite::connect();
3333

3434
// Create and insert a user
3535
const auto user1 = User1{.name = "John", .age = 30};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <gtest/gtest.h>
2+
3+
#include <rfl.hpp>
4+
#include <rfl/json.hpp>
5+
#include <sqlgen.hpp>
6+
#include <sqlgen/sqlite.hpp>
7+
#include <vector>
8+
9+
#include "sqlgen/sqlite/to_sql.hpp"
10+
11+
namespace test_union_in_select_from {
12+
13+
TEST(sqlite, test_union_in_select_from) {}
14+
15+
} // namespace test_union_in_select_from

0 commit comments

Comments
 (0)