From 608c8c4adb6b6fe4aa01537ab3e4757b294d7ef1 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sat, 27 Sep 2025 10:04:28 +0200 Subject: [PATCH 1/7] Added the IN and NOT IN conditions in the dynamic module --- include/sqlgen/dynamic/Condition.hpp | 16 +++++++++++++--- src/sqlgen/mysql/to_sql.cpp | 18 ++++++++++++++++++ src/sqlgen/postgres/to_sql.cpp | 18 ++++++++++++++++++ src/sqlgen/sqlite/to_sql.cpp | 18 ++++++++++++++++++ 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/include/sqlgen/dynamic/Condition.hpp b/include/sqlgen/dynamic/Condition.hpp index 75ac4d07..999d7955 100644 --- a/include/sqlgen/dynamic/Condition.hpp +++ b/include/sqlgen/dynamic/Condition.hpp @@ -31,6 +31,11 @@ struct Condition { Operation op2; }; + struct In { + Operation op; + std::vector patterns; + }; + struct IsNotNull { Operation op; }; @@ -68,15 +73,20 @@ struct Condition { dynamic::Value pattern; }; + struct NotIn { + Operation op; + std::vector patterns; + }; + struct Or { Ref cond1; Ref cond2; }; using ReflectionType = - rfl::TaggedUnion<"what", And, Equal, GreaterEqual, GreaterThan, IsNull, - IsNotNull, LesserEqual, LesserThan, Like, Not, NotEqual, - NotLike, Or>; + rfl::TaggedUnion<"what", And, Equal, GreaterEqual, GreaterThan, In, + IsNull, IsNotNull, LesserEqual, LesserThan, Like, Not, + NotEqual, NotIn, NotLike, Or>; const ReflectionType& reflection() const { return val; } diff --git a/src/sqlgen/mysql/to_sql.cpp b/src/sqlgen/mysql/to_sql.cpp index 6a390e66..f10c9417 100644 --- a/src/sqlgen/mysql/to_sql.cpp +++ b/src/sqlgen/mysql/to_sql.cpp @@ -209,6 +209,8 @@ std::string condition_to_sql(const dynamic::Condition& _cond) noexcept { template std::string condition_to_sql_impl(const ConditionType& _condition) noexcept { + using namespace std::ranges::views; + using C = std::remove_cvref_t; std::stringstream stream; @@ -229,6 +231,14 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept { stream << operation_to_sql(_condition.op1) << " > " << operation_to_sql(_condition.op2); + } else if constexpr (std::is_same_v) { + stream << operation_to_sql(_condition.op) << " IN (" + << internal::strings::join( + ", ", + internal::collect::vector(_condition.patterns | + transform(column_or_value_to_sql))) + << ")"; + } else if constexpr (std::is_same_v) { stream << operation_to_sql(_condition.op) << " IS NULL"; @@ -262,6 +272,14 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept { stream << "(" << condition_to_sql(*_condition.cond1) << ") OR (" << condition_to_sql(*_condition.cond2) << ")"; + } else if constexpr (std::is_same_v) { + stream << operation_to_sql(_condition.op) << " NOT IN (" + << internal::strings::join( + ", ", + internal::collect::vector(_condition.patterns | + transform(column_or_value_to_sql))) + << ")"; + } else { static_assert(rfl::always_false_v, "Not all cases were covered."); } diff --git a/src/sqlgen/postgres/to_sql.cpp b/src/sqlgen/postgres/to_sql.cpp index 5595d933..c479ead9 100644 --- a/src/sqlgen/postgres/to_sql.cpp +++ b/src/sqlgen/postgres/to_sql.cpp @@ -161,6 +161,8 @@ std::string condition_to_sql(const dynamic::Condition& _cond) noexcept { template std::string condition_to_sql_impl(const ConditionType& _condition) noexcept { + using namespace std::ranges::views; + using C = std::remove_cvref_t; std::stringstream stream; @@ -181,6 +183,14 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept { stream << operation_to_sql(_condition.op1) << " > " << operation_to_sql(_condition.op2); + } else if constexpr (std::is_same_v) { + stream << operation_to_sql(_condition.op) << " IN (" + << internal::strings::join( + ", ", + internal::collect::vector(_condition.patterns | + transform(column_or_value_to_sql))) + << ")"; + } else if constexpr (std::is_same_v) { stream << operation_to_sql(_condition.op) << " IS NULL"; @@ -210,6 +220,14 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept { stream << operation_to_sql(_condition.op) << " NOT LIKE " << column_or_value_to_sql(_condition.pattern); + } else if constexpr (std::is_same_v) { + stream << operation_to_sql(_condition.op) << " NOT IN (" + << internal::strings::join( + ", ", + internal::collect::vector(_condition.patterns | + transform(column_or_value_to_sql))) + << ")"; + } else if constexpr (std::is_same_v) { stream << "(" << condition_to_sql(*_condition.cond1) << ") OR (" << condition_to_sql(*_condition.cond2) << ")"; diff --git a/src/sqlgen/sqlite/to_sql.cpp b/src/sqlgen/sqlite/to_sql.cpp index 4802233f..371e5ba8 100644 --- a/src/sqlgen/sqlite/to_sql.cpp +++ b/src/sqlgen/sqlite/to_sql.cpp @@ -169,6 +169,8 @@ std::string condition_to_sql(const dynamic::Condition& _cond) noexcept { template std::string condition_to_sql_impl(const ConditionType& _condition) noexcept { + using namespace std::ranges::views; + using C = std::remove_cvref_t; std::stringstream stream; @@ -189,6 +191,14 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept { stream << operation_to_sql(_condition.op1) << " > " << operation_to_sql(_condition.op2); + } else if constexpr (std::is_same_v) { + stream << operation_to_sql(_condition.op) << " IN (" + << internal::strings::join( + ", ", + internal::collect::vector(_condition.patterns | + transform(column_or_value_to_sql))) + << ")"; + } else if constexpr (std::is_same_v) { stream << operation_to_sql(_condition.op) << " IS NULL"; @@ -218,6 +228,14 @@ std::string condition_to_sql_impl(const ConditionType& _condition) noexcept { stream << operation_to_sql(_condition.op) << " NOT LIKE " << column_or_value_to_sql(_condition.pattern); + } else if constexpr (std::is_same_v) { + stream << operation_to_sql(_condition.op) << " NOT IN (" + << internal::strings::join( + ", ", + internal::collect::vector(_condition.patterns | + transform(column_or_value_to_sql))) + << ")"; + } else if constexpr (std::is_same_v) { stream << "(" << condition_to_sql(*_condition.cond1) << ") OR (" << condition_to_sql(*_condition.cond2) << ")"; From d7835c89542bcb06050d3da02e041f89605b3b62 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sat, 27 Sep 2025 11:19:28 +0200 Subject: [PATCH 2/7] Backup --- include/sqlgen/col.hpp | 13 +++++ include/sqlgen/transpilation/conditions.hpp | 28 +++++++++++ include/sqlgen/transpilation/to_condition.hpp | 39 +++++++++++++++ tests/sqlite/test_in.cpp | 50 +++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 tests/sqlite/test_in.cpp diff --git a/include/sqlgen/col.hpp b/include/sqlgen/col.hpp index b3443890..7ad630d6 100644 --- a/include/sqlgen/col.hpp +++ b/include/sqlgen/col.hpp @@ -40,6 +40,13 @@ struct Col { /// Returns the column name. std::string name() const noexcept { return Name().str(); } + /// Returns an IN condition. + template + auto in(const Ts&... _ts) const noexcept { + return transpilation::make_condition(transpilation::conditions::in( + transpilation::Col<_name, _alias>{}, _ts...)); + } + /// Returns an IS NULL condition. auto is_null() const noexcept { return transpilation::make_condition(transpilation::conditions::is_null( @@ -64,6 +71,12 @@ struct Col { transpilation::Col<_name, _alias>{}, _pattern)); } + /// Returns a NOT IN condition. + template + auto not_in(const Ts&... _ts) const noexcept { + return not_in(_ts...); + } + /// Returns a SET clause in an UPDATE statement. template auto set(const T& _to) const noexcept { diff --git a/include/sqlgen/transpilation/conditions.hpp b/include/sqlgen/transpilation/conditions.hpp index f9eb4383..e5efae0d 100644 --- a/include/sqlgen/transpilation/conditions.hpp +++ b/include/sqlgen/transpilation/conditions.hpp @@ -1,6 +1,8 @@ #ifndef SQLGEN_TRANSPILATION_CONDITIONS_HPP_ #define SQLGEN_TRANSPILATION_CONDITIONS_HPP_ +#include + namespace sqlgen::transpilation::conditions { template @@ -53,6 +55,19 @@ auto greater_than(const OpType1& _op1, const OpType2& _op2) { std::remove_cvref_t>{.op1 = _op1, .op2 = _op2}; } +template +struct In { + using ResultType = bool; + + OpType op; + rfl::Tuple patterns; +}; + +template +auto in(const OpType& _op, const Ts&... _ts) { + return In{.op = _op, .patterns = rfl::Tuple(_ts...)}; +} + template struct IsNull { using ResultType = bool; @@ -139,6 +154,19 @@ auto not_equal(const OpType1& _op1, const OpType2& _op2) { .op1 = _op1, .op2 = _op2}; } +template +struct NotIn { + using ResultType = bool; + + OpType op; + rfl::Tuple patterns; +}; + +template +auto not_in(const OpType& _op, const Ts&... _ts) { + return NotIn{.op = _op, .patterns = rfl::Tuple(_ts...)}; +} + template struct NotLike { using ResultType = bool; diff --git a/include/sqlgen/transpilation/to_condition.hpp b/include/sqlgen/transpilation/to_condition.hpp index 3b6ed7a5..c97eaf16 100644 --- a/include/sqlgen/transpilation/to_condition.hpp +++ b/include/sqlgen/transpilation/to_condition.hpp @@ -3,17 +3,20 @@ #include #include +#include #include #include #include "../Ref.hpp" #include "../Result.hpp" #include "../dynamic/Condition.hpp" +#include "../internal/collect/vector.hpp" #include "Condition.hpp" #include "all_columns_exist.hpp" #include "conditions.hpp" #include "is_timestamp.hpp" #include "make_field.hpp" +#include "remove_nullable_t.hpp" #include "to_transpilation_type.hpp" #include "underlying_t.hpp" @@ -141,6 +144,24 @@ struct ToCondition> { } }; +template +struct ToCondition> { + using Underlying1 = remove_nullable_t>; + using Underlying2 = remove_nullable_t>; + + static_assert(std::equality_comparable_with, + "Must be equality comparable."); + + dynamic::Condition operator()(const auto& _cond) const { + return dynamic::Condition{ + .val = dynamic::Condition::In{ + .op = make_field(_cond.op).val, + .patterns = rfl::apply(_cond.patterns, [](const auto&... _p) { + return std::vector({to_value(_p)...}); + })}}; + } +}; + template struct ToCondition> { dynamic::Condition operator()(const auto& _cond) const { @@ -198,6 +219,24 @@ struct ToCondition> { } }; +template +struct ToCondition> { + using Underlying1 = remove_nullable_t>; + using Underlying2 = remove_nullable_t>; + + static_assert(std::equality_comparable_with, + "Must be equality comparable."); + + dynamic::Condition operator()(const auto& _cond) const { + return dynamic::Condition{ + .val = dynamic::Condition::NotIn{ + .op = make_field(_cond.op).val, + .patterns = rfl::apply(_cond.patterns, [](const auto&... _p) { + return std::vector({to_value(_p)...}); + })}}; + } +}; + template struct ToCondition> { dynamic::Condition operator()(const auto& _cond) const { diff --git a/tests/sqlite/test_in.cpp b/tests/sqlite/test_in.cpp new file mode 100644 index 00000000..f1a39b47 --- /dev/null +++ b/tests/sqlite/test_in.cpp @@ -0,0 +1,50 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_in { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(sqlite, test_in) { + 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}}); + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto conn = sqlite::connect(); + + const auto people2 = + conn.and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.in("Bart", "Lisa", "Maggie")) | + order_by("age"_c)) + .value(); + + std::cout << rfl::json::write(people2) << std::endl; + + const std::string expected1 = R"([])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_in + From f7bd7f99681253f6d797a444546158bf0390200a Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sat, 27 Sep 2025 22:34:38 +0200 Subject: [PATCH 3/7] Made it work and added tests for sqlite --- include/sqlgen/col.hpp | 3 +- include/sqlgen/transpilation/conditions.hpp | 8 ++- include/sqlgen/transpilation/string_t.hpp | 28 ++++++++++ include/sqlgen/transpilation/to_condition.hpp | 52 +++++++++++-------- tests/sqlite/test_in.cpp | 10 ++-- tests/sqlite/test_not_in.cpp | 48 +++++++++++++++++ 6 files changed, 117 insertions(+), 32 deletions(-) create mode 100644 include/sqlgen/transpilation/string_t.hpp create mode 100644 tests/sqlite/test_not_in.cpp diff --git a/include/sqlgen/col.hpp b/include/sqlgen/col.hpp index 7ad630d6..97b9075c 100644 --- a/include/sqlgen/col.hpp +++ b/include/sqlgen/col.hpp @@ -74,7 +74,8 @@ struct Col { /// Returns a NOT IN condition. template auto not_in(const Ts&... _ts) const noexcept { - return not_in(_ts...); + return transpilation::make_condition(transpilation::conditions::not_in( + transpilation::Col<_name, _alias>{}, _ts...)); } /// Returns a SET clause in an UPDATE statement. diff --git a/include/sqlgen/transpilation/conditions.hpp b/include/sqlgen/transpilation/conditions.hpp index e5efae0d..c32dfc54 100644 --- a/include/sqlgen/transpilation/conditions.hpp +++ b/include/sqlgen/transpilation/conditions.hpp @@ -3,6 +3,8 @@ #include +#include "string_t.hpp" + namespace sqlgen::transpilation::conditions { template @@ -65,7 +67,8 @@ struct In { template auto in(const OpType& _op, const Ts&... _ts) { - return In{.op = _op, .patterns = rfl::Tuple(_ts...)}; + return In...>{ + .op = _op, .patterns = rfl::Tuple...>(_ts...)}; } template @@ -164,7 +167,8 @@ struct NotIn { template auto not_in(const OpType& _op, const Ts&... _ts) { - return NotIn{.op = _op, .patterns = rfl::Tuple(_ts...)}; + return NotIn...>{ + .op = _op, .patterns = rfl::Tuple...>(_ts...)}; } template diff --git a/include/sqlgen/transpilation/string_t.hpp b/include/sqlgen/transpilation/string_t.hpp new file mode 100644 index 00000000..c137d8a3 --- /dev/null +++ b/include/sqlgen/transpilation/string_t.hpp @@ -0,0 +1,28 @@ +#ifndef SQLGEN_TRANSPILATION_STRINGT_HPP_ +#define SQLGEN_TRANSPILATION_STRINGT_HPP_ + +#include +#include + +namespace sqlgen::transpilation { + +template +struct StringType; + +template +struct StringType { + using Type = T; +}; + +template + requires std::is_convertible_v +struct StringType { + using Type = std::string; +}; + +template +using string_t = typename StringType>::Type; + +} // namespace sqlgen::transpilation + +#endif diff --git a/include/sqlgen/transpilation/to_condition.hpp b/include/sqlgen/transpilation/to_condition.hpp index c97eaf16..0de4b59e 100644 --- a/include/sqlgen/transpilation/to_condition.hpp +++ b/include/sqlgen/transpilation/to_condition.hpp @@ -144,21 +144,24 @@ struct ToCondition> { } }; -template -struct ToCondition> { - using Underlying1 = remove_nullable_t>; - using Underlying2 = remove_nullable_t>; +template +struct ToCondition> { + using UnderlyingT = remove_nullable_t>; - static_assert(std::equality_comparable_with, - "Must be equality comparable."); + static constexpr bool is_equality_comparable = + (true && ... && std::equality_comparable_with); + + static_assert(is_equality_comparable, "Must be equality comparable."); dynamic::Condition operator()(const auto& _cond) const { return dynamic::Condition{ - .val = dynamic::Condition::In{ - .op = make_field(_cond.op).val, - .patterns = rfl::apply(_cond.patterns, [](const auto&... _p) { - return std::vector({to_value(_p)...}); - })}}; + .val = dynamic::Condition::In{.op = make_field(_cond.op).val, + .patterns = rfl::apply( + [](const auto&... _p) { + return std::vector( + {to_value(_p)...}); + }, + _cond.patterns)}}; } }; @@ -219,21 +222,24 @@ struct ToCondition> { } }; -template -struct ToCondition> { - using Underlying1 = remove_nullable_t>; - using Underlying2 = remove_nullable_t>; +template +struct ToCondition> { + using UnderlyingT = remove_nullable_t>; - static_assert(std::equality_comparable_with, - "Must be equality comparable."); + static constexpr bool is_equality_comparable = + (true && ... && std::equality_comparable_with); + + static_assert(is_equality_comparable, "Must be equality comparable."); dynamic::Condition operator()(const auto& _cond) const { - return dynamic::Condition{ - .val = dynamic::Condition::NotIn{ - .op = make_field(_cond.op).val, - .patterns = rfl::apply(_cond.patterns, [](const auto&... _p) { - return std::vector({to_value(_p)...}); - })}}; + return dynamic::Condition{.val = dynamic::Condition::NotIn{ + .op = make_field(_cond.op).val, + .patterns = rfl::apply( + [](const auto&... _p) { + return std::vector( + {to_value(_p)...}); + }, + _cond.patterns)}}; } }; diff --git a/tests/sqlite/test_in.cpp b/tests/sqlite/test_in.cpp index f1a39b47..8e1b9ed1 100644 --- a/tests/sqlite/test_in.cpp +++ b/tests/sqlite/test_in.cpp @@ -30,18 +30,16 @@ TEST(sqlite, test_in) { using namespace sqlgen; using namespace sqlgen::literals; - const auto conn = sqlite::connect(); - const auto people2 = - conn.and_then(write(std::ref(people1))) + sqlite::connect() + .and_then(write(std::ref(people1))) .and_then(sqlgen::read> | where("first_name"_c.in("Bart", "Lisa", "Maggie")) | order_by("age"_c)) .value(); - std::cout << rfl::json::write(people2) << std::endl; - - const std::string expected1 = R"([])"; + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; EXPECT_EQ(rfl::json::write(people2), expected1); } diff --git a/tests/sqlite/test_not_in.cpp b/tests/sqlite/test_not_in.cpp new file mode 100644 index 00000000..ed5a2cb2 --- /dev/null +++ b/tests/sqlite/test_not_in.cpp @@ -0,0 +1,48 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_not_in { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(sqlite, test_not_in) { + 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}}); + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto people2 = + sqlite::connect() + .and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.not_in("Homer", "Hugo")) | + order_by("age"_c)) + .value(); + + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_not_in + From b838ebec2b6212de5b08c46ec50da3b988a9a460 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 28 Sep 2025 19:07:48 +0200 Subject: [PATCH 4/7] Added support for vectors --- include/sqlgen/transpilation/conditions.hpp | 26 ++++++++++ include/sqlgen/transpilation/to_condition.hpp | 40 +++++++++++++++ tests/sqlite/test_in_vec.cpp | 49 +++++++++++++++++++ tests/sqlite/test_not_in_vec.cpp | 49 +++++++++++++++++++ 4 files changed, 164 insertions(+) create mode 100644 tests/sqlite/test_in_vec.cpp create mode 100644 tests/sqlite/test_not_in_vec.cpp diff --git a/include/sqlgen/transpilation/conditions.hpp b/include/sqlgen/transpilation/conditions.hpp index c32dfc54..6fdb95af 100644 --- a/include/sqlgen/transpilation/conditions.hpp +++ b/include/sqlgen/transpilation/conditions.hpp @@ -71,6 +71,19 @@ auto in(const OpType& _op, const Ts&... _ts) { .op = _op, .patterns = rfl::Tuple...>(_ts...)}; } +template +struct InVec { + using ResultType = bool; + + OpType op; + std::vector patterns; +}; + +template +auto in(const OpType& _op, const std::vector& _patterns) { + return InVec{.op = _op, .patterns = _patterns}; +} + template struct IsNull { using ResultType = bool; @@ -171,6 +184,19 @@ auto not_in(const OpType& _op, const Ts&... _ts) { .op = _op, .patterns = rfl::Tuple...>(_ts...)}; } +template +struct NotInVec { + using ResultType = bool; + + OpType op; + std::vector patterns; +}; + +template +auto not_in(const OpType& _op, const std::vector& _patterns) { + return NotInVec{.op = _op, .patterns = _patterns}; +} + template struct NotLike { using ResultType = bool; diff --git a/include/sqlgen/transpilation/to_condition.hpp b/include/sqlgen/transpilation/to_condition.hpp index 0de4b59e..e5fb089e 100644 --- a/include/sqlgen/transpilation/to_condition.hpp +++ b/include/sqlgen/transpilation/to_condition.hpp @@ -165,6 +165,26 @@ struct ToCondition> { } }; +template +struct ToCondition> { + using UnderlyingT = remove_nullable_t>; + + static constexpr bool is_equality_comparable = + std::equality_comparable_with; + + static_assert(is_equality_comparable, "Must be equality comparable."); + + dynamic::Condition operator()(const auto& _cond) const { + using namespace std::ranges::views; + return dynamic::Condition{ + .val = dynamic::Condition::In{ + .op = make_field(_cond.op).val, + .patterns = sqlgen::internal::collect::vector( + _cond.patterns | + transform([](const auto& _v) { return to_value(_v); }))}}; + } +}; + template struct ToCondition> { dynamic::Condition operator()(const auto& _cond) const { @@ -243,6 +263,26 @@ struct ToCondition> { } }; +template +struct ToCondition> { + using UnderlyingT = remove_nullable_t>; + + static constexpr bool is_equality_comparable = + std::equality_comparable_with; + + static_assert(is_equality_comparable, "Must be equality comparable."); + + dynamic::Condition operator()(const auto& _cond) const { + using namespace std::ranges::views; + return dynamic::Condition{ + .val = dynamic::Condition::NotIn{ + .op = make_field(_cond.op).val, + .patterns = sqlgen::internal::collect::vector( + _cond.patterns | + transform([](const auto& _v) { return to_value(_v); }))}}; + } +}; + template struct ToCondition> { dynamic::Condition operator()(const auto& _cond) const { diff --git a/tests/sqlite/test_in_vec.cpp b/tests/sqlite/test_in_vec.cpp new file mode 100644 index 00000000..4bfa9af9 --- /dev/null +++ b/tests/sqlite/test_in_vec.cpp @@ -0,0 +1,49 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_in_vec { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(sqlite, test_in_vec) { + 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}}); + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto people2 = + sqlite::connect() + .and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.in( + std::vector({"Bart", "Lisa", "Maggie"}))) | + order_by("age"_c)) + .value(); + + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_in_vec + diff --git a/tests/sqlite/test_not_in_vec.cpp b/tests/sqlite/test_not_in_vec.cpp new file mode 100644 index 00000000..05ee3ab4 --- /dev/null +++ b/tests/sqlite/test_not_in_vec.cpp @@ -0,0 +1,49 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_not_in_vec { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(sqlite, test_not_in_vec) { + 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}}); + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto people2 = + sqlite::connect() + .and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.not_in( + std::vector({"Homer", "Hugo"}))) | + order_by("age"_c)) + .value(); + + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_not_in_vec + From 9b9367d88bddd17f251beb991a2fd21c37aeef0d Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 28 Sep 2025 19:29:28 +0200 Subject: [PATCH 5/7] Added tests for postgres and mysql --- tests/mysql/test_in.cpp | 54 +++++++++++++++++++++++++++++ tests/mysql/test_in_vec.cpp | 55 ++++++++++++++++++++++++++++++ tests/mysql/test_not_in.cpp | 54 +++++++++++++++++++++++++++++ tests/mysql/test_not_in_vec.cpp | 55 ++++++++++++++++++++++++++++++ tests/postgres/test_in.cpp | 54 +++++++++++++++++++++++++++++ tests/postgres/test_in_vec.cpp | 55 ++++++++++++++++++++++++++++++ tests/postgres/test_not_in.cpp | 54 +++++++++++++++++++++++++++++ tests/postgres/test_not_in_vec.cpp | 55 ++++++++++++++++++++++++++++++ 8 files changed, 436 insertions(+) create mode 100644 tests/mysql/test_in.cpp create mode 100644 tests/mysql/test_in_vec.cpp create mode 100644 tests/mysql/test_not_in.cpp create mode 100644 tests/mysql/test_not_in_vec.cpp create mode 100644 tests/postgres/test_in.cpp create mode 100644 tests/postgres/test_in_vec.cpp create mode 100644 tests/postgres/test_not_in.cpp create mode 100644 tests/postgres/test_not_in_vec.cpp diff --git a/tests/mysql/test_in.cpp b/tests/mysql/test_in.cpp new file mode 100644 index 00000000..05f4ed95 --- /dev/null +++ b/tests/mysql/test_in.cpp @@ -0,0 +1,54 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_in { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(mysql, test_in) { + 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 credentials = sqlgen::mysql::Credentials{.host = "localhost", + .user = "sqlgen", + .password = "password", + .dbname = "mysql"}; + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto people2 = + mysql::connect(credentials) + .and_then(drop | if_exists) + .and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.in("Bart", "Lisa", "Maggie")) | + order_by("age"_c)) + .value(); + + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_in + diff --git a/tests/mysql/test_in_vec.cpp b/tests/mysql/test_in_vec.cpp new file mode 100644 index 00000000..b1e5d7cd --- /dev/null +++ b/tests/mysql/test_in_vec.cpp @@ -0,0 +1,55 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_in_vec { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(mysql, test_in_vec) { + 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 credentials = sqlgen::mysql::Credentials{.host = "localhost", + .user = "sqlgen", + .password = "password", + .dbname = "mysql"}; + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto people2 = + mysql::connect(credentials) + .and_then(drop | if_exists) + .and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.in( + std::vector({"Bart", "Lisa", "Maggie"}))) | + order_by("age"_c)) + .value(); + + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_in_vec + diff --git a/tests/mysql/test_not_in.cpp b/tests/mysql/test_not_in.cpp new file mode 100644 index 00000000..e9824b39 --- /dev/null +++ b/tests/mysql/test_not_in.cpp @@ -0,0 +1,54 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_not_in { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(mysql, test_not_in) { + 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 credentials = sqlgen::mysql::Credentials{.host = "localhost", + .user = "sqlgen", + .password = "password", + .dbname = "mysql"}; + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto people2 = + mysql::connect(credentials) + .and_then(drop | if_exists) + .and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.not_in("Homer", "Hugo")) | + order_by("age"_c)) + .value(); + + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_not_in + diff --git a/tests/mysql/test_not_in_vec.cpp b/tests/mysql/test_not_in_vec.cpp new file mode 100644 index 00000000..69e25875 --- /dev/null +++ b/tests/mysql/test_not_in_vec.cpp @@ -0,0 +1,55 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_not_in_vec { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(mysql, test_not_in_vec) { + 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 credentials = sqlgen::mysql::Credentials{.host = "localhost", + .user = "sqlgen", + .password = "password", + .dbname = "mysql"}; + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto people2 = + mysql::connect(credentials) + .and_then(drop | if_exists) + .and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.not_in( + std::vector({"Homer", "Hugo"}))) | + order_by("age"_c)) + .value(); + + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_not_in_vec + diff --git a/tests/postgres/test_in.cpp b/tests/postgres/test_in.cpp new file mode 100644 index 00000000..118bb833 --- /dev/null +++ b/tests/postgres/test_in.cpp @@ -0,0 +1,54 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_in { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(postgres, test_in) { + 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 credentials = sqlgen::postgres::Credentials{.user = "postgres", + .password = "password", + .host = "localhost", + .dbname = "postgres"}; + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto people2 = + postgres::connect(credentials) + .and_then(drop | if_exists) + .and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.in("Bart", "Lisa", "Maggie")) | + order_by("age"_c)) + .value(); + + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_in + diff --git a/tests/postgres/test_in_vec.cpp b/tests/postgres/test_in_vec.cpp new file mode 100644 index 00000000..388efe2f --- /dev/null +++ b/tests/postgres/test_in_vec.cpp @@ -0,0 +1,55 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_in_vec { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(postgres, test_in_vec) { + 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 credentials = sqlgen::postgres::Credentials{.user = "postgres", + .password = "password", + .host = "localhost", + .dbname = "postgres"}; + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto people2 = + postgres::connect(credentials) + .and_then(drop | if_exists) + .and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.in( + std::vector({"Bart", "Lisa", "Maggie"}))) | + order_by("age"_c)) + .value(); + + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_in_vec + diff --git a/tests/postgres/test_not_in.cpp b/tests/postgres/test_not_in.cpp new file mode 100644 index 00000000..5c2dc5e2 --- /dev/null +++ b/tests/postgres/test_not_in.cpp @@ -0,0 +1,54 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_not_in { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(postgres, test_not_in) { + 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 credentials = sqlgen::postgres::Credentials{.user = "postgres", + .password = "password", + .host = "localhost", + .dbname = "postgres"}; + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto people2 = + postgres::connect(credentials) + .and_then(drop | if_exists) + .and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.not_in("Homer", "Hugo")) | + order_by("age"_c)) + .value(); + + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_not_in + diff --git a/tests/postgres/test_not_in_vec.cpp b/tests/postgres/test_not_in_vec.cpp new file mode 100644 index 00000000..341f1ee0 --- /dev/null +++ b/tests/postgres/test_not_in_vec.cpp @@ -0,0 +1,55 @@ + +#include + +#include +#include +#include +#include +#include + +namespace test_not_in_vec { + +struct Person { + sqlgen::PrimaryKey id; + std::string first_name; + std::string last_name; + int age; +}; + +TEST(postgres, test_not_in_vec) { + 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 credentials = sqlgen::postgres::Credentials{.user = "postgres", + .password = "password", + .host = "localhost", + .dbname = "postgres"}; + + using namespace sqlgen; + using namespace sqlgen::literals; + + const auto people2 = + postgres::connect(credentials) + .and_then(drop | if_exists) + .and_then(write(std::ref(people1))) + .and_then(sqlgen::read> | + where("first_name"_c.not_in( + std::vector({"Homer", "Hugo"}))) | + order_by("age"_c)) + .value(); + + const std::string expected1 = + R"([{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10}])"; + + EXPECT_EQ(rfl::json::write(people2), expected1); +} + +} // namespace test_not_in_vec + From 5cb50de02e6d93bab7323529dd3b20f76d019103 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 28 Sep 2025 19:34:24 +0200 Subject: [PATCH 6/7] Added documentation --- docs/col.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/docs/col.md b/docs/col.md index b21cacb3..7c40b875 100644 --- a/docs/col.md +++ b/docs/col.md @@ -113,6 +113,57 @@ FROM "Person" WHERE "first_name" NOT LIKE 'H%'; ``` +#### IN and NOT IN Operations + +Use IN and NOT IN to check if a column value matches any value in a list: + +```cpp +using namespace sqlgen; +using namespace sqlgen::literals; + +// Find people with specific first names (variadic arguments) +const auto query1 = read> | + where("first_name"_c.in("Bart", "Lisa", "Maggie")); + +// Find people NOT with specific first names (variadic arguments) +const auto query2 = read> | + where("first_name"_c.not_in("Homer", "Hugo")); + +// Find people with specific first names (using vector) +const auto names = std::vector({"Bart", "Lisa", "Maggie"}); +const auto query3 = read> | + where("first_name"_c.in(names)); + +// Find people NOT with specific first names (using vector) +const auto excluded_names = std::vector({"Homer", "Hugo"}); +const auto query4 = read> | + where("first_name"_c.not_in(excluded_names)); +``` + +This generates SQL like: + +```sql +-- For query1 +SELECT "id", "first_name", "last_name", "age" +FROM "Person" +WHERE "first_name" IN ('Bart', 'Lisa', 'Maggie'); + +-- For query2 +SELECT "id", "first_name", "last_name", "age" +FROM "Person" +WHERE "first_name" NOT IN ('Homer', 'Hugo'); + +-- For query3 +SELECT "id", "first_name", "last_name", "age" +FROM "Person" +WHERE "first_name" IN ('Bart', 'Lisa', 'Maggie'); + +-- For query4 +SELECT "id", "first_name", "last_name", "age" +FROM "Person" +WHERE "first_name" NOT IN ('Homer', 'Hugo'); +``` + #### Ordering Specify column ordering in queries: From e84d18e27bf61a4dd1810a425c8859e3c22ed809 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 28 Sep 2025 20:11:56 +0200 Subject: [PATCH 7/7] Added in compile guards --- tests/mysql/test_in.cpp | 2 ++ tests/mysql/test_in_vec.cpp | 2 ++ tests/mysql/test_not_in.cpp | 2 ++ tests/mysql/test_not_in_vec.cpp | 2 ++ tests/postgres/test_in.cpp | 2 ++ tests/postgres/test_in_vec.cpp | 2 ++ tests/postgres/test_not_in.cpp | 2 ++ tests/postgres/test_not_in_vec.cpp | 2 ++ 8 files changed, 16 insertions(+) diff --git a/tests/mysql/test_in.cpp b/tests/mysql/test_in.cpp index 05f4ed95..a9a73e0e 100644 --- a/tests/mysql/test_in.cpp +++ b/tests/mysql/test_in.cpp @@ -1,3 +1,4 @@ +#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY #include @@ -52,3 +53,4 @@ TEST(mysql, test_in) { } // namespace test_in +#endif diff --git a/tests/mysql/test_in_vec.cpp b/tests/mysql/test_in_vec.cpp index b1e5d7cd..5be0906a 100644 --- a/tests/mysql/test_in_vec.cpp +++ b/tests/mysql/test_in_vec.cpp @@ -1,3 +1,4 @@ +#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY #include @@ -53,3 +54,4 @@ TEST(mysql, test_in_vec) { } // namespace test_in_vec +#endif diff --git a/tests/mysql/test_not_in.cpp b/tests/mysql/test_not_in.cpp index e9824b39..af0d47f4 100644 --- a/tests/mysql/test_not_in.cpp +++ b/tests/mysql/test_not_in.cpp @@ -1,3 +1,4 @@ +#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY #include @@ -52,3 +53,4 @@ TEST(mysql, test_not_in) { } // namespace test_not_in +#endif diff --git a/tests/mysql/test_not_in_vec.cpp b/tests/mysql/test_not_in_vec.cpp index 69e25875..3328a256 100644 --- a/tests/mysql/test_not_in_vec.cpp +++ b/tests/mysql/test_not_in_vec.cpp @@ -1,3 +1,4 @@ +#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY #include @@ -53,3 +54,4 @@ TEST(mysql, test_not_in_vec) { } // namespace test_not_in_vec +#endif diff --git a/tests/postgres/test_in.cpp b/tests/postgres/test_in.cpp index 118bb833..b48a0cd0 100644 --- a/tests/postgres/test_in.cpp +++ b/tests/postgres/test_in.cpp @@ -1,3 +1,4 @@ +#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY #include @@ -52,3 +53,4 @@ TEST(postgres, test_in) { } // namespace test_in +#endif diff --git a/tests/postgres/test_in_vec.cpp b/tests/postgres/test_in_vec.cpp index 388efe2f..d579eada 100644 --- a/tests/postgres/test_in_vec.cpp +++ b/tests/postgres/test_in_vec.cpp @@ -1,3 +1,4 @@ +#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY #include @@ -53,3 +54,4 @@ TEST(postgres, test_in_vec) { } // namespace test_in_vec +#endif diff --git a/tests/postgres/test_not_in.cpp b/tests/postgres/test_not_in.cpp index 5c2dc5e2..12ee8832 100644 --- a/tests/postgres/test_not_in.cpp +++ b/tests/postgres/test_not_in.cpp @@ -1,3 +1,4 @@ +#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY #include @@ -52,3 +53,4 @@ TEST(postgres, test_not_in) { } // namespace test_not_in +#endif diff --git a/tests/postgres/test_not_in_vec.cpp b/tests/postgres/test_not_in_vec.cpp index 341f1ee0..2b52a569 100644 --- a/tests/postgres/test_not_in_vec.cpp +++ b/tests/postgres/test_not_in_vec.cpp @@ -1,3 +1,4 @@ +#ifndef SQLGEN_BUILD_DRY_TESTS_ONLY #include @@ -53,3 +54,4 @@ TEST(postgres, test_not_in_vec) { } // namespace test_not_in_vec +#endif