Skip to content
Closed
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
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Welcome to the sqlgen documentation. This guide provides detailed information ab
- [sqlgen::inner_join, sqlgen::left_join, sqlgen::right_join, sqlgen::full_join](joins.md) - How to join different tables
- [sqlgen::insert](insert.md) - How to insert data within transactions
- [sqlgen::select_from](select_from.md) - How to read data from a database using more complex queries
- [sqlgen::unite and sqlgen::unite_all](unite.md) - How to combine results from multiple SELECT statements
- [sqlgen::update](update.md) - How to update data in a table

## Other Operations
Expand Down
82 changes: 82 additions & 0 deletions docs/unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# `unite` and `unite_all`

The `unite` and `unite_all` functions allow you to combine the results of multiple `SELECT` statements into a single result set.

## `unite`

The `unite` function corresponds to the SQL `UNION` operator. It combines the result sets of two or more `SELECT` statements and removes duplicate rows.

### Example

```cpp
struct User1 {
std::string name;
int age;
};

struct User2 {
std::string name;
int age;
};

const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);

const auto united = sqlgen::unite<std::vector<User1>>(s1, s2);
```

## `unite_all`

The `unite_all` function corresponds to the SQL `UNION ALL` operator. It combines the result sets of two or more `SELECT` statements, including all duplicate rows.

### Example

```cpp
struct User1 {
std::string name;
int age;
};

struct User2 {
std::string name;
int age;
};

const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);

const auto united = sqlgen::unite_all<std::vector<User1>>(s1, s2);
```

## Nesting in `SELECT` statements

You can use the result of a `unite` or `unite_all` operation as a subquery in a `SELECT` statement.

### Example

```cpp
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2);

const auto sel = sqlgen::select_from(united.as("u"), "name"_c, "age"_c);
```

## Nesting in `JOIN` statements

You can also use the result of a `unite` or `unite_all` operation as a subquery in a `JOIN` statement.

### Example

```cpp
struct Login {
int id;
std::string username;
};

const auto united = sqlgen::unite<std::vector<User1>>(s1, s2);

const auto sel =
sqlgen::select_from<Login>(
"id"_c, "username"_c,
sqlgen::inner_join(united.as("u"), "username"_c == "u.name"_c))
.where("id"_c == 1);
```
1 change: 1 addition & 0 deletions include/sqlgen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
8 changes: 7 additions & 1 deletion include/sqlgen/dynamic/SelectFrom.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion include/sqlgen/dynamic/Statement.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
#include "Drop.hpp"
#include "Insert.hpp"
#include "SelectFrom.hpp"
#include "Union.hpp"
#include "Update.hpp"
#include "Write.hpp"

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

Expand Down
12 changes: 12 additions & 0 deletions include/sqlgen/dynamic/Union.hpp
Original file line number Diff line number Diff line change
@@ -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
14 changes: 10 additions & 4 deletions include/sqlgen/internal/GetColType.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 23 additions & 0 deletions include/sqlgen/internal/all_same_v.hpp
Original file line number Diff line number Diff line change
@@ -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
20 changes: 15 additions & 5 deletions include/sqlgen/select_from.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,21 @@ auto select_from_impl(const Result<Ref<Connection>>& _res,
});
}

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 _TableOrQueryType, class _AliasType, class _FieldsType,
class _JoinsType = Nothing, class _WhereType = Nothing,
class _GroupByType = Nothing, class _OrderByType = Nothing,
class _LimitType = Nothing, class _ToType = Nothing>
struct SelectFrom {
using TableOrQueryType = _TableOrQueryType;
using AliasType = _AliasType;
using FieldsType = _FieldsType;
using JoinsType = _JoinsType;
using WhereType = _WhereType;
using GroupByType = _GroupByType;
using OrderByType = _OrderByType;
using LimitType = _LimitType;
using ToType = _ToType;

auto operator()(const auto& _conn) const {
using TableTupleType =
transpilation::table_tuple_t<TableOrQueryType, AliasType, JoinsType>;
Expand Down Expand Up @@ -347,7 +357,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 =
Expand Down
7 changes: 5 additions & 2 deletions include/sqlgen/sqlite/Connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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(
Expand Down
14 changes: 14 additions & 0 deletions include/sqlgen/transpilation/Union.hpp
Original file line number Diff line number Diff line change
@@ -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
29 changes: 29 additions & 0 deletions include/sqlgen/transpilation/fields_to_named_tuple_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions include/sqlgen/transpilation/to_sql.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"

Expand Down Expand Up @@ -120,6 +122,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_);
}
};

template <class T>
dynamic::Statement to_sql(const T& _t) {
return ToSQL<std::remove_cvref_t<T>>{}(_t);
Expand Down
45 changes: 45 additions & 0 deletions include/sqlgen/transpilation/to_union.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#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... SelectTs>
dynamic::Union to_union(const rfl::Tuple<SelectTs...>& _selects) noexcept {
using ValueType = value_t<ContainerType>;
using NamedTupleType = rfl::named_tuple_t<ValueType>;

const auto columns = NamedTupleType::Names::names();

const auto selects = rfl::apply(
[](const auto... _s) {
return Ref<std::vector<dynamic::SelectFrom>>::make(
std::vector<dynamic::SelectFrom>({to_select_from<
table_tuple_t<typename SelectTs::TableOrQueryType,
typename SelectTs::AliasType,
typename SelectTs::JoinsType>,
typename SelectTs::AliasType, typename SelectTs::FieldsType,
typename SelectTs::TableOrQueryType,
typename SelectTs::JoinsType, typename SelectTs::WhereType,
typename SelectTs::GroupByType, typename SelectTs::OrderByType,
typename SelectTs::LimitType>(_s.fields_, _s.from_, _s.joins_,
_s.where_, _s.limit_)...}));
},
_selects);

return dynamic::Union{.columns = columns, .selects = selects};
}

} // namespace sqlgen::transpilation

#endif
Loading
Loading