diff --git a/include/sqlgen/dynamic/Value.hpp b/include/sqlgen/dynamic/Value.hpp index 586159b..35eb668 100644 --- a/include/sqlgen/dynamic/Value.hpp +++ b/include/sqlgen/dynamic/Value.hpp @@ -25,6 +25,8 @@ struct Integer { int64_t val; }; +struct Null {}; + struct String { std::string val; }; @@ -35,7 +37,7 @@ struct Timestamp { struct Value { using ReflectionType = rfl::TaggedUnion<"type", Duration, Boolean, Float, - Integer, String, Timestamp>; + Integer, Null, String, Timestamp>; const auto& reflection() const { return val; } ReflectionType val; }; diff --git a/include/sqlgen/transpilation/to_sets.hpp b/include/sqlgen/transpilation/to_sets.hpp index d1e6750..e95aa75 100644 --- a/include/sqlgen/transpilation/to_sets.hpp +++ b/include/sqlgen/transpilation/to_sets.hpp @@ -31,10 +31,9 @@ struct ToSet, ToType>> { static_assert( all_columns_exist>(), "At least one column referenced in your SET query does not exist."); - static_assert( - std::is_convertible_v>>, - remove_nullable_t>>>, - "Must be convertible."); + static_assert(std::is_convertible_v>, + underlying_t>>, + "Must be convertible."); dynamic::Update::Set operator()(const auto& _set) const { return dynamic::Update::Set{ diff --git a/include/sqlgen/transpilation/to_value.hpp b/include/sqlgen/transpilation/to_value.hpp index 5ca5c55..a39a217 100644 --- a/include/sqlgen/transpilation/to_value.hpp +++ b/include/sqlgen/transpilation/to_value.hpp @@ -4,9 +4,11 @@ #include #include +#include "../dynamic/Type.hpp" #include "../dynamic/Value.hpp" #include "Value.hpp" #include "has_reflection_method.hpp" +#include "is_nullable.hpp" namespace sqlgen::transpilation { @@ -17,7 +19,17 @@ template struct ToValue { dynamic::Value operator()(const T& _t) const { using Type = std::remove_cvref_t; - if constexpr (std::is_floating_point_v) { + if constexpr (is_nullable_v) { + if (!_t) { + return dynamic::Value{dynamic::Null{}}; + } + return ToValue>{}(*_t); + + } else if constexpr (std::is_same_v || + std::is_same_v) { + return dynamic::Value{dynamic::Null{}}; + + } else if constexpr (std::is_floating_point_v) { return dynamic::Value{dynamic::Float{.val = static_cast(_t)}}; } else if constexpr (std::is_same_v) { diff --git a/src/sqlgen/duckdb/to_sql.cpp b/src/sqlgen/duckdb/to_sql.cpp index 3769f54..034aaed 100644 --- a/src/sqlgen/duckdb/to_sql.cpp +++ b/src/sqlgen/duckdb/to_sql.cpp @@ -145,6 +145,9 @@ std::string column_or_value_to_sql( return "INTERVAL '" + std::to_string(_v.val) + " " + rfl::enum_to_string(_v.unit) + "'"; + } else if constexpr (std::is_same_v) { + return "NULL"; + } else if constexpr (std::is_same_v) { return "to_timestamp(" + std::to_string(_v.seconds_since_unix) + ")"; diff --git a/src/sqlgen/mysql/to_sql.cpp b/src/sqlgen/mysql/to_sql.cpp index 2346f91..4683a78 100644 --- a/src/sqlgen/mysql/to_sql.cpp +++ b/src/sqlgen/mysql/to_sql.cpp @@ -175,6 +175,9 @@ std::string column_or_value_to_sql( if constexpr (std::is_same_v) { return "'" + escape_single_quote(_v.val) + "'"; + } else if constexpr (std::is_same_v) { + return "NULL"; + } else if constexpr (std::is_same_v) { const auto unit = _v.unit == dynamic::TimeUnit::milliseconds diff --git a/src/sqlgen/postgres/to_sql.cpp b/src/sqlgen/postgres/to_sql.cpp index d2a4777..534c650 100644 --- a/src/sqlgen/postgres/to_sql.cpp +++ b/src/sqlgen/postgres/to_sql.cpp @@ -134,6 +134,9 @@ std::string column_or_value_to_sql( return "INTERVAL '" + std::to_string(_v.val) + " " + rfl::enum_to_string(_v.unit) + "'"; + } else if constexpr (std::is_same_v) { + return "NULL"; + } else if constexpr (std::is_same_v) { return "to_timestamp(" + std::to_string(_v.seconds_since_unix) + ")"; diff --git a/src/sqlgen/sqlite/to_sql.cpp b/src/sqlgen/sqlite/to_sql.cpp index c93da21..1a53328 100644 --- a/src/sqlgen/sqlite/to_sql.cpp +++ b/src/sqlgen/sqlite/to_sql.cpp @@ -136,6 +136,9 @@ std::string column_or_value_to_sql( rfl::enum_to_string(_v.unit) + "'"; } + } else if constexpr (std::is_same_v) { + return "NULL"; + } else if constexpr (std::is_same_v) { return std::to_string(_v.seconds_since_unix); diff --git a/tests/sqlite/test_update_with_optional.cpp b/tests/sqlite/test_update_with_optional.cpp index 66057b8..ce3a40b 100644 --- a/tests/sqlite/test_update_with_optional.cpp +++ b/tests/sqlite/test_update_with_optional.cpp @@ -33,16 +33,22 @@ TEST(sqlite, test_update_with_optional) { using namespace sqlgen; using namespace sqlgen::literals; - const auto query = - update("first_name"_c.set("last_name"_c), "age"_c.set(100)) | - where("first_name"_c == "Hugo"); + const auto query1 = update("first_name"_c.set("last_name"_c), + "age"_c.set(std::nullopt)) | + where("first_name"_c == "Hugo"); - query(conn).value(); + const auto query2 = + update("age"_c.set(50)) | where("first_name"_c == "Homer"); + + const auto query3 = update("age"_c.set(std::optional(11))) | + where("first_name"_c == "Bart"); + + query1(conn).and_then(query2).and_then(query3).value(); const auto people2 = sqlgen::read>(conn).value(); const std::string expected = - R"([{"id":0,"first_name":"Homer","last_name":"Simpson","age":45},{"id":1,"first_name":"Bart","last_name":"Simpson","age":10},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":4,"first_name":"Simpson","last_name":"Simpson","age":100}])"; + R"([{"id":0,"first_name":"Homer","last_name":"Simpson","age":50},{"id":1,"first_name":"Bart","last_name":"Simpson","age":11},{"id":2,"first_name":"Lisa","last_name":"Simpson","age":8},{"id":3,"first_name":"Maggie","last_name":"Simpson","age":0},{"id":4,"first_name":"Simpson","last_name":"Simpson"}])"; EXPECT_EQ(rfl::json::write(people2), expected); }