Skip to content

Commit

Permalink
added explicit null and not null
Browse files Browse the repository at this point in the history
  • Loading branch information
fnc12 committed Oct 11, 2023
1 parent c1466d4 commit d840654
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 27 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ auto storage = make_storage("db.sqlite",
make_column("name", &UserType::name, default_value("name_placeholder"))));
```

Too easy isn't it? You do not have to specify mapped type explicitly - it is deduced from your member pointers you pass during making a column (for example: `&User::id`). To create a column you have to pass two arguments at least: its name in the table and your mapped class member pointer. You can also add extra arguments to tell your storage about column's constraints like `primary_key`, `autoincrement`, `default_value`, `unique` or `generated_always_as` (order isn't important; `not_null` is deduced from type automatically).
Too easy isn't it? You do not have to specify mapped type explicitly - it is deduced from your member pointers you pass during making a column (for example: `&User::id`). To create a column you have to pass two arguments at least: its name in the table and your mapped class member pointer. You can also add extra arguments to tell your storage about column's constraints like `primary_key`, `autoincrement`, `default_value`, `unique` or `generated_always_as` (order isn't important; `not_null`/`null` are deduced from type automatically but can be added manually if you wish with `null()` and `not_null()`).

More details about making storage can be found in [tutorial](https://github.com/fnc12/sqlite_orm/wiki/Making-a-storage).

Expand Down
19 changes: 19 additions & 0 deletions dev/constraints.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,9 @@ namespace sqlite_orm {
}
};

struct null_t {};

struct not_null_t {};
}

namespace internal {
Expand Down Expand Up @@ -440,6 +443,12 @@ namespace sqlite_orm {
template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = is_generated_always<T>::value;

template<class T>
using is_null = std::is_same<null_t, T>;

template<class T>
using is_not_null = std::is_same<not_null_t, T>;

/**
* PRIMARY KEY INSERTABLE traits.
*/
Expand All @@ -458,6 +467,8 @@ namespace sqlite_orm {
using is_constraint =
mpl::instantiate<mpl::disjunction<check_if<is_primary_key>,
check_if<is_foreign_key>,
check_if<is_null>,
check_if<is_not_null>,
check_if_is_template<unique_t>,
check_if_is_template<default_t>,
check_if_is_template<check_t>,
Expand Down Expand Up @@ -536,4 +547,12 @@ namespace sqlite_orm {
internal::check_t<T> check(T t) {
return {std::move(t)};
}

inline internal::null_t null() {
return {};
}

inline internal::not_null_t not_null() {
return {};
}
}
21 changes: 17 additions & 4 deletions dev/serializing_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,24 @@ namespace sqlite_orm {
const bool& isNotNull = get<2>(tpl);
auto& context = get<3>(tpl);

iterate_tuple(column.constraints, [&ss, &context](auto& constraint) {
ss << serialize(constraint, context) << ' ';
auto first = true;
constexpr std::array<const char*, 2> sep = {" ", ""};
iterate_tuple(column.constraints, [&ss, &context, &first, &sep](auto& constraint) {
ss << sep[std::exchange(first, false)];
ss << serialize(constraint, context);
});
if(isNotNull) {
ss << "NOT NULL";
using ConstraintsTuple = decltype(column.constraints);
constexpr bool constraintsHaveNullConstraint =
mpl::invoke_t<check_if_tuple_has_type<null_t>, ConstraintsTuple>::value;
constexpr bool constraintsHaveNotNullConstraint =
mpl::invoke_t<check_if_tuple_has_type<not_null_t>, ConstraintsTuple>::value;
if(!constraintsHaveNullConstraint && !constraintsHaveNotNullConstraint) {
ss << sep[std::exchange(first, false)];
if(isNotNull) {
ss << "NOT NULL";
} else {
ss << "NULL";
}
}

return ss;
Expand Down
32 changes: 25 additions & 7 deletions dev/statement_serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,26 @@ namespace sqlite_orm {
}
};

template<>
struct statement_serializer<null_t, void> {
using statement_type = null_t;

template<class Ctx>
std::string operator()(const statement_type& statement, const Ctx& context) const {
return "NULL";
}
};

template<>
struct statement_serializer<not_null_t, void> {
using statement_type = not_null_t;

template<class Ctx>
std::string operator()(const statement_type& statement, const Ctx& context) const {
return "NOT NULL";
}
};

template<class... Cs>
struct statement_serializer<primary_key_t<Cs...>, void> {
using statement_type = primary_key_t<Cs...>;
Expand Down Expand Up @@ -1031,13 +1051,11 @@ namespace sqlite_orm {
if(!context.skip_types_and_constraints) {
ss << " " << type_printer<field_type_t<column_type>>().print();
const bool columnIsNotNull = column.is_not_null();
auto constraintsTuple = streaming_column_constraints(
call_as_template_base<column_constraints>(polyfill::identity{})(column),
columnIsNotNull,
context);
if(std::tuple_size<decltype(constraintsTuple)>::value > 0) {
ss << " " << constraintsTuple;
}
ss << " "
<< streaming_column_constraints(
call_as_template_base<column_constraints>(polyfill::identity{})(column),
columnIsNotNull,
context);
}
return ss.str();
}
Expand Down
72 changes: 61 additions & 11 deletions include/sqlite_orm/sqlite_orm.h
Original file line number Diff line number Diff line change
Expand Up @@ -1698,6 +1698,9 @@ namespace sqlite_orm {
}
};

struct null_t {};

struct not_null_t {};
}

namespace internal {
Expand Down Expand Up @@ -1726,6 +1729,12 @@ namespace sqlite_orm {
template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = is_generated_always<T>::value;

template<class T>
using is_null = std::is_same<null_t, T>;

template<class T>
using is_not_null = std::is_same<not_null_t, T>;

/**
* PRIMARY KEY INSERTABLE traits.
*/
Expand All @@ -1744,6 +1753,8 @@ namespace sqlite_orm {
using is_constraint =
mpl::instantiate<mpl::disjunction<check_if<is_primary_key>,
check_if<is_foreign_key>,
check_if<is_null>,
check_if<is_not_null>,
check_if_is_template<unique_t>,
check_if_is_template<default_t>,
check_if_is_template<check_t>,
Expand Down Expand Up @@ -1822,6 +1833,14 @@ namespace sqlite_orm {
internal::check_t<T> check(T t) {
return {std::move(t)};
}

inline internal::null_t null() {
return {};
}

inline internal::not_null_t not_null() {
return {};
}
}
#pragma once

Expand Down Expand Up @@ -13663,11 +13682,24 @@ namespace sqlite_orm {
const bool& isNotNull = get<2>(tpl);
auto& context = get<3>(tpl);

iterate_tuple(column.constraints, [&ss, &context](auto& constraint) {
ss << serialize(constraint, context) << ' ';
auto first = true;
constexpr std::array<const char*, 2> sep = {" ", ""};
iterate_tuple(column.constraints, [&ss, &context, &first, &sep](auto& constraint) {
ss << sep[std::exchange(first, false)];
ss << serialize(constraint, context);
});
if(isNotNull) {
ss << "NOT NULL";
using ConstraintsTuple = decltype(column.constraints);
constexpr bool constraintsHaveNullConstraint =
mpl::invoke_t<check_if_tuple_has_type<null_t>, ConstraintsTuple>::value;
constexpr bool constraintsHaveNotNullConstraint =
mpl::invoke_t<check_if_tuple_has_type<not_null_t>, ConstraintsTuple>::value;
if(!constraintsHaveNullConstraint && !constraintsHaveNotNullConstraint) {
ss << sep[std::exchange(first, false)];
if(isNotNull) {
ss << "NOT NULL";
} else {
ss << "NULL";
}
}

return ss;
Expand Down Expand Up @@ -16433,6 +16465,26 @@ namespace sqlite_orm {
}
};

template<>
struct statement_serializer<null_t, void> {
using statement_type = null_t;

template<class Ctx>
std::string operator()(const statement_type& statement, const Ctx& context) const {
return "NULL";
}
};

template<>
struct statement_serializer<not_null_t, void> {
using statement_type = not_null_t;

template<class Ctx>
std::string operator()(const statement_type& statement, const Ctx& context) const {
return "NOT NULL";
}
};

template<class... Cs>
struct statement_serializer<primary_key_t<Cs...>, void> {
using statement_type = primary_key_t<Cs...>;
Expand Down Expand Up @@ -16578,13 +16630,11 @@ namespace sqlite_orm {
if(!context.skip_types_and_constraints) {
ss << " " << type_printer<field_type_t<column_type>>().print();
const bool columnIsNotNull = column.is_not_null();
auto constraintsTuple = streaming_column_constraints(
call_as_template_base<column_constraints>(polyfill::identity{})(column),
columnIsNotNull,
context);
if(std::tuple_size<decltype(constraintsTuple)>::value > 0) {
ss << " " << constraintsTuple;
}
ss << " "
<< streaming_column_constraints(
call_as_template_base<column_constraints>(polyfill::identity{})(column),
columnIsNotNull,
context);
}
return ss.str();
}
Expand Down
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ add_executable(unit_tests
statement_serializer_tests/column_constraints/primary_key.cpp
statement_serializer_tests/column_constraints/unique.cpp
statement_serializer_tests/column_constraints/check.cpp
statement_serializer_tests/column_constraints/null.cpp
statement_serializer_tests/column_constraints/not_null.cpp
statement_serializer_tests/bindables.cpp
statement_serializer_tests/ast/upsert_clause.cpp
statement_serializer_tests/ast/excluded.cpp
Expand Down
12 changes: 12 additions & 0 deletions tests/statement_serializer_tests/column_constraints/not_null.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <sqlite_orm/sqlite_orm.h>
#include <catch2/catch_all.hpp>

using namespace sqlite_orm;

TEST_CASE("statement_serializer not null") {
internal::db_objects_tuple<> storage;
internal::serializer_context<internal::db_objects_tuple<>> context{storage};
auto statement = not_null();
auto value = serialize(statement, context);
REQUIRE(value == "NOT NULL");
}
12 changes: 12 additions & 0 deletions tests/statement_serializer_tests/column_constraints/null.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <sqlite_orm/sqlite_orm.h>
#include <catch2/catch_all.hpp>

using namespace sqlite_orm;

TEST_CASE("statement_serializer null") {
internal::db_objects_tuple<> storage;
internal::serializer_context<internal::db_objects_tuple<>> context{storage};
auto statement = null();
auto value = serialize(statement, context);
REQUIRE(value == "NULL");
}
38 changes: 34 additions & 4 deletions tests/statement_serializer_tests/schema/column.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,50 @@ TEST_CASE("statement_serializer column") {
std::string expected;
SECTION("with types and constraints") {
context.skip_types_and_constraints = false;
SECTION("id INTEGER NOT NULL") {
SECTION("id INTEGER (implicit) NOT NULL") {
auto column = make_column("id", &User::id);
value = serialize(column, context);
expected = "\"id\" INTEGER NOT NULL";
}
SECTION("name TEXT NOT NULL") {
SECTION("id INTEGER (explicit) NOT NULL") {
auto column = make_column("id", &User::id, not_null());
value = serialize(column, context);
expected = "\"id\" INTEGER NOT NULL";
}
SECTION("id INTEGER (explicit) NULL") {
auto column = make_column("id", &User::id, null());
value = serialize(column, context);
expected = "\"id\" INTEGER NULL";
}
SECTION("name TEXT (implicit) NOT NULL") {
auto column = make_column("name", &User::name);
value = serialize(column, context);
expected = "\"name\" TEXT NOT NULL";
}
SECTION("nullable text") {
SECTION("name TEXT (explicit) NOT NULL") {
auto column = make_column("name", &User::name, not_null());
value = serialize(column, context);
expected = "\"name\" TEXT NOT NULL";
}
SECTION("name TEXT (explicit) NULL") {
auto column = make_column("name", &User::name, null());
value = serialize(column, context);
expected = "\"name\" TEXT NULL";
}
SECTION("nullable text (implicit) NULL") {
auto column = make_column("nullable_text", &User::nullableText);
value = serialize(column, context);
expected = "\"nullable_text\" TEXT ";
expected = "\"nullable_text\" TEXT NULL";
}
SECTION("nullable text (explicit) NOT NULL") {
auto column = make_column("nullable_text", &User::nullableText, not_null());
value = serialize(column, context);
expected = "\"nullable_text\" TEXT NOT NULL";
}
SECTION("nullable text (explicit) NULL") {
auto column = make_column("nullable_text", &User::nullableText, null());
value = serialize(column, context);
expected = "\"nullable_text\" TEXT NULL";
}
}
SECTION("without types and constraints") {
Expand Down

0 comments on commit d840654

Please sign in to comment.