8000 Non-standart primary key checking by denzor200 · Pull Request #644 · fnc12/sqlite_orm · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Non-standart primary key checking #644

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Mar 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bc834d4
First step: static_assert without '_without_rowid' checking
denzor200 Jan 16, 2021
0bec1da
table_without_rowid_t class
denzor200 Jan 16, 2021
f23d876
New type traits was added
denzor200 Jan 16, 2021
c977f4d
static_assert was edited
denzor200 Jan 16, 2021
e4787d6
fix for Windows building
denzor200 Jan 16, 2021
255eac2
'table_base' is simple structure now, not a template
denzor200 Jan 16, 2021
b794f90
Merge remote-tracking branch 'upstream/dev' into PRIMARY_NO_DEF_INSERT
denzor200 Jan 28, 2021
a0b1f35
Edited static_assert condition
denzor200 Jan 28, 2021
1843884
Added 'assert_insertable_type' method
denzor200 Jan 28, 2021
7fa9a70
Removed '_without_rowid' field
denzor200 Jan 28, 2021
a4abe43
Removed 'table_base' structure
denzor200 Jan 28, 2021
25da2b7
clang-format
denzor200 Jan 28, 2021
248fa10
Merge remote-tracking branch 'upstream/dev' into PRIMARY_NO_DEF_INSERT
denzor200 Feb 5, 2021
ea3190f
Added 'assert_insertable_type' into insert_range method
denzor200 Feb 5, 2021
5c9f9e9
Fix for undoned merge
denzor200 Feb 5, 2021
b8dafd2
Merge remote-tracking branch 'upstream/dev' into PRIMARY_NO_DEF_INSERT
denzor200 Feb 26, 2021
a455026
clang-format && fix for building test
denzor200 Feb 26, 2021
11e8069
minimal static_assert was finally implemented
denzor200 Feb 26, 2021
69cebfa
more informative runtime error
denzor200 Feb 27, 2021
4ea5309
'table_base' structure returned back
denzor200 Feb 28, 2021
5c86a32
resolved some conversations
denzor200 Feb 28, 2021
1d0a52c
fix for previous commit
denzor200 Feb 28, 2021
32979e4
fix for assert_insertable_type && moved it into 'prepare'
denzor200 Feb 28, 2021
895ed59
static_assert in static_tests was fixed for C++14
denzor200 Feb 28, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions dev/column.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,48 @@ namespace sqlite_orm {
}
};

// we are compelled to wrap all sfinae-implemented traits to prevent "error: type/value mismatch at argument 2 in template parameter list"
namespace sfinae {
/**
* Column with insertable primary key traits. Common case.
*/
template<class T, class SFINAE = void>
struct is_column_with_insertable_primary_key : public std::false_type {};

/**
* Column with insertable primary key traits. Specialized case case.
*/
template<class O, class T, class... Op>
struct is_column_with_insertable_primary_key<
column_t<O, T, Op...>,
typename std::enable_if<(tuple_helper::tuple_contains_type<
constraints::primary_key_t<>,
typename column_t<O, T, Op...>::constraints_type>::value)>::type> {
using column_type = column_t<O, T, Op...>;
static constexpr bool value = is_primary_key_insertable<column_type>::value;
};

/**
* Column with noninsertable primary key traits. Common case.
*/
template<class T, class SFINAE = void>
struct is_column_with_noninsertable_primary_key : public std::false_type {};

/**
* Column with noninsertable primary key traits. Specialized case case.
*/
template<class O, class T, class... Op>
struct is_column_with_noninsertable_primary_key<
column_t<O, T, Op...>,
typename std::enable_if<(tuple_helper::tuple_contains_type<
constraints::primary_key_t<>,
typename column_t<O, T, Op...>::constraints_type>::value)>::type> {
using column_type = column_t<O, T, Op...>;
static constexpr bool value = !is_primary_key_insertable<column_type>::value;
};

}

