From f3d687e1bc2aac41601760702cb26fe07268c850 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Wed, 30 Apr 2025 08:43:32 +0200 Subject: [PATCH] Completed support for order_by --- include/sqlgen/dynamic/OrderBy.hpp | 9 ++-- include/sqlgen/read.hpp | 23 +++++----- include/sqlgen/transpilation/OrderBy.hpp | 4 +- include/sqlgen/transpilation/to_order_by.hpp | 43 ++++++++++++++++++ .../sqlgen/transpilation/to_select_from.hpp | 7 ++- tests/sqlite/test_order_by.cpp | 44 +++++++++++++++++++ 6 files changed, 113 insertions(+), 17 deletions(-) create mode 100644 include/sqlgen/transpilation/to_order_by.hpp create mode 100644 tests/sqlite/test_order_by.cpp diff --git a/include/sqlgen/dynamic/OrderBy.hpp b/include/sqlgen/dynamic/OrderBy.hpp index f64938f..96f000c 100644 --- a/include/sqlgen/dynamic/OrderBy.hpp +++ b/include/sqlgen/dynamic/OrderBy.hpp @@ -7,11 +7,12 @@ namespace sqlgen::dynamic { +struct Wrapper { + Column column; + bool desc = false; +}; + struct OrderBy { - struct Wrapper { - Column column; - bool desc = false; - }; std::vector columns; }; diff --git a/include/sqlgen/read.hpp b/include/sqlgen/read.hpp index ebd92a6..e8a8356 100644 --- a/include/sqlgen/read.hpp +++ b/include/sqlgen/read.hpp @@ -13,11 +13,11 @@ namespace sqlgen { -template +template Result read_impl(const Ref& _conn) { if constexpr (internal::is_range_v) { using ValueType = typename ContainerType::value_type::value_type; - const auto query = transpilation::to_select_from(); + const auto query = transpilation::to_select_from(); return _conn->read(query).transform( [](auto&& _it) { return ContainerType(_it); }); @@ -35,21 +35,23 @@ Result read_impl(const Ref& _conn) { }; using ValueType = typename ContainerType::value_type; - return read_impl>(_conn).and_then(to_container); + return read_impl, OrderByType>(_conn).and_then( + to_container); } } -template +template Result read_impl(const Result>& _res) { - return _res.and_then( - [](const auto& _conn) { return read_impl(_conn); }); + return _res.and_then([](const auto& _conn) { + return read_impl(_conn); + }); } template struct Read { Result operator()(const auto& _conn) const noexcept { try { - return read_impl(_conn); + return read_impl(_conn); } catch (std::exception& e) { return error(e.what()); } @@ -61,9 +63,10 @@ struct Read { "order_by is already assigned."); static_assert(sizeof...(ColTypes) != 0, "You must assign at least one column to order by."); - return Read...>>{}; + return Read< + ContainerType, + transpilation::order_by_t...>>{}; } OrderByType order_by_; diff --git a/include/sqlgen/transpilation/OrderBy.hpp b/include/sqlgen/transpilation/OrderBy.hpp index ef4900a..97c975e 100644 --- a/include/sqlgen/transpilation/OrderBy.hpp +++ b/include/sqlgen/transpilation/OrderBy.hpp @@ -31,7 +31,9 @@ struct OrderBy {}; template auto make_order_by() { - static_assert(all_columns_exist(), "All columns must exist."); + static_assert( + all_columns_exist::ColType...>(), + "All columns must exist."); return OrderBy...>{}; } diff --git a/include/sqlgen/transpilation/to_order_by.hpp b/include/sqlgen/transpilation/to_order_by.hpp new file mode 100644 index 0000000..5b5a9e0 --- /dev/null +++ b/include/sqlgen/transpilation/to_order_by.hpp @@ -0,0 +1,43 @@ +#ifndef SQLGEN_TRANSPILATION_TO_ORDER_BY_HPP_ +#define SQLGEN_TRANSPILATION_TO_ORDER_BY_HPP_ + +#include + +#include "../Result.hpp" +#include "../dynamic/OrderBy.hpp" +#include "OrderBy.hpp" + +namespace sqlgen::transpilation { + +template +struct ToOrderBy; + +template <> +struct ToOrderBy { + std::optional operator()() const { return std::nullopt; } +}; + +template +struct ToOrderBy> { + template + dynamic::Wrapper to_wrapper() const { + using Name = typename W::ColType::Name; + const auto column = dynamic::Column{.alias = std::nullopt, + .name = Name().str(), + .type = dynamic::types::Unknown{}}; + return dynamic::Wrapper{.column = column, .desc = W::desc}; + } + + std::optional operator()() const { + const auto columns = + std::vector({to_wrapper()...}); + return dynamic::OrderBy{.columns = columns}; + } +}; + +template +const auto to_order_by = ToOrderBy{}; + +} // namespace sqlgen::transpilation + +#endif diff --git a/include/sqlgen/transpilation/to_select_from.hpp b/include/sqlgen/transpilation/to_select_from.hpp index f2c7218..e42c62e 100644 --- a/include/sqlgen/transpilation/to_select_from.hpp +++ b/include/sqlgen/transpilation/to_select_from.hpp @@ -8,15 +8,17 @@ #include #include +#include "../Result.hpp" #include "../dynamic/SelectFrom.hpp" #include "../dynamic/Table.hpp" #include "get_schema.hpp" #include "get_tablename.hpp" #include "make_columns.hpp" +#include "to_order_by.hpp" namespace sqlgen::transpilation { -template +template requires std::is_class_v> && std::is_aggregate_v> dynamic::SelectFrom to_select_from() { @@ -28,7 +30,8 @@ dynamic::SelectFrom to_select_from() { return dynamic::SelectFrom{.table = dynamic::Table{.name = get_tablename(), .schema = get_schema()}, - .columns = columns}; + .columns = columns, + .order_by = to_order_by()}; } } // namespace sqlgen::transpilation diff --git a/tests/sqlite/test_order_by.cpp b/tests/sqlite/test_order_by.cpp new file mode 100644 index 0000000..f39d38f --- /dev/null +++ b/tests/sqlite/test_order_by.cpp @@ -0,0 +1,44 @@ +#include + +#include +#include +#include +#include +#include + +namespace test_order_by { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(sqlite, test_order_by) { + const auto people1 = std::vector( + {Person{ + .id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45}, + Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10}, + Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8}, + Person{ + .id = 3, .first_name = "Maggie", .last_name = "Simpson", .age = 0}, + Person{ + .id = 4, .first_name = "Hugo", .last_name = "Simpson", .age = 10}}); + + const auto conn = sqlgen::sqlite::connect(); + + sqlgen::write(conn, people1); + + const auto query = sqlgen::read>.order_by( + sqlgen::col<"age">, sqlgen::col<"first_name">.desc()); + + const auto people2 = query(conn).value(); + + const std::string expected = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":4,"first_name":"Hugo","last_name":"Simpson","age":10},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10},{"id":0,"first_name":"Homer","last_name":"Simpson","age":45}])"; + + EXPECT_EQ(rfl::json::write(people2), expected); +} + +} // namespace test_order_by