diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index fc7cc4e7a..90fa2ea83 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -534,32 +534,15 @@ namespace sqlite_orm::internal { } }; - template<> - struct statement_serializer { - using statement_type = window_ref_t; - - template - SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, - const Ctx&) SQLITE_ORM_OR_CONST_CALLOP { - return statement.name; - } - }; - template void serialize_over_arguments(std::stringstream& ss, const Tuple& arguments, const Ctx& context) { - using args_tuple = std::decay_t; - constexpr bool is_named_ref = std::tuple_size::value == 1 && - std::is_same, window_ref_t>::value; - if constexpr (is_named_ref) { + if constexpr (std::tuple_size::value == 0) { + ss << " OVER ()"; + } else if constexpr (std::tuple_size::value == 1 && + std::is_same::type, window_ref_t>::value) { ss << " OVER " << std::get<0>(arguments).name; } else { - ss << " OVER ("; - std::string separator; - iterate_tuple(arguments, [&ss, &context, &separator](auto& arg) { - ss << separator << serialize(arg, context); - separator = " "; - }); - ss << ")"; + ss << " OVER (" << streaming_actions_tuple(arguments, context) << ")"; } } @@ -599,13 +582,8 @@ namespace sqlite_orm::internal { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; - ss << "WINDOW " << statement.name << " AS ("; - std::string separator; - iterate_tuple(statement.arguments, [&ss, &context, &separator](auto& arg) { - ss << separator << serialize(arg, context); - separator = " "; - }); - ss << ")"; + ss << "WINDOW " << statement.name << " AS (" << streaming_actions_tuple(statement.arguments, context) + << ")"; return ss.str(); } }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 261b47294..53ca39d6b 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -22194,32 +22194,15 @@ namespace sqlite_orm::internal { } }; - template<> - struct statement_serializer { - using statement_type = window_ref_t; - - template - SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, - const Ctx&) SQLITE_ORM_OR_CONST_CALLOP { - return statement.name; - } - }; - template void serialize_over_arguments(std::stringstream& ss, const Tuple& arguments, const Ctx& context) { - using args_tuple = std::decay_t; - constexpr bool is_named_ref = std::tuple_size::value == 1 && - std::is_same, window_ref_t>::value; - if constexpr (is_named_ref) { + if constexpr (std::tuple_size::value == 0) { + ss << " OVER ()"; + } else if constexpr (std::tuple_size::value == 1 && + std::is_same::type, window_ref_t>::value) { ss << " OVER " << std::get<0>(arguments).name; } else { - ss << " OVER ("; - std::string separator; - iterate_tuple(arguments, [&ss, &context, &separator](auto& arg) { - ss << separator << serialize(arg, context); - separator = " "; - }); - ss << ")"; + ss << " OVER (" << streaming_actions_tuple(arguments, context) << ")"; } } @@ -22259,13 +22242,8 @@ namespace sqlite_orm::internal { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; - ss << "WINDOW " << statement.name << " AS ("; - std::string separator; - iterate_tuple(statement.arguments, [&ss, &context, &separator](auto& arg) { - ss << separator << serialize(arg, context); - separator = " "; - }); - ss << ")"; + ss << "WINDOW " << statement.name << " AS (" << streaming_actions_tuple(statement.arguments, context) + << ")"; return ss.str(); } }; diff --git a/tests/window_function_tests.cpp b/tests/window_function_tests.cpp index 066542425..d87bac141 100644 --- a/tests/window_function_tests.cpp +++ b/tests/window_function_tests.cpp @@ -200,3 +200,50 @@ TEST_CASE("window functions") { REQUIRE(std::get<2>(rows[5]) == 2); } } + +TEST_CASE("window functions - issue #475 count over with where order by limit") { + struct UserProfile { + int id = 0; + std::string firstName; + std::string lastName; + }; + + auto storage = make_storage("", + make_table("user_profile", + make_column("id", &UserProfile::id, primary_key().autoincrement()), + make_column("first_name", &UserProfile::firstName), + make_column("last_name", &UserProfile::lastName))); + storage.sync_schema(); + + storage.insert(UserProfile{0, "Alice", "Smith"}); + storage.insert(UserProfile{0, "Bob", "Jones"}); + storage.insert(UserProfile{0, "Charlie", "Brown"}); + storage.insert(UserProfile{0, "Diana", "Davis"}); + storage.insert(UserProfile{0, "Eve", "Wilson"}); + + int refId = 0; + int resultPerPage = 3; + + // SQL: SELECT id, first_name, last_name, COUNT(id) OVER () + // FROM user_profile WHERE id > ? ORDER BY id LIMIT ? + auto rows = storage.select( + columns(&UserProfile::id, &UserProfile::firstName, &UserProfile::lastName, count(&UserProfile::id).over()), + where(c(&UserProfile::id) > refId), + order_by(&UserProfile::id), + limit(resultPerPage)); + + REQUIRE(rows.size() == 3); + // Every row should have total_count=5 (total rows in table) + REQUIRE(std::get<0>(rows[0]) == 1); + REQUIRE(std::get<1>(rows[0]) == "Alice"); + REQUIRE(std::get<2>(rows[0]) == "Smith"); + REQUIRE(std::get<3>(rows[0]) == 5); + + REQUIRE(std::get<0>(rows[1]) == 2); + REQUIRE(std::get<1>(rows[1]) == "Bob"); + REQUIRE(std::get<3>(rows[1]) == 5); + + REQUIRE(std::get<0>(rows[2]) == 3); + REQUIRE(std::get<1>(rows[2]) == "Charlie"); + REQUIRE(std::get<3>(rows[2]) == 5); +}