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
-73
View File
@@ -1,73 +0,0 @@
name: linux-cxx20-conan
on: [push, pull_request]
jobs:
linux:
strategy:
fail-fast: false
matrix:
include:
- compiler: llvm
compiler-version: 16
link: "static"
- compiler: llvm
compiler-version: 18
link: "static"
- compiler: gcc
compiler-version: 11
additional-dep: "g++-11"
link: "static"
- compiler: gcc
compiler-version: 12
link: "static"
- compiler: llvm
compiler-version: 16
link: "shared"
- compiler: llvm
compiler-version: 18
link: "shared"
- compiler: gcc
compiler-version: 11
additional-dep: "g++-11"
link: "shared"
- compiler: gcc
compiler-version: 12
link: "shared"
name: "${{ github.job }} (${{ matrix.compiler }}-${{ matrix.compiler-version }}-${{ matrix.link }})"
concurrency:
group: ci-${{ github.ref }}-${{ github.job }}-${{ matrix.compiler }}-${{ matrix.compiler-version }}-${{ matrix.link }}
cancel-in-progress: true
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y ninja-build pipx ${{ matrix.additional-dep }}
- name: Install Conan
run: |
pipx install conan
conan profile detect
- name: Compile
run: |
if [[ "${{ matrix.compiler }}" == "llvm" ]]; then
export CC=clang-${{ matrix.compiler-version }}
export CXX=clang++-${{ matrix.compiler-version }}
elif [[ "${{ matrix.compiler }}" == "gcc" ]]; then
export CC=gcc-${{ matrix.compiler-version }}
export CXX=g++-${{ matrix.compiler-version }}
fi
sudo ln -s $(which ccache) /usr/local/bin/$CC
sudo ln -s $(which ccache) /usr/local/bin/$CXX
$CXX --version
if [[ "${{ matrix.link }}" == "static" ]]; then
conan build . --build=missing -s compiler.cppstd=gnu20
else
conan build . --build=missing -s compiler.cppstd=gnu20 -o sqlgen/*:with_mysql=True -o */*:shared=True
fi
-47
View File
@@ -1,47 +0,0 @@
name: macos-cxx20-conan
on: [push, pull_request]
jobs:
macos-clang:
strategy:
fail-fast: false
matrix:
include:
- os: "macos-latest"
link: "static"
- os: "macos-13"
link: "static"
- os: "macos-latest"
link: "shared"
- os: "macos-13"
link: "shared"
name: "${{ github.job }} (${{ matrix.os }}-${{ matrix.link }})"
concurrency:
group: ci-${{ github.ref }}-${{ github.job }}-${{ matrix.os }}-${{ matrix.link }}
cancel-in-progress: true
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Install dependencies
run: brew install ninja pipx
- name: Install Conan
run: |
pipx install conan
conan profile detect
- name: Compile
env:
CC: clang
CXX: clang++
run: |
$CXX --version
if [[ "${{ matrix.link }}" == "static" ]]; then
conan build . --build=missing -s compiler.cppstd=gnu20
else
conan build . --build=missing -s compiler.cppstd=gnu20 -o sqlgen/*:with_mysql=True -o */*:shared=True
fi
+1
View File
@@ -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
+80
View File
@@ -0,0 +1,80 @@
# `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 = select_from<Login, "t1">("id"_t1, "username"_t1) |
inner_join<"t2">(united, "username"_t1 == "name"_t2) |
where("id"_t1 == 1) | to<std::vector<Login>>;
```
+1
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"
+3 -2
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;
+7 -1
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;
+2 -1
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
+12
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
+10 -4
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
+23
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
+6 -2
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);
+8 -2
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;
+41 -47
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 =
+5 -2
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(
+14
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
@@ -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>(),
+13 -11
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);
+40
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
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
+27
View File
@@ -72,6 +72,8 @@ std::string table_or_query_to_sql(
std::string type_to_sql(const dynamic::Type& _type) noexcept;
std::string union_to_sql(const dynamic::Union& _stmt) noexcept;
std::string update_to_sql(const dynamic::Update& _stmt) noexcept;
std::string write_to_sql(const dynamic::Write& _stmt) noexcept;
@@ -798,6 +800,10 @@ std::string table_or_query_to_sql(
return wrap_in_quotes(*_t.schema) + "." + wrap_in_quotes(_t.name);
}
return wrap_in_quotes(_t.name);
} else if constexpr (std::is_same_v<Type, Ref<dynamic::Union>>) {
return "(" + union_to_sql(*_t) + ")";
} else {
return "(" + select_from_to_sql(*_t) + ")";
}
@@ -829,6 +835,9 @@ std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept {
} else if constexpr (std::is_same_v<S, dynamic::SelectFrom>) {
return select_from_to_sql(_s);
} else if constexpr (std::is_same_v<S, dynamic::Union>) {
return union_to_sql(_s);
} else if constexpr (std::is_same_v<S, dynamic::Update>) {
return update_to_sql(_s);
@@ -909,6 +918,24 @@ std::string type_to_sql(const dynamic::Type& _type) noexcept {
});
}
std::string union_to_sql(const dynamic::Union& _stmt) noexcept {
using namespace std::ranges::views;
const auto columns = internal::strings::join(
", ",
internal::collect::vector(_stmt.columns | transform(wrap_in_quotes)));
const auto to_str = [&](const auto& _select) {
return "SELECT " + columns + " FROM (" + select_from_to_sql(_select) + ")";
};
const auto separator =
_stmt.all ? std::string(" UNION ALL ") : std::string(" UNION ");
return internal::strings::join(
separator, internal::collect::vector(*_stmt.selects | transform(to_str)));
}
std::string update_to_sql(const dynamic::Update& _stmt) noexcept {
using namespace std::ranges::views;
+4 -2
View File
@@ -137,8 +137,10 @@ Result<Connection::StmtPtr> Connection::prepare_statement(
return stmt_ptr;
}
Result<Ref<Iterator>> Connection::read_impl(const dynamic::SelectFrom& _query) {
const auto sql = mysql::to_sql_impl(_query);
Result<Ref<Iterator>> Connection::read_impl(
const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query) {
const auto sql =
_query.visit([](const auto& _q) { return mysql::to_sql_impl(_q); });
const auto err =
mysql_real_query(conn_.get(), sql.c_str(), static_cast<int>(sql.size()));
if (err) {
+30
View File
@@ -72,6 +72,8 @@ std::string table_or_query_to_sql(
std::string type_to_sql(const dynamic::Type& _type) noexcept;
std::string union_to_sql(const dynamic::Union& _stmt) noexcept;
std::string update_to_sql(const dynamic::Update& _stmt) noexcept;
// ----------------------------------------------------------------------------
@@ -837,6 +839,10 @@ std::string table_or_query_to_sql(
return wrap_in_quotes(*_t.schema) + "." + wrap_in_quotes(_t.name);
}
return wrap_in_quotes(_t.name);
} else if constexpr (std::is_same_v<Type, Ref<dynamic::Union>>) {
return "(" + union_to_sql(*_t) + ")";
} else {
return "(" + select_from_to_sql(*_t) + ")";
}
@@ -872,12 +878,36 @@ std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept {
} else if constexpr (std::is_same_v<S, dynamic::Update>) {
return update_to_sql(_s);
} else if constexpr (std::is_same_v<S, dynamic::Union>) {
return union_to_sql(_s);
} else {
static_assert(rfl::always_false_v<S>, "Unsupported type.");
}
});
}
std::string union_to_sql(const dynamic::Union& _stmt) noexcept {
using namespace std::ranges::views;
const auto columns = internal::strings::join(
", ",
internal::collect::vector(_stmt.columns | transform([](const auto& _col) {
return "t." + wrap_in_quotes(_col);
})));
const auto to_str = [&](const auto& _select) {
return "SELECT " + columns + " FROM (" + select_from_to_sql(_select) +
") t";
};
const auto separator =
_stmt.all ? std::string(" UNION ALL ") : std::string(" UNION ");
return internal::strings::join(
separator, internal::collect::vector(*_stmt.selects | transform(to_str)));
}
std::string type_to_sql(const dynamic::Type& _type) noexcept {
return _type.visit([](const auto _t) -> std::string {
using T = std::remove_cvref_t<decltype(_t)>;
+3 -2
View File
@@ -110,8 +110,9 @@ rfl::Result<Ref<Connection>> Connection::make(
.transform([](auto&& _conn) { return Ref<Connection>::make(_conn); });
}
Result<Ref<Iterator>> Connection::read_impl(const dynamic::SelectFrom& _query) {
const auto sql = postgres::to_sql_impl(_query);
Result<Ref<Iterator>> Connection::read_impl(
const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query) {
const auto sql = _query.visit([](const auto& _q) { return to_sql_impl(_q); });
return Ref<Iterator>::make(sql, conn_);
}
+27
View File
@@ -61,6 +61,8 @@ std::string table_or_query_to_sql(
std::string type_to_sql(const dynamic::Type& _type) noexcept;
std::string union_to_sql(const dynamic::Union& _stmt) noexcept;
std::string update_to_sql(const dynamic::Update& _stmt) noexcept;
std::string write_to_sql(const dynamic::Write& _stmt) noexcept;
@@ -762,6 +764,10 @@ std::string table_or_query_to_sql(
return wrap_in_quotes(*_t.schema) + "." + wrap_in_quotes(_t.name);
}
return wrap_in_quotes(_t.name);
} else if constexpr (std::is_same_v<Type, Ref<dynamic::Union>>) {
return "(" + union_to_sql(*_t) + ")";
} else {
return "(" + select_from_to_sql(*_t) + ")";
}
@@ -799,12 +805,33 @@ std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept {
} else if constexpr (std::is_same_v<S, dynamic::Write>) {
return write_to_sql(_s);
} else if constexpr (std::is_same_v<S, dynamic::Union>) {
return union_to_sql(_s);
} else {
static_assert(rfl::always_false_v<S>, "Unsupported type.");
}
});
}
std::string union_to_sql(const dynamic::Union& _stmt) noexcept {
using namespace std::ranges::views;
const auto columns = internal::strings::join(
", ",
internal::collect::vector(_stmt.columns | transform(wrap_in_quotes)));
const auto to_str = [&](const auto& _select) {
return "SELECT " + columns + " FROM (" + select_from_to_sql(_select) + ")";
};
const auto separator =
_stmt.all ? std::string(" UNION ALL ") : std::string(" UNION ");
return internal::strings::join(
separator, internal::collect::vector(*_stmt.selects | transform(to_str)));
}
std::string type_to_sql(const dynamic::Type& _type) noexcept {
return _type.visit([](const auto _t) -> std::string {
using T = std::remove_cvref_t<decltype(_t)>;
+4 -2
View File
@@ -2,6 +2,7 @@
#include <ranges>
#include <rfl.hpp>
#include <rfl/Variant.hpp>
#include <sstream>
#include "sqlgen/internal/collect/vector.hpp"
@@ -101,8 +102,9 @@ typename Connection::ConnPtr Connection::make_conn(const std::string& _fname) {
return ConnPtr::make(std::shared_ptr<sqlite3>(conn, &sqlite3_close)).value();
}
Result<Ref<Iterator>> Connection::read_impl(const dynamic::SelectFrom& _query) {
const auto sql = to_sql_impl(_query);
Result<Ref<Iterator>> Connection::read_impl(
const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query) {
const auto sql = _query.visit([](const auto& _q) { return to_sql_impl(_q); });
sqlite3_stmt* p_stmt = nullptr;
+27
View File
@@ -53,6 +53,8 @@ std::string table_or_query_to_sql(
std::string type_to_sql(const dynamic::Type& _type) noexcept;
std::string union_to_sql(const dynamic::Union& _stmt) noexcept;
std::string update_to_sql(const dynamic::Update& _stmt) noexcept;
// ----------------------------------------------------------------------------
@@ -720,6 +722,10 @@ std::string table_or_query_to_sql(
return wrap_in_quotes(*_t.schema) + "." + wrap_in_quotes(_t.name);
}
return wrap_in_quotes(_t.name);
} else if constexpr (std::is_same_v<Type, Ref<dynamic::Union>>) {
return "(" + union_to_sql(*_t) + ")";
} else {
return "(" + select_from_to_sql(*_t) + ")";
}
@@ -756,12 +762,33 @@ std::string to_sql_impl(const dynamic::Statement& _stmt) noexcept {
} else if constexpr (std::is_same_v<S, dynamic::Write>) {
return insert_or_write_to_sql(_s);
} else if constexpr (std::is_same_v<S, dynamic::Union>) {
return union_to_sql(_s);
} else {
static_assert(rfl::always_false_v<S>, "Unsupported type.");
}
});
}
std::string union_to_sql(const dynamic::Union& _stmt) noexcept {
using namespace std::ranges::views;
const auto columns = internal::strings::join(
", ",
internal::collect::vector(_stmt.columns | transform(wrap_in_quotes)));
const auto to_str = [&](const auto& _select) {
return "SELECT " + columns + " FROM (" + select_from_to_sql(_select) + ")";
};
const auto separator =
_stmt.all ? std::string(" UNION ALL ") : std::string(" UNION ");
return internal::strings::join(
separator, internal::collect::vector(*_stmt.selects | transform(to_str)));
}
std::string type_to_sql(const dynamic::Type& _type) noexcept {
return _type.visit([](const auto _t) -> std::string {
using T = std::remove_cvref_t<decltype(_t)>;
+60
View File
@@ -0,0 +1,60 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/duckdb.hpp>
#include <vector>
#include "sqlgen/duckdb/to_sql.hpp"
namespace test_union {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(duckdb, test_union) {
using namespace sqlgen::literals;
const auto conn = sqlgen::duckdb::connect();
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto result = sqlgen::unite<std::vector<User1>>(s1, s2, s3)(conn);
const auto users = result.value();
const auto query =
sqlgen::duckdb::to_sql(sqlgen::unite<std::vector<User1>>(s1, s2, s3));
EXPECT_EQ(
query,
R"(SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3"))");
EXPECT_EQ(users.size(), 3);
}
} // namespace test_union
+60
View File
@@ -0,0 +1,60 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/duckdb.hpp>
#include <vector>
#include "sqlgen/duckdb/to_sql.hpp"
namespace test_union_all {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(duckdb, test_union_all) {
using namespace sqlgen::literals;
const auto conn = sqlgen::duckdb::connect();
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 30, .name = "John"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto result = sqlgen::unite_all<std::vector<User1>>(s1, s2, s3)(conn);
const auto users = result.value();
const auto query =
sqlgen::duckdb::to_sql(sqlgen::unite_all<std::vector<User1>>(s1, s2, s3));
EXPECT_EQ(
query,
R"(SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION ALL SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION ALL SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3"))");
EXPECT_EQ(users.size(), 3);
}
} // namespace test_union_all
+76
View File
@@ -0,0 +1,76 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/duckdb.hpp>
#include <vector>
#include "sqlgen/duckdb/to_sql.hpp"
namespace test_union_in_join {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
struct Login {
int id;
std::string username;
};
TEST(duckdb, test_union_in_join) {
using namespace sqlgen;
using namespace sqlgen::literals;
const auto conn = sqlgen::duckdb::connect();
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto login = Login{.id = 1, .username = "John"};
sqlgen::write(conn, login);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = select_from<Login, "t1">("id"_t1, "username"_t1) |
inner_join<"t2">(united, "username"_t1 == "name"_t2) |
where("id"_t1 == 1) | to<std::vector<Login>>;
const auto query = sqlgen::duckdb::to_sql(sel);
const auto result = sel(conn);
const auto users = result.value();
EXPECT_EQ(
query,
R"(SELECT t1."id", t1."username" FROM "Login" t1 INNER JOIN (SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3")) t2 ON t1."username" = t2."name" WHERE t1."id" = 1)");
EXPECT_EQ(users.size(), 1);
EXPECT_EQ(users.at(0).id, 1);
EXPECT_EQ(users.at(0).username, "John");
}
} // namespace test_union_in_join
+76
View File
@@ -0,0 +1,76 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/duckdb.hpp>
#include <vector>
#include "sqlgen/duckdb/to_sql.hpp"
namespace test_union_in_join2 {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
struct Login {
int id;
std::string username;
};
TEST(duckdb, test_union_in_join2) {
using namespace sqlgen;
using namespace sqlgen::literals;
const auto conn = sqlgen::duckdb::connect();
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto login = Login{.id = 1, .username = "John"};
sqlgen::write(conn, login);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = select_from<"t1">(united, "id"_t2, "username"_t2) |
inner_join<Login, "t2">("username"_t2 == "name"_t1) |
where("id"_t2 == 1) | to<std::vector<Login>>;
const auto query = sqlgen::duckdb::to_sql(sel);
const auto result = sel(conn);
const auto users = result.value();
EXPECT_EQ(
query,
R"(SELECT t2."id", t2."username" FROM (SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3")) t1 INNER JOIN "Login" t2 ON t2."username" = t1."name" WHERE t2."id" = 1)");
EXPECT_EQ(users.size(), 1);
EXPECT_EQ(users.at(0).id, 1);
EXPECT_EQ(users.at(0).username, "John");
}
} // namespace test_union_in_join2
+70
View File
@@ -0,0 +1,70 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/duckdb.hpp>
#include <vector>
#include "sqlgen/duckdb/to_sql.hpp"
namespace test_union_in_select {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(duckdb, test_union_in_select) {
using namespace sqlgen::literals;
const auto conn = sqlgen::duckdb::connect();
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = sqlgen::select_from(united, "name"_c, "age"_c) |
sqlgen::to<std::vector<User1>>;
const auto result = sel(conn);
const auto users = result.value();
const auto query = sqlgen::duckdb::to_sql(sel);
EXPECT_EQ(
query,
R"(SELECT "name", "age" FROM (SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3")))");
EXPECT_EQ(users.size(), 3);
EXPECT_EQ(users.at(0).name, "Jane");
EXPECT_EQ(users.at(0).age, 25);
EXPECT_EQ(users.at(1).name, "Joe");
EXPECT_EQ(users.at(1).age, 40);
EXPECT_EQ(users.at(2).name, "John");
EXPECT_EQ(users.at(2).age, 30);
}
} // namespace test_union_in_select
+76
View File
@@ -0,0 +1,76 @@
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/mysql.hpp>
#include <vector>
namespace test_union {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(mysql, test_union) {
using namespace sqlgen::literals;
const auto credentials = sqlgen::mysql::Credentials{.host = "localhost",
.user = "sqlgen",
.password = "password",
.dbname = "mysql"};
const auto conn = sqlgen::mysql::connect(credentials)
.and_then(sqlgen::drop<User1> | sqlgen::if_exists)
.and_then(sqlgen::drop<User2> | sqlgen::if_exists)
.and_then(sqlgen::drop<User3> | sqlgen::if_exists);
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto result = sqlgen::unite<std::vector<User1>>(s1, s2, s3)(conn);
const auto query =
sqlgen::mysql::to_sql(sqlgen::unite<std::vector<User1>>(s1, s2, s3));
const auto users = result.value();
EXPECT_EQ(
query,
R"(SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User1`) t UNION SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User2`) t UNION SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User3`) t)");
EXPECT_EQ(users.size(), 3);
EXPECT_EQ(users.at(0).name, "John");
EXPECT_EQ(users.at(0).age, 30);
EXPECT_EQ(users.at(1).name, "Jane");
EXPECT_EQ(users.at(1).age, 25);
EXPECT_EQ(users.at(2).name, "Joe");
EXPECT_EQ(users.at(2).age, 40);
}
} // namespace test_union
#endif // SQLGEN_BUILD_DRY_TESTS_ONLY
+78
View File
@@ -0,0 +1,78 @@
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/mysql.hpp>
#include <vector>
#include "sqlgen/mysql/to_sql.hpp"
namespace test_union_all {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(mysql, test_union_all) {
using namespace sqlgen::literals;
const auto credentials = sqlgen::mysql::Credentials{.host = "localhost",
.user = "sqlgen",
.password = "password",
.dbname = "mysql"};
const auto conn = sqlgen::mysql::connect(credentials)
.and_then(sqlgen::drop<User1> | sqlgen::if_exists)
.and_then(sqlgen::drop<User2> | sqlgen::if_exists)
.and_then(sqlgen::drop<User3> | sqlgen::if_exists);
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 30, .name = "John"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto result = sqlgen::unite_all<std::vector<User1>>(s1, s2, s3)(conn);
const auto query =
sqlgen::mysql::to_sql(sqlgen::unite_all<std::vector<User1>>(s1, s2, s3));
const auto users = result.value();
EXPECT_EQ(
query,
R"(SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User1`) t UNION ALL SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User2`) t UNION ALL SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User3`) t)");
EXPECT_EQ(users.size(), 3);
EXPECT_EQ(users.at(0).name, "John");
EXPECT_EQ(users.at(0).age, 30);
EXPECT_EQ(users.at(1).name, "Jane");
EXPECT_EQ(users.at(1).age, 25);
EXPECT_EQ(users.at(2).name, "John");
EXPECT_EQ(users.at(2).age, 30);
}
} // namespace test_union_all
#endif // SQLGEN_BUILD_DRY_TESTS_ONLY
+89
View File
@@ -0,0 +1,89 @@
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/mysql.hpp>
#include <vector>
#include "sqlgen/mysql/to_sql.hpp"
namespace test_union_in_join {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
struct Login {
int id;
std::string username;
};
TEST(mysql, test_union_in_join) {
using namespace sqlgen;
using namespace sqlgen::literals;
const auto credentials = sqlgen::mysql::Credentials{.host = "localhost",
.user = "sqlgen",
.password = "password",
.dbname = "mysql"};
const auto conn = sqlgen::mysql::connect(credentials)
.and_then(sqlgen::drop<User1> | sqlgen::if_exists)
.and_then(sqlgen::drop<User2> | sqlgen::if_exists)
.and_then(sqlgen::drop<User3> | sqlgen::if_exists)
.and_then(sqlgen::drop<Login> | sqlgen::if_exists);
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto login = Login{.id = 1, .username = "John"};
sqlgen::write(conn, login);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = select_from<Login, "t1">("id"_t1, "username"_t1) |
inner_join<"t2">(united, "username"_t1 == "name"_t2) |
where("id"_t1 == 1) | to<std::vector<Login>>;
const auto query = sqlgen::mysql::to_sql(sel);
const auto result = sel(conn);
const auto users = result.value();
EXPECT_EQ(
query,
R"(SELECT t1.`id`, t1.`username` FROM `Login` t1 INNER JOIN (SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User1`) t UNION SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User2`) t UNION SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User3`) t) t2 ON t1.`username` = t2.`name` WHERE t1.`id` = 1)");
EXPECT_EQ(users.size(), 1);
EXPECT_EQ(users.at(0).id, 1);
EXPECT_EQ(users.at(0).username, "John");
}
} // namespace test_union_in_join
#endif // SQLGEN_BUILD_DRY_TESTS_ONLY
+89
View File
@@ -0,0 +1,89 @@
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/mysql.hpp>
#include <vector>
#include "sqlgen/mysql/to_sql.hpp"
namespace test_union_in_join2 {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
struct Login {
int id;
std::string username;
};
TEST(mysql, test_union_in_join2) {
using namespace sqlgen;
using namespace sqlgen::literals;
const auto credentials = sqlgen::mysql::Credentials{.host = "localhost",
.user = "sqlgen",
.password = "password",
.dbname = "mysql"};
const auto conn = sqlgen::mysql::connect(credentials)
.and_then(sqlgen::drop<User1> | sqlgen::if_exists)
.and_then(sqlgen::drop<User2> | sqlgen::if_exists)
.and_then(sqlgen::drop<User3> | sqlgen::if_exists)
.and_then(sqlgen::drop<Login> | sqlgen::if_exists);
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto login = Login{.id = 1, .username = "John"};
sqlgen::write(conn, login);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = select_from<"t1">(united, "id"_t2, "username"_t2) |
inner_join<Login, "t2">("username"_t2 == "name"_t1) |
where("id"_t2 == 1) | to<std::vector<Login>>;
const auto query = sqlgen::mysql::to_sql(sel);
const auto result = sel(conn);
const auto users = result.value();
EXPECT_EQ(
query,
R"(SELECT t2.`id`, t2.`username` FROM (SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User1`) t UNION SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User2`) t UNION SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User3`) t) t1 INNER JOIN `Login` t2 ON t2.`username` = t1.`name` WHERE t2.`id` = 1)");
EXPECT_EQ(users.size(), 1);
EXPECT_EQ(users.at(0).id, 1);
EXPECT_EQ(users.at(0).username, "John");
}
} // namespace test_union_in_join2
#endif // SQLGEN_BUILD_DRY_TESTS_ONLY
+81
View File
@@ -0,0 +1,81 @@
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/mysql.hpp>
#include <vector>
#include "sqlgen/mysql/to_sql.hpp"
namespace test_union_in_select {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(mysql, test_union_in_select) {
using namespace sqlgen::literals;
const auto credentials = sqlgen::mysql::Credentials{.host = "localhost",
.user = "sqlgen",
.password = "password",
.dbname = "mysql"};
const auto conn = sqlgen::mysql::connect(credentials)
.and_then(sqlgen::drop<User1> | sqlgen::if_exists)
.and_then(sqlgen::drop<User2> | sqlgen::if_exists)
.and_then(sqlgen::drop<User3> | sqlgen::if_exists);
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = sqlgen::select_from<"t1">(united, "name"_t1, "age"_t1) |
sqlgen::to<std::vector<User1>>;
const auto query = sqlgen::mysql::to_sql(sel);
const auto result = sel(conn);
const auto users = result.value();
EXPECT_EQ(
query,
R"(SELECT t1.`name`, t1.`age` FROM (SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User1`) t UNION SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User2`) t UNION SELECT t.`name`, t.`age` FROM (SELECT `name`, `age` FROM `User3`) t) t1)");
EXPECT_EQ(users.size(), 3);
EXPECT_EQ(users.at(0).name, "John");
EXPECT_EQ(users.at(0).age, 30);
EXPECT_EQ(users.at(1).name, "Jane");
EXPECT_EQ(users.at(1).age, 25);
EXPECT_EQ(users.at(2).name, "Joe");
EXPECT_EQ(users.at(2).age, 40);
}
} // namespace test_union_in_select
#endif // SQLGEN_BUILD_DRY_TESTS_ONLY
+78
View File
@@ -0,0 +1,78 @@
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/postgres.hpp>
#include <vector>
#include "sqlgen/postgres/to_sql.hpp"
namespace test_union {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(postgres, test_union) {
using namespace sqlgen::literals;
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
.password = "password",
.host = "localhost",
.dbname = "postgres"};
const auto conn = sqlgen::postgres::connect(credentials)
.and_then(sqlgen::drop<User1> | sqlgen::if_exists)
.and_then(sqlgen::drop<User2> | sqlgen::if_exists)
.and_then(sqlgen::drop<User3> | sqlgen::if_exists);
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto result = sqlgen::unite<std::vector<User1>>(s1, s2, s3)(conn);
const auto users = result.value();
const auto query =
sqlgen::postgres::to_sql(sqlgen::unite<std::vector<User1>>(s1, s2, s3));
EXPECT_EQ(
query,
R"(SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3"))");
EXPECT_EQ(users.size(), 3);
EXPECT_EQ(users.at(0).name, "John");
EXPECT_EQ(users.at(0).age, 30);
EXPECT_EQ(users.at(1).name, "Jane");
EXPECT_EQ(users.at(1).age, 25);
EXPECT_EQ(users.at(2).name, "Joe");
EXPECT_EQ(users.at(2).age, 40);
}
} // namespace test_union
#endif // SQLGEN_BUILD_DRY_TESTS_ONLY
+78
View File
@@ -0,0 +1,78 @@
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/postgres.hpp>
#include <vector>
#include "sqlgen/postgres/to_sql.hpp"
namespace test_union_all {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(postgres, test_union_all) {
using namespace sqlgen::literals;
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
.password = "password",
.host = "localhost",
.dbname = "postgres"};
const auto conn = sqlgen::postgres::connect(credentials)
.and_then(sqlgen::drop<User1> | sqlgen::if_exists)
.and_then(sqlgen::drop<User2> | sqlgen::if_exists)
.and_then(sqlgen::drop<User3> | sqlgen::if_exists);
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 30, .name = "John"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto result = sqlgen::unite_all<std::vector<User1>>(s1, s2, s3)(conn);
const auto users = result.value();
const auto query = sqlgen::postgres::to_sql(
sqlgen::unite_all<std::vector<User1>>(s1, s2, s3));
EXPECT_EQ(
query,
R"(SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION ALL SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION ALL SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3"))");
EXPECT_EQ(users.size(), 3);
EXPECT_EQ(users.at(0).name, "John");
EXPECT_EQ(users.at(0).age, 30);
EXPECT_EQ(users.at(1).name, "Jane");
EXPECT_EQ(users.at(1).age, 25);
EXPECT_EQ(users.at(2).name, "John");
EXPECT_EQ(users.at(2).age, 30);
}
} // namespace test_union_all
#endif // SQLGEN_BUILD_DRY_TESTS_ONLY
+89
View File
@@ -0,0 +1,89 @@
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/postgres.hpp>
#include <vector>
#include "sqlgen/postgres/to_sql.hpp"
namespace test_union_in_join {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
struct Login {
int id;
std::string username;
};
TEST(postgres, test_union_in_join) {
using namespace sqlgen;
using namespace sqlgen::literals;
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
.password = "password",
.host = "localhost",
.dbname = "postgres"};
const auto conn = sqlgen::postgres::connect(credentials)
.and_then(sqlgen::drop<User1> | sqlgen::if_exists)
.and_then(sqlgen::drop<User2> | sqlgen::if_exists)
.and_then(sqlgen::drop<User3> | sqlgen::if_exists)
.and_then(sqlgen::drop<Login> | sqlgen::if_exists);
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto login = Login{.id = 1, .username = "John"};
sqlgen::write(conn, login);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = select_from<Login, "t1">("id"_t1, "username"_t1) |
inner_join<"t2">(united, "username"_t1 == "name"_t2) |
where("id"_t1 == 1) | to<std::vector<Login>>;
const auto query = sqlgen::postgres::to_sql(sel);
const auto result = sel(conn);
const auto users = result.value();
EXPECT_EQ(
query,
R"(SELECT t1."id", t1."username" FROM "Login" t1 INNER JOIN (SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3")) t2 ON t1."username" = t2."name" WHERE t1."id" = 1)");
EXPECT_EQ(users.size(), 1);
EXPECT_EQ(users.at(0).id, 1);
EXPECT_EQ(users.at(0).username, "John");
}
} // namespace test_union_in_join
#endif // SQLGEN_BUILD_DRY_TESTS_ONLY
+89
View File
@@ -0,0 +1,89 @@
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/postgres.hpp>
#include <vector>
#include "sqlgen/postgres/to_sql.hpp"
namespace test_union_in_join2 {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
struct Login {
int id;
std::string username;
};
TEST(postgres, test_union_in_join2) {
using namespace sqlgen;
using namespace sqlgen::literals;
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
.password = "password",
.host = "localhost",
.dbname = "postgres"};
const auto conn = sqlgen::postgres::connect(credentials)
.and_then(sqlgen::drop<User1> | sqlgen::if_exists)
.and_then(sqlgen::drop<User2> | sqlgen::if_exists)
.and_then(sqlgen::drop<User3> | sqlgen::if_exists)
.and_then(sqlgen::drop<Login> | sqlgen::if_exists);
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto login = Login{.id = 1, .username = "John"};
sqlgen::write(conn, login);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = select_from<"t1">(united, "id"_t2, "username"_t2) |
inner_join<Login, "t2">("username"_t2 == "name"_t1) |
where("id"_t2 == 1) | to<std::vector<Login>>;
const auto query = sqlgen::postgres::to_sql(sel);
const auto result = sel(conn);
const auto users = result.value();
EXPECT_EQ(
query,
R"(SELECT t2."id", t2."username" FROM (SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3")) t1 INNER JOIN "Login" t2 ON t2."username" = t1."name" WHERE t2."id" = 1)");
EXPECT_EQ(users.size(), 1);
EXPECT_EQ(users.at(0).id, 1);
EXPECT_EQ(users.at(0).username, "John");
}
} // namespace test_union_in_join2
#endif // SQLGEN_BUILD_DRY_TESTS_ONLY
+81
View File
@@ -0,0 +1,81 @@
#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/postgres.hpp>
#include <vector>
#include "sqlgen/postgres/to_sql.hpp"
namespace test_union_in_select {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(postgres, test_union_in_select) {
using namespace sqlgen::literals;
const auto credentials = sqlgen::postgres::Credentials{.user = "postgres",
.password = "password",
.host = "localhost",
.dbname = "postgres"};
const auto conn = sqlgen::postgres::connect(credentials)
.and_then(sqlgen::drop<User1> | sqlgen::if_exists)
.and_then(sqlgen::drop<User2> | sqlgen::if_exists)
.and_then(sqlgen::drop<User3> | sqlgen::if_exists);
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = sqlgen::select_from(united, "name"_c, "age"_c) |
sqlgen::to<std::vector<User1>>;
const auto result = sel(conn);
const auto users = result.value();
const auto query = sqlgen::postgres::to_sql(sel);
EXPECT_EQ(
query,
R"(SELECT "name", "age" FROM (SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3")))");
EXPECT_EQ(users.size(), 3);
EXPECT_EQ(users.at(0).name, "John");
EXPECT_EQ(users.at(0).age, 30);
EXPECT_EQ(users.at(1).name, "Jane");
EXPECT_EQ(users.at(1).age, 25);
EXPECT_EQ(users.at(2).name, "Joe");
EXPECT_EQ(users.at(2).age, 40);
}
} // namespace test_union_in_select
#endif // SQLGEN_BUILD_DRY_TESTS_ONLY
+66
View File
@@ -0,0 +1,66 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/sqlite.hpp>
#include <vector>
#include "sqlgen/sqlite/to_sql.hpp"
namespace test_union {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(sqlite, test_union) {
using namespace sqlgen::literals;
const auto conn = sqlgen::sqlite::connect();
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto result = sqlgen::unite<std::vector<User1>>(s1, s2, s3)(conn);
const auto users = result.value();
const auto query =
sqlgen::sqlite::to_sql(sqlgen::unite<std::vector<User1>>(s1, s2, s3));
EXPECT_EQ(
query,
R"(SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3"))");
EXPECT_EQ(users.size(), 3);
EXPECT_EQ(users.at(0).name, "Jane");
EXPECT_EQ(users.at(0).age, 25);
EXPECT_EQ(users.at(1).name, "Joe");
EXPECT_EQ(users.at(1).age, 40);
EXPECT_EQ(users.at(2).name, "John");
EXPECT_EQ(users.at(2).age, 30);
}
} // namespace test_union
+66
View File
@@ -0,0 +1,66 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/sqlite.hpp>
#include <vector>
#include "sqlgen/sqlite/to_sql.hpp"
namespace test_union_all {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(sqlite, test_union_all) {
using namespace sqlgen::literals;
const auto conn = sqlgen::sqlite::connect();
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 30, .name = "John"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto result = sqlgen::unite_all<std::vector<User1>>(s1, s2, s3)(conn);
const auto users = result.value();
const auto query =
sqlgen::sqlite::to_sql(sqlgen::unite_all<std::vector<User1>>(s1, s2, s3));
EXPECT_EQ(
query,
R"(SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION ALL SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION ALL SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3"))");
EXPECT_EQ(users.size(), 3);
EXPECT_EQ(users.at(0).name, "John");
EXPECT_EQ(users.at(0).age, 30);
EXPECT_EQ(users.at(1).name, "Jane");
EXPECT_EQ(users.at(1).age, 25);
EXPECT_EQ(users.at(2).name, "John");
EXPECT_EQ(users.at(2).age, 30);
}
} // namespace test_union_all
+76
View File
@@ -0,0 +1,76 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/sqlite.hpp>
#include <vector>
#include "sqlgen/sqlite/to_sql.hpp"
namespace test_union_in_join {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
struct Login {
int id;
std::string username;
};
TEST(sqlite, test_union_in_join) {
using namespace sqlgen;
using namespace sqlgen::literals;
const auto conn = sqlgen::sqlite::connect();
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto login = Login{.id = 1, .username = "John"};
sqlgen::write(conn, login);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = select_from<Login, "t1">("id"_t1, "username"_t1) |
inner_join<"t2">(united, "username"_t1 == "name"_t2) |
where("id"_t1 == 1) | to<std::vector<Login>>;
const auto query = sqlgen::sqlite::to_sql(sel);
const auto result = sel(conn);
const auto users = result.value();
EXPECT_EQ(
query,
R"(SELECT t1."id", t1."username" FROM "Login" t1 INNER JOIN (SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3")) t2 ON t1."username" = t2."name" WHERE t1."id" = 1)");
EXPECT_EQ(users.size(), 1);
EXPECT_EQ(users.at(0).id, 1);
EXPECT_EQ(users.at(0).username, "John");
}
} // namespace test_union_in_join
+76
View File
@@ -0,0 +1,76 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/sqlite.hpp>
#include <vector>
#include "sqlgen/sqlite/to_sql.hpp"
namespace test_union_in_join2 {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
struct Login {
int id;
std::string username;
};
TEST(sqlite, test_union_in_join2) {
using namespace sqlgen;
using namespace sqlgen::literals;
const auto conn = sqlgen::sqlite::connect();
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto login = Login{.id = 1, .username = "John"};
sqlgen::write(conn, login);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = select_from<"t1">(united, "id"_t2, "username"_t2) |
inner_join<Login, "t2">("username"_t2 == "name"_t1) |
where("id"_t2 == 1) | to<std::vector<Login>>;
const auto query = sqlgen::sqlite::to_sql(sel);
const auto result = sel(conn);
const auto users = result.value();
EXPECT_EQ(
query,
R"(SELECT t2."id", t2."username" FROM (SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3")) t1 INNER JOIN "Login" t2 ON t2."username" = t1."name" WHERE t2."id" = 1)");
EXPECT_EQ(users.size(), 1);
EXPECT_EQ(users.at(0).id, 1);
EXPECT_EQ(users.at(0).username, "John");
}
} // namespace test_union_in_join2
+72
View File
@@ -0,0 +1,72 @@
#include <gtest/gtest.h>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <sqlgen.hpp>
#include <sqlgen/sqlite.hpp>
#include <vector>
#include "sqlgen/sqlite/to_sql.hpp"
namespace test_union_in_select {
struct User1 {
std::string name;
int age;
};
struct User2 {
std::string name;
int age;
};
struct User3 {
int age;
std::string name;
};
TEST(sqlite, test_union_in_select) {
using namespace sqlgen::literals;
// Connect to SQLite database
const auto conn = sqlgen::sqlite::connect();
// Create and insert a user
const auto user1 = User1{.name = "John", .age = 30};
sqlgen::write(conn, user1);
const auto user2 = User2{.name = "Jane", .age = 25};
sqlgen::write(conn, user2);
const auto user3 = User3{.age = 40, .name = "Joe"};
sqlgen::write(conn, user3);
const auto s1 = sqlgen::select_from<User1>("name"_c, "age"_c);
const auto s2 = sqlgen::select_from<User2>("name"_c, "age"_c);
const auto s3 = sqlgen::select_from<User3>("name"_c, "age"_c);
const auto united = sqlgen::unite<std::vector<User1>>(s1, s2, s3);
const auto sel = sqlgen::select_from(united, "name"_c, "age"_c) |
sqlgen::to<std::vector<User1>>;
const auto result = sel(conn);
const auto users = result.value();
const auto query = sqlgen::sqlite::to_sql(sel);
EXPECT_EQ(
query,
R"(SELECT "name", "age" FROM (SELECT "name", "age" FROM (SELECT "name", "age" FROM "User1") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User2") UNION SELECT "name", "age" FROM (SELECT "name", "age" FROM "User3")))");
EXPECT_EQ(users.size(), 3);
EXPECT_EQ(users.at(0).name, "Jane");
EXPECT_EQ(users.at(0).age, 25);
EXPECT_EQ(users.at(1).name, "Joe");
EXPECT_EQ(users.at(1).age, 40);
EXPECT_EQ(users.at(2).name, "John");
EXPECT_EQ(users.at(2).age, 30);
}
} // namespace test_union_in_select