From ebc80d616d7c39571905155d18b6fcf8ba1cfdf4 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Thu, 1 May 2025 07:27:40 +0200 Subject: [PATCH] Added limit --- include/sqlgen.hpp | 2 + include/sqlgen/dynamic/Limit.hpp | 4 +- include/sqlgen/limit.hpp | 29 ++++++++++++ include/sqlgen/order_by.hpp | 36 +++++++++++++++ include/sqlgen/read.hpp | 45 ++++++++---------- include/sqlgen/transpilation/Limit.hpp | 12 +++++ .../{OrderBy.hpp => order_by_t.hpp} | 4 +- include/sqlgen/transpilation/to_limit.hpp | 30 ++++++++++++ include/sqlgen/transpilation/to_order_by.hpp | 2 +- .../sqlgen/transpilation/to_select_from.hpp | 8 ++-- include/sqlgen/transpilation/value_t.hpp | 26 +++++++++++ tests/sqlite/test_limit.cpp | 46 +++++++++++++++++++ tests/sqlite/test_order_by.cpp | 6 ++- 13 files changed, 213 insertions(+), 37 deletions(-) create mode 100644 include/sqlgen/limit.hpp create mode 100644 include/sqlgen/order_by.hpp create mode 100644 include/sqlgen/transpilation/Limit.hpp rename include/sqlgen/transpilation/{OrderBy.hpp => order_by_t.hpp} (92%) create mode 100644 include/sqlgen/transpilation/to_limit.hpp create mode 100644 include/sqlgen/transpilation/value_t.hpp create mode 100644 tests/sqlite/test_limit.cpp diff --git a/include/sqlgen.hpp b/include/sqlgen.hpp index 71d066d..ee491b4 100644 --- a/include/sqlgen.hpp +++ b/include/sqlgen.hpp @@ -11,6 +11,8 @@ #include "sqlgen/Result.hpp" #include "sqlgen/Varchar.hpp" #include "sqlgen/col.hpp" +#include "sqlgen/limit.hpp" +#include "sqlgen/order_by.hpp" #include "sqlgen/read.hpp" #include "sqlgen/write.hpp" diff --git a/include/sqlgen/dynamic/Limit.hpp b/include/sqlgen/dynamic/Limit.hpp index 08d82d4..83330c4 100644 --- a/include/sqlgen/dynamic/Limit.hpp +++ b/include/sqlgen/dynamic/Limit.hpp @@ -1,9 +1,7 @@ #ifndef SQLGEN_DYNAMIC_LIMIT_HPP_ #define SQLGEN_DYNAMIC_LIMIT_HPP_ -#include - -#include "Column.hpp" +#include namespace sqlgen::dynamic { diff --git a/include/sqlgen/limit.hpp b/include/sqlgen/limit.hpp new file mode 100644 index 0000000..a2cf811 --- /dev/null +++ b/include/sqlgen/limit.hpp @@ -0,0 +1,29 @@ +#ifndef SQLGEN_LIMIT_HPP_ +#define SQLGEN_LIMIT_HPP_ + +#include + +#include "Result.hpp" +#include "read.hpp" +#include "transpilation/Limit.hpp" +#include "transpilation/value_t.hpp" + +namespace sqlgen { + +using Limit = transpilation::Limit; + +template +auto operator|(const Read& _r, + const Limit& _limit) { + static_assert(std::is_same_v, + "You cannot call limit twice (but you can order by more " + "than one column)."); + return Read{.order_by_ = _r.order_by_, + .limit_ = _limit}; +} + +inline auto limit(const size_t _val) { return Limit{_val}; }; + +} // namespace sqlgen + +#endif diff --git a/include/sqlgen/order_by.hpp b/include/sqlgen/order_by.hpp new file mode 100644 index 0000000..426f33d --- /dev/null +++ b/include/sqlgen/order_by.hpp @@ -0,0 +1,36 @@ +#ifndef SQLGEN_ORDER_BY_HPP_ +#define SQLGEN_ORDER_BY_HPP_ + +#include + +#include "Result.hpp" +#include "read.hpp" +#include "transpilation/order_by_t.hpp" +#include "transpilation/value_t.hpp" + +namespace sqlgen { + +template +struct OrderBy {}; + +template +auto operator|(const Read&, + const OrderBy&) { + static_assert(std::is_same_v, + "You cannot call order_by twice (but you can order by more " + "than one column)."); + static_assert(sizeof...(ColTypes) != 0, + "You must assign at least one column to order by."); + return Read, + std::remove_cvref_t...>>{}; +} + +template +auto order_by(const ColTypes&...) { + return OrderBy{}; +}; + +} // namespace sqlgen + +#endif diff --git a/include/sqlgen/read.hpp b/include/sqlgen/read.hpp index e8a8356..f628506 100644 --- a/include/sqlgen/read.hpp +++ b/include/sqlgen/read.hpp @@ -8,16 +8,18 @@ #include "Ref.hpp" #include "Result.hpp" #include "internal/is_range.hpp" -#include "transpilation/OrderBy.hpp" #include "transpilation/to_select_from.hpp" +#include "transpilation/value_t.hpp" namespace sqlgen { -template -Result read_impl(const Ref& _conn) { +template +Result read_impl(const Ref& _conn, + const LimitType& _limit) { + using ValueType = transpilation::value_t; 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(_limit); return _conn->read(query).transform( [](auto&& _it) { return ContainerType(_it); }); @@ -34,42 +36,33 @@ Result read_impl(const Ref& _conn) { return container; }; - using ValueType = typename ContainerType::value_type; - return read_impl, OrderByType>(_conn).and_then( - to_container); + return read_impl, OrderByType>(_conn, _limit) + .and_then(to_container); } } -template -Result read_impl(const Result>& _res) { - return _res.and_then([](const auto& _conn) { - return read_impl(_conn); +template +Result read_impl(const Result>& _res, + const LimitType& _limit) { + return _res.and_then([&](const auto& _conn) { + return read_impl(_conn, _limit); }); } -template +template struct Read { Result operator()(const auto& _conn) const noexcept { try { - return read_impl(_conn); + return read_impl(_conn, limit_); } catch (std::exception& e) { return error(e.what()); } } - template - auto order_by(const ColTypes&...) const noexcept { - static_assert(std::is_same_v, - "order_by is already assigned."); - static_assert(sizeof...(ColTypes) != 0, - "You must assign at least one column to order by."); - return Read< - ContainerType, - transpilation::order_by_t...>>{}; - } - OrderByType order_by_; + + LimitType limit_; }; template diff --git a/include/sqlgen/transpilation/Limit.hpp b/include/sqlgen/transpilation/Limit.hpp new file mode 100644 index 0000000..a181c58 --- /dev/null +++ b/include/sqlgen/transpilation/Limit.hpp @@ -0,0 +1,12 @@ +#ifndef SQLGEN_TRANSPILATION_LIMIT_HPP_ +#define SQLGEN_TRANSPILATION_LIMIT_HPP_ + +#include "../dynamic/Limit.hpp" + +namespace sqlgen::transpilation { + +using Limit = dynamic::Limit; + +} // namespace sqlgen::transpilation + +#endif diff --git a/include/sqlgen/transpilation/OrderBy.hpp b/include/sqlgen/transpilation/order_by_t.hpp similarity index 92% rename from include/sqlgen/transpilation/OrderBy.hpp rename to include/sqlgen/transpilation/order_by_t.hpp index 97c975e..70d69dd 100644 --- a/include/sqlgen/transpilation/OrderBy.hpp +++ b/include/sqlgen/transpilation/order_by_t.hpp @@ -1,5 +1,5 @@ -#ifndef SQLGEN_TRANSPILATION_ORDERBY_HPP_ -#define SQLGEN_TRANSPILATION_ORDERBY_HPP_ +#ifndef SQLGEN_TRANSPILATION_ORDERBYT_HPP_ +#define SQLGEN_TRANSPILATION_ORDERBYT_HPP_ #include #include diff --git a/include/sqlgen/transpilation/to_limit.hpp b/include/sqlgen/transpilation/to_limit.hpp new file mode 100644 index 0000000..70fe974 --- /dev/null +++ b/include/sqlgen/transpilation/to_limit.hpp @@ -0,0 +1,30 @@ +#ifndef SQLGEN_TRANSPILATION_TO_LIMIT_HPP_ +#define SQLGEN_TRANSPILATION_TO_LIMIT_HPP_ + +#include +#include +#include + +#include "../Result.hpp" +#include "../dynamic/Limit.hpp" +#include "Limit.hpp" +#include "order_by_t.hpp" + +namespace sqlgen::transpilation { + +template +std::optional to_limit(const LimitType& _limit) { + if constexpr (std::is_same_v, Nothing>) { + return std::nullopt; + + } else if constexpr (std::is_same_v, Limit>) { + return _limit; + + } else { + static_assert(rfl::always_false_v, "Unsupported type"); + } +} + +} // namespace sqlgen::transpilation + +#endif diff --git a/include/sqlgen/transpilation/to_order_by.hpp b/include/sqlgen/transpilation/to_order_by.hpp index 5b5a9e0..af483b2 100644 --- a/include/sqlgen/transpilation/to_order_by.hpp +++ b/include/sqlgen/transpilation/to_order_by.hpp @@ -5,7 +5,7 @@ #include "../Result.hpp" #include "../dynamic/OrderBy.hpp" -#include "OrderBy.hpp" +#include "order_by_t.hpp" namespace sqlgen::transpilation { diff --git a/include/sqlgen/transpilation/to_select_from.hpp b/include/sqlgen/transpilation/to_select_from.hpp index e42c62e..88d5e7c 100644 --- a/include/sqlgen/transpilation/to_select_from.hpp +++ b/include/sqlgen/transpilation/to_select_from.hpp @@ -14,14 +14,15 @@ #include "get_schema.hpp" #include "get_tablename.hpp" #include "make_columns.hpp" +#include "to_limit.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() { +dynamic::SelectFrom to_select_from(const LimitType& _limit = LimitType{}) { using NamedTupleType = rfl::named_tuple_t>; using Fields = typename NamedTupleType::Fields; @@ -31,7 +32,8 @@ dynamic::SelectFrom to_select_from() { return dynamic::SelectFrom{.table = dynamic::Table{.name = get_tablename(), .schema = get_schema()}, .columns = columns, - .order_by = to_order_by()}; + .order_by = to_order_by(), + .limit = to_limit(_limit)}; } } // namespace sqlgen::transpilation diff --git a/include/sqlgen/transpilation/value_t.hpp b/include/sqlgen/transpilation/value_t.hpp new file mode 100644 index 0000000..8bdb1ba --- /dev/null +++ b/include/sqlgen/transpilation/value_t.hpp @@ -0,0 +1,26 @@ +#ifndef SQLGEN_TRANSPILATION_VALUET_HPP_ +#define SQLGEN_TRANSPILATION_VALUET_HPP_ + +#include "../Range.hpp" + +namespace sqlgen::transpilation { + +template +struct ValueType; + +template +struct ValueType { + using Type = typename ContainerType::value_type; +}; + +template +struct ValueType> { + using Type = typename Range::value_type::value_type; +}; + +template +using value_t = typename ValueType::Type; + +} // namespace sqlgen::transpilation + +#endif diff --git a/tests/sqlite/test_limit.cpp b/tests/sqlite/test_limit.cpp new file mode 100644 index 0000000..940429c --- /dev/null +++ b/tests/sqlite/test_limit.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include +#include +#include +#include + +namespace test_limit { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(sqlite, test_limit) { + 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); + + using namespace sqlgen; + + const auto query = + sqlgen::read> | order_by(col<"age">) | limit(2); + + 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}])"; + + EXPECT_EQ(rfl::json::write(people2), expected); +} + +} // namespace test_limit diff --git a/tests/sqlite/test_order_by.cpp b/tests/sqlite/test_order_by.cpp index f39d38f..f0e2a80 100644 --- a/tests/sqlite/test_order_by.cpp +++ b/tests/sqlite/test_order_by.cpp @@ -30,8 +30,10 @@ TEST(sqlite, test_order_by) { sqlgen::write(conn, people1); - const auto query = sqlgen::read>.order_by( - sqlgen::col<"age">, sqlgen::col<"first_name">.desc()); + using namespace sqlgen; + + const auto query = sqlgen::read> | + order_by(col<"age">, col<"first_name">.desc()); const auto people2 = query(conn).value();