/**
* Column traits. Common case.
*/
Expand All @@ -121,6 +163,18 @@ namespace sqlite_orm {
template<class O, class T, class... Op>
struct is_column<column_t<O, T, Op...>> : public std::true_type {};

/**
* Column with insertable primary key traits.
*/
template<class T>
struct is_column_with_insertable_primary_key : public sfinae::is_column_with_insertable_primary_key<T> {};

/**
* Column with noninsertable primary key traits.
*/
template<class T>
struct is_column_with_noninsertable_primary_key : public sfinae::is_column_with_noninsertable_primary_key<T> {};

template<class T>
struct column_field_type {
using type = void;
Expand Down
19 changes: 19 additions & 0 deletions dev/constraints.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <type_traits> // std::is_base_of, std::false_type, std::true_type
#include <ostream> // std::ostream

#include "tuple_helper.h"

namespace sqlite_orm {

namespace constraints {
Expand Down Expand Up @@ -450,6 +452,23 @@ namespace sqlite_orm {
*/
template<class... Cs>
struct is_primary_key<constraints::primary_key_t<Cs...>> : public std::true_type {};

/**
* PRIMARY KEY INSERTABLE traits.
*/
template<typename T>
struct is_primary_key_insertable {
using field_type = typename T::field_type;
using constraints_type = typename T::constraints_type;

static_assert((tuple_helper::tuple_contains_type<constraints::primary_key_t<>, constraints_type>::value),
"an unexpected type was passed");

static constexpr bool value =
(tuple_helper::tuple_contains_some_type<constraints::default_t, constraints_type>::value ||
tuple_helper::tuple_contains_type<constraints::autoincrement_t, constraints_type>::value ||
std::is_base_of<integer_printer, type_printer<field_type>>::value);
};
}

}
3 changes: 2 additions & 1 deletion dev/statement_serializator.h
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,8 @@ namespace sqlite_orm {
auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names();

tImpl.table.for_each_column([&tImpl, &columnNames, &compositeKeyColumnNames](auto& c) {
if(tImpl.table._without_rowid || !c.template has<constraints::primary_key_t<>>()) {
using table_type = typename std::decay<decltype(tImpl.table)>::type;
if(table_type::is_without_rowid || !c.template has<constraints::primary_key_t<>>()) {
auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name);
if(it == compositeKeyColumnNames.end()) {
columnNames.emplace_back(c.name);
Expand Down
62 changes: 54 additions & 8 deletions dev/storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
#include "statement_serializator.h"
#include "table_name_collector.h"
#include "object_from_column_builder.h"
#include "table.h"
#include "column.h"

namespace sqlite_orm {

Expand Down Expand Up @@ -87,6 +89,7 @@ namespace sqlite_orm {

template<class I>
void create_table(sqlite3* db, const std::string& tableName, const I& tableImpl) {
using table_type = typename std::decay<decltype(tableImpl.table)>::type;
std::stringstream ss;
ss << "CREATE TABLE '" << tableName << "' ( ";
auto columnsCount = tableImpl.table.columns_count;
Expand All @@ -101,7 +104,7 @@ namespace sqlite_orm {
index++;
});
ss << ") ";
if(tableImpl.table._without_rowid) {
if(table_type::is_without_rowid) {
ss << "WITHOUT ROWID ";
}
perform_void_exec(db, ss.str());
Expand Down Expand Up @@ -142,6 +145,37 @@ namespace sqlite_orm {
static_assert(tuple_helper::has_type<O, mapped_types_tuples>::value, "type is not mapped to a storage");
}

template<class O>
void assert_insertable_type() const {
auto& tImpl = this->get_impl<O>();
using table_type = typename std::decay<decltype(tImpl.table)>::type;
using columns_type = typename std::decay<decltype(tImpl.table.columns)>::type;

using bool_type = std::integral_constant<bool, table_type::is_without_rowid>;

static_if<bool_type{}>(
[](auto& tImpl) {
std::ignore = tImpl;

// all right. it's a "without_rowid" table
},
[](auto& tImpl) {
std::ignore = tImpl;
static_assert(
count_tuple<columns_type, is_column_with_insertable_primary_key>::value <= 1,
"Attempting to execute 'insert' request into an noninsertable table was detected. "
"Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of "
"'insert', or you can use 'insert' with explicit column listing.");
static_assert(
count_tuple<columns_type, is_column_with_noninsertable_primary_key>::value == 0,
"Attempting to execute 'insert' request into an noninsertable table was detected. "
"Insertable table cannot contain non-standard primary keys. Please use 'replace' instead "
"of 'insert', or you can use 'insert' with explicit column listing.");

// unfortunately, this static_assert's can't see an composite keys((
})(tImpl);
}

template<class O>
auto& get_impl() const {
return this->impl.template get_impl<O>();
Expand Down Expand Up @@ -600,20 +634,27 @@ namespace sqlite_orm {
template<class O>
int insert(const O& o) {
this->assert_mapped_type<O>();
auto statement = this->prepare(sqlite_orm::insert(std::ref(o)));
return int(this->execute(statement));
this->assert_insertable_type<O>();

return call_insert_impl_and_catch_constraint_failed([this, &o]() {
auto statement = this->prepare(sqlite_orm::insert(std::ref(o)));
return int(this->execute(statement));
});
}

template<class It>
void insert_range(It from, It to) {
using O = typename std::iterator_traits<It>::value_type;
this->assert_mapped_type<O>();
this->assert_insertable_type<O>();
if(from == to) {
return;
}

auto statement = this->prepare(sqlite_orm::insert_range(from, to));
this->execute(statement);
call_insert_impl_and_catch_constraint_failed([this, from, to]() {
auto statement = this->prepare(sqlite_orm::insert_range(from, to));
this->execute(statement);
});
}

/**
Expand Down Expand Up @@ -651,9 +692,9 @@ namespace sqlite_orm {
return res;
}

template<class... Tss, class... Cs>
template<template<class...> class TTable, class... Tss, class... Cs>
sync_schema_result
sync_table(const storage_impl<table_t<Cs...>, Tss...>& tImpl, sqlite3* db, bool preserve) {
sync_table(const storage_impl<TTable<Cs...>, Tss...>& tImpl, sqlite3* db, bool preserve) {
auto res = sync_schema_result::already_in_sync;

auto schema_stat = tImpl.schema_status(db, preserve);
Expand Down Expand Up @@ -854,6 +895,7 @@ namespace sqlite_orm {
prepared_statement_t<insert_t<T>> prepare(insert_t<T> ins) {
using object_type = typename expression_object_type<decltype(ins)>::type;
this->assert_mapped_type<object_type>();
this->assert_insertable_type<object_type>();
return prepare_impl<insert_t<T>>(std::move(ins));
}

Expand All @@ -866,6 +908,9 @@ namespace sqlite_orm {

template<class It>
prepared_statement_t<insert_range_t<It>> prepare(insert_range_t<It> statement) {
using object_type = typename expression_object_type<decltype(statement)>::type;
this->assert_mapped_type<object_type>();
this->assert_insertable_type<object_type>();
return prepare_impl<insert_range_t<It>>(std::move(statement));
}

Expand Down Expand Up @@ -976,7 +1021,8 @@ namespace sqlite_orm {

auto processObject = [&index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto& o) {
tImpl.table.for_each_column([&](auto& c) {
if(tImpl.table._without_rowid || !c.template has<constraints::primary_key_t<>>()) {
using table_type = typename std::decay<decltype(tImpl.table)>::type;
if(table_type::is_without_rowid || !c.template has<constraints::primary_key_t<>>()) {
auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name);
if(it == compositeKeyColumnNames.end()) {
using column_type = typename std::decay<decltype(c)>::type;
Expand Down
11 changes: 11 additions & 0 deletions dev/storage_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ namespace sqlite_orm {
template<class T, class... Args>
struct table_t;

template<class T, class... Args>
struct table_without_rowid_t;

namespace storage_traits {

/**
Expand Down Expand Up @@ -95,6 +98,14 @@ namespace sqlite_orm {
using type = std::tuple<typename Args::field_type...>;
};

/**
* type is std::tuple of field types of mapped colums.
*/
template<class T, class... Args>
struct table_types<table_without_rowid_t<T, Args...>> {
using type = std::tuple<typename Args::field_type...>;
};

/**
* S - storage_impl type
* T - mapped or not mapped data type
Expand Down
40 changes: 30 additions & 10 deletions dev/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,35 +19,39 @@ namespace sqlite_orm {

namespace internal {

template<bool _without_rowid>
struct table_base {

/**
* Table name.
*/
std::string name;

bool _without_rowid = false;
static constexpr const bool is_without_rowid = _without_rowid;
};

template<class T, class... Cs>
struct table_without_rowid_t;

/**
* Table interface class. Implementation is hidden in `table_impl` class.
* Template for table interface class.
*/
template<class T, class... Cs>
struct table_t : table_base {
template<class T, bool _without_rowid, class... Cs>
struct table_template : table_base<_without_rowid> {
using object_type = T;
using columns_type = std::tuple<Cs...>;
using super = table_base<_without_rowid>;

static constexpr const int columns_count = static_cast<int>(std::tuple_size<columns_type>::value);

using super::name;
columns_type columns;

table_t(decltype(name) name_, columns_type columns_) :
table_base{std::move(name_)}, columns(std::move(columns_)) {}
table_template(std::string name_, columns_type columns_) :
super{std::move(name_)}, columns{std::move(columns_)} {}

table_t<T, Cs...> without_rowid() const {
auto res = *this;
res._without_rowid = true;
return res;
table_without_rowid_t<T, Cs...> without_rowid() const {
return {name, columns};
}

/**
Expand Down Expand Up @@ -264,6 +268,22 @@ namespace sqlite_orm {
return res;
}
};

/**
* Table interface class.
*/
template<class T, class... Cs>
struct table_t : table_template<T, false, Cs...> {
using table_template<T, false, Cs...>::table_template;
};

/**
* Table interface class with 'without_rowid' tag.
*/
template<class T, class... Cs>
struct table_without_rowid_t : table_template<T, true, Cs...> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to split table in two classes? We can just have a template bool arg in table_t class instead of two table classes

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still actual

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should leave table_without_rowid_t, beacause "a template bool arg in table_t class" may deliver some small problems for users.
For example, lets see section "Slow & size effective"
https://github.com/fnc12/sqlite_orm/wiki/FAQ

using table_template<T, true, Cs...>::table_template;
};
}

/**
Expand Down
22 changes: 21 additions & 1 deletion dev/tuple_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ namespace sqlite_orm {

// got from here http://stackoverflow.com/questions/25958259/how-do-i-find-out-if-a-tuple-contains-a-type
namespace tuple_helper {

/**
* HAS_TYPE type trait
*/
template<typename T, typename Tuple>
struct has_type;

Expand All @@ -25,6 +27,24 @@ namespace sqlite_orm {
template<typename T, typename Tuple>
using tuple_contains_type = typename has_type<T, Tuple>::type;

/**
* HAS_SOME_TYPE type trait
*/
template<template<class> class TT, typename Tuple>
struct has_some_type;

template<template<class> class TT>
struct has_some_type<TT, std::tuple<>> : std::false_type {};

template<template<class> class TT, typename U, typename... Ts>
struct has_some_type<TT, std::tuple<U, Ts...>> : has_some_type<TT, std::tuple<Ts...>> {};

template<template<class> class TT, typename T, typename... Ts>
struct has_some_type<TT, std::tuple<TT<T>, Ts...>> : std::true_type {};

template<template<class> class TT, typename Tuple>
using tuple_contains_some_type = typename has_some_type<TT, Tuple>::type;

template<size_t N, class... Args>
struct iterator {

Expand Down
Loading
0