mirror of
https://github.com/getml/sqlgen.git
synced 2026-05-06 07:02:46 -05:00
Added support for UNION and UNION ALL (#96)
This commit is contained in:
committed by
GitHub
parent
7e4fa42bd9
commit
287ac58bf6
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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>>;
|
||||
```
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "sqlgen/select_from.hpp"
|
||||
#include "sqlgen/sqlgen_api.hpp"
|
||||
#include "sqlgen/to.hpp"
|
||||
#include "sqlgen/unite.hpp"
|
||||
#include "sqlgen/update.hpp"
|
||||
#include "sqlgen/where.hpp"
|
||||
#include "sqlgen/write.hpp"
|
||||
|
||||
@@ -72,10 +72,11 @@ class SQLGEN_API Connection {
|
||||
}
|
||||
|
||||
template <class ContainerType>
|
||||
auto read(const dynamic::SelectFrom &_query) {
|
||||
auto read(const rfl::Variant<dynamic::SelectFrom, dynamic::Union> &_query) {
|
||||
using ValueType = transpilation::value_t<ContainerType>;
|
||||
const auto sql = _query.visit([&](const auto &_q) { return to_sql(_q); });
|
||||
return internal::to_container<ContainerType, Iterator<ValueType>>(
|
||||
Iterator<ValueType>(to_sql(_query), conn_));
|
||||
Iterator<ValueType>(sql, conn_));
|
||||
}
|
||||
|
||||
Result<Nothing> rollback() noexcept;
|
||||
|
||||
@@ -18,7 +18,13 @@
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
struct SelectFrom {
|
||||
using TableOrQueryType = rfl::Variant<Table, Ref<SelectFrom>>;
|
||||
struct Union {
|
||||
std::vector<std::string> columns;
|
||||
Ref<std::vector<SelectFrom>> selects;
|
||||
bool all = false;
|
||||
};
|
||||
|
||||
using TableOrQueryType = rfl::Variant<Table, Ref<SelectFrom>, Ref<Union>>;
|
||||
|
||||
struct Field {
|
||||
Operation val;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Drop.hpp"
|
||||
#include "Insert.hpp"
|
||||
#include "SelectFrom.hpp"
|
||||
#include "Union.hpp"
|
||||
#include "Update.hpp"
|
||||
#include "Write.hpp"
|
||||
|
||||
@@ -17,7 +18,7 @@ namespace sqlgen::dynamic {
|
||||
|
||||
using Statement =
|
||||
rfl::TaggedUnion<"stmt", CreateAs, CreateIndex, CreateTable, DeleteFrom,
|
||||
Drop, Insert, SelectFrom, Update, Write>;
|
||||
Drop, Insert, SelectFrom, Union, Update, Write>;
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef SQLGEN_DYNAMIC_UNION_HPP_
|
||||
#define SQLGEN_DYNAMIC_UNION_HPP_
|
||||
|
||||
#include "SelectFrom.hpp"
|
||||
|
||||
namespace sqlgen::dynamic {
|
||||
|
||||
using Union = SelectFrom::Union;
|
||||
|
||||
} // namespace sqlgen::dynamic
|
||||
|
||||
#endif
|
||||
@@ -17,12 +17,18 @@ struct GetColType {
|
||||
static Type get_value(const T& _t) { return _t; }
|
||||
};
|
||||
|
||||
template <rfl::internal::StringLiteral _name>
|
||||
struct GetColType<Col<_name>> {
|
||||
using Type = transpilation::Col<_name>;
|
||||
static Type get_value(const auto&) { return transpilation::Col<_name>{}; }
|
||||
template <rfl::internal::StringLiteral _name,
|
||||
rfl::internal::StringLiteral _alias>
|
||||
struct GetColType<Col<_name, _alias>> {
|
||||
using Type = transpilation::Col<_name, _alias>;
|
||||
static Type get_value(const auto&) {
|
||||
return transpilation::Col<_name, _alias>{};
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using get_col_type_t = typename GetColType<T>::Type;
|
||||
|
||||
} // namespace sqlgen::internal
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#include <rfl.hpp>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sqlgen::internal {
|
||||
|
||||
template <class TupleT>
|
||||
struct AllSame;
|
||||
|
||||
template <class Head, class... Tail>
|
||||
struct AllSame<std::tuple<Head, Tail...>> {
|
||||
static constexpr bool value = std::conjunction_v<std::is_same<Head, Tail>...>;
|
||||
};
|
||||
|
||||
template <class Head, class... Tail>
|
||||
struct AllSame<rfl::Tuple<Head, Tail...>> {
|
||||
static constexpr bool value = std::conjunction_v<std::is_same<Head, Tail>...>;
|
||||
};
|
||||
|
||||
template <class TupleT>
|
||||
constexpr bool all_same_v = AllSame<std::remove_cvref_t<TupleT>>::value;
|
||||
|
||||
} // namespace sqlgen::internal
|
||||
@@ -13,7 +13,10 @@
|
||||
#include "../Result.hpp"
|
||||
#include "../Transaction.hpp"
|
||||
#include "../dynamic/Column.hpp"
|
||||
#include "../dynamic/Insert.hpp"
|
||||
#include "../dynamic/SelectFrom.hpp"
|
||||
#include "../dynamic/Statement.hpp"
|
||||
#include "../dynamic/Union.hpp"
|
||||
#include "../dynamic/Write.hpp"
|
||||
#include "../internal/to_container.hpp"
|
||||
#include "../internal/write_or_insert.hpp"
|
||||
@@ -54,7 +57,7 @@ class SQLGEN_API Connection {
|
||||
}
|
||||
|
||||
template <class ContainerType>
|
||||
auto read(const dynamic::SelectFrom& _query) {
|
||||
auto read(const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query) {
|
||||
using ValueType = transpilation::value_t<ContainerType>;
|
||||
return internal::to_container<ContainerType>(
|
||||
read_impl(_query).transform([](auto&& _it) {
|
||||
@@ -94,7 +97,8 @@ class SQLGEN_API Connection {
|
||||
const std::variant<dynamic::Insert, dynamic::Write>& _stmt)
|
||||
const noexcept;
|
||||
|
||||
Result<Ref<Iterator>> read_impl(const dynamic::SelectFrom& _query);
|
||||
Result<Ref<Iterator>> read_impl(
|
||||
const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query);
|
||||
|
||||
Result<Nothing> write_impl(
|
||||
const std::vector<std::vector<std::optional<std::string>>>& _data);
|
||||
|
||||
@@ -4,16 +4,21 @@
|
||||
#include <libpq-fe.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <rfl.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../Iterator.hpp"
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../Transaction.hpp"
|
||||
#include "../dynamic/Column.hpp"
|
||||
#include "../dynamic/Insert.hpp"
|
||||
#include "../dynamic/SelectFrom.hpp"
|
||||
#include "../dynamic/Statement.hpp"
|
||||
#include "../dynamic/Union.hpp"
|
||||
#include "../dynamic/Write.hpp"
|
||||
#include "../internal/iterator_t.hpp"
|
||||
#include "../internal/to_container.hpp"
|
||||
@@ -57,7 +62,7 @@ class SQLGEN_API Connection {
|
||||
}
|
||||
|
||||
template <class ContainerType>
|
||||
auto read(const dynamic::SelectFrom& _query) {
|
||||
auto read(const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query) {
|
||||
using ValueType = transpilation::value_t<ContainerType>;
|
||||
return internal::to_container<ContainerType>(
|
||||
read_impl(_query).transform([](auto&& _it) {
|
||||
@@ -86,7 +91,8 @@ class SQLGEN_API Connection {
|
||||
const std::vector<std::vector<std::optional<std::string>>>&
|
||||
_data) noexcept;
|
||||
|
||||
Result<Ref<Iterator>> read_impl(const dynamic::SelectFrom& _query);
|
||||
Result<Ref<Iterator>> read_impl(
|
||||
const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query);
|
||||
|
||||
std::string to_buffer(
|
||||
const std::vector<std::optional<std::string>>& _line) const noexcept;
|
||||
|
||||
@@ -32,21 +32,14 @@
|
||||
|
||||
namespace sqlgen {
|
||||
|
||||
template <class TableTupleType, class AliasType, class FieldsType,
|
||||
class TableOrQueryType, class JoinsType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType,
|
||||
class ContainerType, class Connection>
|
||||
template <class SelectFromT, class ContainerType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
auto select_from_impl(const Ref<Connection>& _conn, const FieldsType& _fields,
|
||||
const TableOrQueryType& _table_or_query,
|
||||
const JoinsType& _joins, const WhereType& _where,
|
||||
const LimitType& _limit) {
|
||||
auto select_from_impl(const Ref<Connection>& _conn, const auto& _fields,
|
||||
const auto& _table_or_query, const auto& _joins,
|
||||
const auto& _where, const auto& _limit) {
|
||||
if constexpr (internal::is_range_v<ContainerType>) {
|
||||
const auto query =
|
||||
transpilation::to_select_from<TableTupleType, AliasType, FieldsType,
|
||||
TableOrQueryType, JoinsType, WhereType,
|
||||
GroupByType, OrderByType, LimitType>(
|
||||
_fields, _table_or_query, _joins, _where, _limit);
|
||||
const auto query = transpilation::to_select_from<SelectFromT>(
|
||||
_fields, _table_or_query, _joins, _where, _limit);
|
||||
return _conn->template read<ContainerType>(query);
|
||||
|
||||
} else {
|
||||
@@ -64,43 +57,51 @@ auto select_from_impl(const Ref<Connection>& _conn, const FieldsType& _fields,
|
||||
return container;
|
||||
};
|
||||
|
||||
using IteratorType = internal::iterator_t<
|
||||
transpilation::fields_to_named_tuple_t<TableTupleType, FieldsType>,
|
||||
decltype(_conn)>;
|
||||
using IteratorType =
|
||||
internal::iterator_t<transpilation::fields_to_named_tuple_t<
|
||||
typename SelectFromT::TableTupleType,
|
||||
typename SelectFromT::FieldsType>,
|
||||
decltype(_conn)>;
|
||||
|
||||
using RangeType = Range<IteratorType>;
|
||||
|
||||
return select_from_impl<TableTupleType, AliasType, FieldsType,
|
||||
TableOrQueryType, JoinsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType, RangeType>(
|
||||
return select_from_impl<SelectFromT, RangeType>(
|
||||
_conn, _fields, _table_or_query, _joins, _where, _limit)
|
||||
.and_then(to_container);
|
||||
}
|
||||
}
|
||||
|
||||
template <class TableTupleType, class AliasType, class FieldsType,
|
||||
class TableOrQueryType, class JoinsType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType,
|
||||
class ContainerType, class Connection>
|
||||
template <class SelectFromT, class ContainerType, class Connection>
|
||||
requires is_connection<Connection>
|
||||
auto select_from_impl(const Result<Ref<Connection>>& _res,
|
||||
const FieldsType& _fields,
|
||||
const TableOrQueryType& _table_or_query,
|
||||
const JoinsType& _joins, const WhereType& _where,
|
||||
const LimitType& _limit) {
|
||||
auto select_from_impl(const Result<Ref<Connection>>& _res, const auto& _fields,
|
||||
const auto& _table_or_query, const auto& _joins,
|
||||
const auto& _where, const auto& _limit) {
|
||||
return _res.and_then([&](const auto& _conn) {
|
||||
return select_from_impl<TableTupleType, AliasType, FieldsType,
|
||||
TableOrQueryType, JoinsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType, ContainerType>(
|
||||
return select_from_impl<SelectFromT, ContainerType>(
|
||||
_conn, _fields, _table_or_query, _joins, _where, _limit);
|
||||
});
|
||||
}
|
||||
|
||||
template <class TableOrQueryType, class AliasType, class FieldsType,
|
||||
class JoinsType = Nothing, class WhereType = Nothing,
|
||||
class GroupByType = Nothing, class OrderByType = Nothing,
|
||||
class LimitType = Nothing, class ToType = Nothing>
|
||||
template <class TableOrQueryT, class AliasT, class FieldsT,
|
||||
class JoinsT = Nothing, class WhereT = Nothing,
|
||||
class GroupByT = Nothing, class OrderByT = Nothing,
|
||||
class LimitT = Nothing, class ToT = Nothing>
|
||||
struct SelectFrom {
|
||||
using TableOrQueryType = TableOrQueryT;
|
||||
using AliasType = AliasT;
|
||||
using FieldsType = FieldsT;
|
||||
using JoinsType = JoinsT;
|
||||
using WhereType = WhereT;
|
||||
using GroupByType = GroupByT;
|
||||
using OrderByType = OrderByT;
|
||||
using LimitType = LimitT;
|
||||
using ToType = ToT;
|
||||
|
||||
using SelectFromTypes =
|
||||
transpilation::SelectFromTypes<AliasType, FieldsType, TableOrQueryType,
|
||||
JoinsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType>;
|
||||
|
||||
auto operator()(const auto& _conn) const {
|
||||
using TableTupleType =
|
||||
transpilation::table_tuple_t<TableOrQueryType, AliasType, JoinsType>;
|
||||
@@ -113,9 +114,7 @@ struct SelectFrom {
|
||||
|
||||
using ContainerType = std::conditional_t<std::is_same_v<ToType, Nothing>,
|
||||
Range<IteratorType>, ToType>;
|
||||
return select_from_impl<
|
||||
TableTupleType, AliasType, FieldsType, TableOrQueryType, JoinsType,
|
||||
WhereType, GroupByType, OrderByType, LimitType, ContainerType>(
|
||||
return select_from_impl<SelectFromTypes, ContainerType>(
|
||||
_conn, fields_, from_, joins_, where_, limit_);
|
||||
|
||||
} else {
|
||||
@@ -129,9 +128,7 @@ struct SelectFrom {
|
||||
return std::move(_vec[0]);
|
||||
};
|
||||
|
||||
return select_from_impl<TableTupleType, AliasType, FieldsType,
|
||||
TableOrQueryType, JoinsType, WhereType,
|
||||
GroupByType, OrderByType, LimitType,
|
||||
return select_from_impl<SelectFromTypes,
|
||||
std::vector<std::remove_cvref_t<ToType>>>(
|
||||
_conn, fields_, from_, joins_, where_, limit_)
|
||||
.and_then(extract_result);
|
||||
@@ -311,12 +308,9 @@ struct ToTableOrQuery<
|
||||
SelectFrom<TableOrQueryType, AliasType, FieldsType, JoinsType, WhereType,
|
||||
GroupByType, OrderByType, LimitType, ToType>> {
|
||||
dynamic::SelectFrom::TableOrQueryType operator()(const auto& _query) {
|
||||
using TableTupleType =
|
||||
table_tuple_t<TableOrQueryType, AliasType, JoinsType>;
|
||||
using QueryType = std::remove_cvref_t<decltype(_query)>;
|
||||
return Ref<dynamic::SelectFrom>::make(
|
||||
transpilation::to_select_from<TableTupleType, AliasType, FieldsType,
|
||||
TableOrQueryType, JoinsType, WhereType,
|
||||
GroupByType, OrderByType, LimitType>(
|
||||
transpilation::to_select_from<typename QueryType::SelectFromTypes>(
|
||||
_query.fields_, _query.from_, _query.joins_, _query.where_,
|
||||
_query.limit_));
|
||||
}
|
||||
@@ -347,7 +341,7 @@ inline auto select_from(const FieldTypes&... _fields) {
|
||||
.from_ = transpilation::TableWrapper<TableType>{}};
|
||||
}
|
||||
|
||||
template <rfl::internal::StringLiteral _alias, class QueryType,
|
||||
template <rfl::internal::StringLiteral _alias = "", class QueryType,
|
||||
class... FieldTypes>
|
||||
inline auto select_from(const QueryType& _query, const FieldTypes&... _fields) {
|
||||
using FieldsType =
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../Transaction.hpp"
|
||||
#include "../dynamic/SelectFrom.hpp"
|
||||
#include "../dynamic/Union.hpp"
|
||||
#include "../dynamic/Write.hpp"
|
||||
#include "../internal/to_container.hpp"
|
||||
#include "../internal/write_or_insert.hpp"
|
||||
@@ -50,7 +52,7 @@ class SQLGEN_API Connection {
|
||||
}
|
||||
|
||||
template <class ContainerType>
|
||||
auto read(const dynamic::SelectFrom& _query) {
|
||||
auto read(const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query) {
|
||||
using ValueType = transpilation::value_t<ContainerType>;
|
||||
return internal::to_container<ContainerType>(
|
||||
read_impl(_query).transform([](auto&& _it) {
|
||||
@@ -92,7 +94,8 @@ class SQLGEN_API Connection {
|
||||
Result<StmtPtr> prepare_statement(const std::string& _sql) const noexcept;
|
||||
|
||||
/// Implements the actual read.
|
||||
Result<Ref<Iterator>> read_impl(const dynamic::SelectFrom& _query);
|
||||
Result<Ref<Iterator>> read_impl(
|
||||
const rfl::Variant<dynamic::SelectFrom, dynamic::Union>& _query);
|
||||
|
||||
/// Implements the actual write
|
||||
Result<Nothing> write_impl(
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef SQLGEN_TRANSPILATION_UNION_HPP_
|
||||
#define SQLGEN_TRANSPILATION_UNION_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class... SelectTs>
|
||||
struct Union {};
|
||||
|
||||
} // namespace sqlgen::transpilation
|
||||
|
||||
#endif
|
||||
@@ -4,7 +4,10 @@
|
||||
#include <rfl.hpp>
|
||||
|
||||
#include "../Literal.hpp"
|
||||
#include "../internal/all_same_v.hpp"
|
||||
#include "Union.hpp"
|
||||
#include "make_field.hpp"
|
||||
#include "table_tuple_t.hpp"
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
@@ -38,6 +41,32 @@ struct FieldsToNamedTupleType<StructType, rfl::Tuple<FieldTypes...>> {
|
||||
using Type = typename FieldsToNamedTupleType<StructType, FieldTypes...>::Type;
|
||||
};
|
||||
|
||||
template <class... SelectTs>
|
||||
struct FieldsToNamedTupleType<Union<SelectTs...>> {
|
||||
using NamedTupleTypes = rfl::Tuple<typename FieldsToNamedTupleType<
|
||||
table_tuple_t<typename SelectTs::TableOrQueryType,
|
||||
typename SelectTs::AliasType, typename SelectTs::JoinsType>,
|
||||
typename SelectTs::FieldsType>::Type...>;
|
||||
static_assert(
|
||||
sqlgen::internal::all_same_v<NamedTupleTypes>,
|
||||
"All SELECT statements in a UNION must return the same columns with "
|
||||
"the same types.");
|
||||
using Type = rfl::tuple_element_t<0, NamedTupleTypes>;
|
||||
};
|
||||
|
||||
template <class... SelectTs, class... FieldTypes>
|
||||
struct FieldsToNamedTupleType<Union<SelectTs...>, FieldTypes...> {
|
||||
using Type = typename FieldsToNamedTupleType<
|
||||
typename FieldsToNamedTupleType<Union<SelectTs...>>::Type,
|
||||
FieldTypes...>::Type;
|
||||
};
|
||||
|
||||
template <class... SelectTs, class... FieldTypes>
|
||||
struct FieldsToNamedTupleType<Union<SelectTs...>, rfl::Tuple<FieldTypes...>> {
|
||||
using Type =
|
||||
typename FieldsToNamedTupleType<Union<SelectTs...>, FieldTypes...>::Type;
|
||||
};
|
||||
|
||||
template <class StructType, class... FieldTypes>
|
||||
using fields_to_named_tuple_t =
|
||||
typename FieldsToNamedTupleType<StructType, FieldTypes...>::Type;
|
||||
|
||||
@@ -27,15 +27,18 @@ dynamic::CreateAs to_create_as(const dynamic::CreateAs::What _what,
|
||||
const TableOrQueryType& _table_or_query,
|
||||
const JoinsType& _joins, const WhereType& _where,
|
||||
const LimitType& _limit) {
|
||||
using SelectFromTypes =
|
||||
transpilation::SelectFromTypes<AliasType, FieldsType, TableOrQueryType,
|
||||
JoinsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType>;
|
||||
|
||||
return dynamic::CreateAs{
|
||||
.what = _what,
|
||||
.table_or_view = dynamic::Table{.alias = std::nullopt,
|
||||
.name = get_tablename<T>(),
|
||||
.schema = get_schema<T>()},
|
||||
.query = to_select_from<TableTupleType, AliasType, FieldsType,
|
||||
TableOrQueryType, JoinsType, WhereType,
|
||||
GroupByType, OrderByType, LimitType>(
|
||||
_fields, _table_or_query, _joins, _where, _limit),
|
||||
.query = to_select_from<SelectFromTypes>(_fields, _table_or_query, _joins,
|
||||
_where, _limit),
|
||||
.or_replace = _or_replace,
|
||||
.if_not_exists = _if_not_exists};
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "flatten_fields_t.hpp"
|
||||
#include "get_table_t.hpp"
|
||||
#include "make_fields.hpp"
|
||||
#include "table_tuple_t.hpp"
|
||||
#include "to_alias.hpp"
|
||||
#include "to_condition.hpp"
|
||||
#include "to_group_by.hpp"
|
||||
@@ -30,14 +31,32 @@
|
||||
|
||||
namespace sqlgen::transpilation {
|
||||
|
||||
template <class TableTupleType, class AliasType, class FieldsType,
|
||||
class TableOrQueryType, class JoinsType, class WhereType,
|
||||
class GroupByType, class OrderByType, class LimitType>
|
||||
dynamic::SelectFrom to_select_from(const FieldsType& _fields,
|
||||
const TableOrQueryType& _table_or_query,
|
||||
const JoinsType& _joins,
|
||||
const WhereType& _where,
|
||||
const LimitType& _limit) {
|
||||
template <class AliasT, class FieldsT, class TableOrQueryT, class JoinsT,
|
||||
class WhereT, class GroupByT, class OrderByT, class LimitT>
|
||||
struct SelectFromTypes {
|
||||
using AliasType = AliasT;
|
||||
using FieldsType = FieldsT;
|
||||
using TableOrQueryType = TableOrQueryT;
|
||||
using JoinsType = JoinsT;
|
||||
using WhereType = WhereT;
|
||||
using GroupByType = GroupByT;
|
||||
using OrderByType = OrderByT;
|
||||
using LimitType = LimitT;
|
||||
|
||||
using TableTupleType = table_tuple_t<TableOrQueryType, AliasType, JoinsType>;
|
||||
};
|
||||
|
||||
template <class SelectFromT>
|
||||
dynamic::SelectFrom to_select_from(const auto& _fields,
|
||||
const auto& _table_or_query,
|
||||
const auto& _joins, const auto& _where,
|
||||
const auto& _limit) {
|
||||
using TableTupleType = typename SelectFromT::TableTupleType;
|
||||
using AliasType = typename SelectFromT::AliasType;
|
||||
using FieldsType = typename SelectFromT::FieldsType;
|
||||
using GroupByType = typename SelectFromT::GroupByType;
|
||||
using OrderByType = typename SelectFromT::OrderByType;
|
||||
|
||||
static_assert(check_aggregations<TableTupleType,
|
||||
flatten_fields_t<TableTupleType, FieldsType>,
|
||||
GroupByType>(),
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "../insert.hpp"
|
||||
#include "../read.hpp"
|
||||
#include "../select_from.hpp"
|
||||
#include "../unite.hpp"
|
||||
#include "../update.hpp"
|
||||
#include "columns_t.hpp"
|
||||
#include "read_to_select_from.hpp"
|
||||
@@ -22,6 +23,7 @@
|
||||
#include "to_drop.hpp"
|
||||
#include "to_insert_or_write.hpp"
|
||||
#include "to_select_from.hpp"
|
||||
#include "to_union.hpp"
|
||||
#include "to_update.hpp"
|
||||
#include "value_t.hpp"
|
||||
|
||||
@@ -96,18 +98,11 @@ struct ToSQL<Read<ContainerType, WhereType, OrderByType, LimitType>> {
|
||||
}
|
||||
};
|
||||
|
||||
template <class TableOrQueryType, class AliasType, class FieldsType,
|
||||
class JoinsType, class WhereType, class GroupByType,
|
||||
class OrderByType, class LimitType, class ToType>
|
||||
struct ToSQL<
|
||||
SelectFrom<TableOrQueryType, AliasType, FieldsType, JoinsType, WhereType,
|
||||
GroupByType, OrderByType, LimitType, ToType>> {
|
||||
template <class... Ts>
|
||||
struct ToSQL<SelectFrom<Ts...>> {
|
||||
dynamic::Statement operator()(const auto& _select_from) const {
|
||||
using TableTupleType =
|
||||
table_tuple_t<TableOrQueryType, AliasType, JoinsType>;
|
||||
return to_select_from<TableTupleType, AliasType, FieldsType,
|
||||
TableOrQueryType, JoinsType, WhereType, GroupByType,
|
||||
OrderByType, LimitType>(
|
||||
using SelectFromTypes = typename SelectFrom<Ts...>::SelectFromTypes;
|
||||
return to_select_from<SelectFromTypes>(
|
||||
_select_from.fields_, _select_from.from_, _select_from.joins_,
|
||||
_select_from.where_, _select_from.limit_);
|
||||
}
|
||||
@@ -120,6 +115,13 @@ struct ToSQL<Update<T, SetsType, WhereType>> {
|
||||
}
|
||||
};
|
||||
|
||||
template <class ContainerType, class... Selects>
|
||||
struct ToSQL<sqlgen::Union<ContainerType, Selects...>> {
|
||||
dynamic::Statement operator()(const auto& _union) const {
|
||||
return to_union<ContainerType>(_union.selects_, _union.all_);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
dynamic::Statement to_sql(const T& _t) {
|
||||
return ToSQL<std::remove_cvref_t<T>>{}(_t);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)>;
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)>;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user