Skip to content
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

added static_assert for update call #1238

Merged
merged 7 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion dev/column_names_getter.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace sqlite_orm {
const Ctx& context) {
if(definedOrder) {
auto& table = pick_table<mapped_type_proxy_t<T>>(context.db_objects);
collectedExpressions.reserve(collectedExpressions.size() + table.count_columns_amount());
collectedExpressions.reserve(collectedExpressions.size() + table.template count_of<is_column>());
table.for_each_column([qualified = !context.skip_table_name,
&tableName = table.name,
&collectedExpressions](const column_identifier& column) {
Expand Down
67 changes: 35 additions & 32 deletions dev/constraints.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@ namespace sqlite_orm {
};

template<class T>
struct primary_key_with_autoincrement {
struct primary_key_with_autoincrement : T {
using primary_key_type = T;

primary_key_type primary_key;

primary_key_with_autoincrement(primary_key_type primary_key_) : primary_key(primary_key_) {}
const primary_key_type& as_base() const {
return *this;
}
#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED
primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {}
#endif
};

/**
Expand All @@ -65,7 +68,7 @@ namespace sqlite_orm {

columns_tuple columns;

primary_key_t(decltype(columns) columns) : columns(std::move(columns)) {}
primary_key_t(columns_tuple columns) : columns(std::move(columns)) {}

self asc() const {
auto res = *this;
Expand Down Expand Up @@ -379,6 +382,7 @@ namespace sqlite_orm {
expression_type expression;
};

#if SQLITE_VERSION_NUMBER >= 3031000
struct basic_generated_always {
enum class storage_type {
not_specified,
Expand Down Expand Up @@ -411,6 +415,7 @@ namespace sqlite_orm {
return {std::move(this->expression), this->full, storage_type::stored};
}
};
#endif

struct null_t {};

Expand All @@ -420,28 +425,32 @@ namespace sqlite_orm {
namespace internal {

template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = polyfill::is_specialization_of_v<T, foreign_key_t>;
SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v =
#if SQLITE_VERSION_NUMBER >= 3006019
polyfill::is_specialization_of_v<T, foreign_key_t>;
#else
false;
#endif

template<class T>
using is_foreign_key = polyfill::bool_constant<is_foreign_key_v<T>>;

template<class T>
struct is_primary_key : std::false_type {};

template<class... Cs>
struct is_primary_key<primary_key_t<Cs...>> : std::true_type {};

template<class T>
struct is_primary_key<primary_key_with_autoincrement<T>> : std::true_type {};
SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = std::is_base_of<primary_key_base, T>::value;

template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = is_primary_key<T>::value;
using is_primary_key = polyfill::bool_constant<is_primary_key_v<T>>;

template<class T>
using is_generated_always = polyfill::is_specialization_of<T, generated_always_t>;
SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v =
#if SQLITE_VERSION_NUMBER >= 3031000
polyfill::is_specialization_of_v<T, generated_always_t>;
#else
false;
#endif

template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = is_generated_always<T>::value;
using is_generated_always = polyfill::bool_constant<is_generated_always_v<T>>;

/**
* PRIMARY KEY INSERTABLE traits.
Expand All @@ -458,22 +467,16 @@ namespace sqlite_orm {
};

template<class T>
using is_constraint =
mpl::instantiate<mpl::disjunction<check_if<is_primary_key>,
check_if<is_foreign_key>,
check_if_is_type<null_t>,
check_if_is_type<not_null_t>,
check_if_is_template<unique_t>,
check_if_is_template<default_t>,
check_if_is_template<check_t>,
check_if_is_template<primary_key_with_autoincrement>,
check_if_is_type<collate_constraint_t>,
#if SQLITE_VERSION_NUMBER >= 3031000
check_if<is_generated_always>,
#endif
// dummy tail because of SQLITE_VERSION_NUMBER checks above
mpl::always<std::false_type>>,
T>;
using is_constraint = mpl::instantiate<mpl::disjunction<check_if<is_primary_key>,
check_if<is_foreign_key>,
check_if_is_type<null_t>,
check_if_is_type<not_null_t>,
check_if_is_template<unique_t>,
check_if_is_template<default_t>,
check_if_is_template<check_t>,
check_if_is_type<collate_constraint_t>,
check_if<is_generated_always>>,
T>;
}

#if SQLITE_VERSION_NUMBER >= 3031000
Expand Down
2 changes: 1 addition & 1 deletion dev/functional/index_sequence_util.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include <utility> // std::index_sequence, std::make_index_sequence
#include <utility> // std::index_sequence

#include "../functional/cxx_universal.h"

Expand Down
2 changes: 1 addition & 1 deletion dev/implementations/storage_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ namespace sqlite_orm {
const Table& table,
const std::vector<const table_xinfo*>& columnsToIgnore) const { // must ignore generated columns
std::vector<std::reference_wrapper<const std::string>> columnNames;
columnNames.reserve(table.count_columns_amount());
columnNames.reserve(table.template count_of<is_column>());
table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) {
auto& columnName = column.name;
#if __cpp_lib_ranges >= 201911L
Expand Down
2 changes: 1 addition & 1 deletion dev/implementations/table_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace sqlite_orm {
column.is_not_null(),
std::move(dft),
column.template is<is_primary_key>(),
column.is_generated());
column.template is<is_generated_always>());
});
auto compositeKeyColumnNames = this->composite_key_columns_names();
for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) {
Expand Down
12 changes: 2 additions & 10 deletions dev/schema/column.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,21 +76,13 @@ namespace sqlite_orm {
constraints_type constraints;

/**
* Checks whether contraints are of trait `Trait`
* Checks whether contraints contain specified type.
*/
template<template<class...> class Trait>
constexpr bool is() const {
constexpr static bool is() {
return tuple_has<Trait, constraints_type>::value;
}

constexpr bool is_generated() const {
#if SQLITE_VERSION_NUMBER >= 3031000
return is<is_generated_always>();
#else
return false;
#endif
}

/**
* Simplified interface for `DEFAULT` constraint
* @return string representation of default value if it exists otherwise nullptr
Expand Down
56 changes: 26 additions & 30 deletions dev/schema/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "../tuple_helper/tuple_filter.h"
#include "../tuple_helper/tuple_traits.h"
#include "../tuple_helper/tuple_iteration.h"
#include "../tuple_helper/tuple_transformer.h"
#include "../member_traits/member_traits.h"
#include "../typed_comparator.h"
#include "../type_traits.h"
Expand Down Expand Up @@ -43,6 +44,7 @@ namespace sqlite_orm {
using elements_type = std::tuple<Cs...>;

static constexpr bool is_without_rowid_v = WithoutRowId;

using is_without_rowid = polyfill::bool_constant<is_without_rowid_v>;

elements_type elements;
Expand All @@ -56,16 +58,31 @@ namespace sqlite_orm {
return {this->name, this->elements};
}

/**
* Returns foreign keys count in table definition
/*
* Returns the number of elements of the specified type.
*/
constexpr int foreign_keys_count() const {
#if SQLITE_VERSION_NUMBER >= 3006019
using fk_index_sequence = filter_tuple_sequence_t<elements_type, is_foreign_key>;
return int(fk_index_sequence::size());
#else
return 0;
#endif
template<template<class...> class Trait>
static constexpr int count_of() {
using sequence_of = filter_tuple_sequence_t<elements_type, Trait>;
return int(sequence_of::size());
}

/*
* Returns the number of columns having the specified constraint trait.
*/
template<template<class...> class Trait>
static constexpr int count_of_columns_with() {
using filtered_index_sequence = col_index_sequence_with<elements_type, Trait>;
return int(filtered_index_sequence::size());
}

/*
* Returns the number of columns having the specified constraint trait.
*/
template<template<class...> class Trait>
static constexpr int count_of_columns_excluding() {
using excluded_col_index_sequence = col_index_sequence_excluding<elements_type, Trait>;
return int(excluded_col_index_sequence::size());
}

/**
Expand Down Expand Up @@ -188,27 +205,6 @@ namespace sqlite_orm {
return res;
}

/**
* Counts and returns amount of columns without GENERATED ALWAYS constraints. Skips table constraints.
*/
constexpr int non_generated_columns_count() const {
#if SQLITE_VERSION_NUMBER >= 3031000
using non_generated_col_index_sequence =
col_index_sequence_excluding<elements_type, is_generated_always>;
return int(non_generated_col_index_sequence::size());
#else
return this->count_columns_amount();
#endif
}

/**
* Counts and returns amount of columns. Skips constraints.
*/
constexpr int count_columns_amount() const {
using col_index_sequence = filter_tuple_sequence_t<elements_type, is_column>;
return int(col_index_sequence::size());
}

/**
* Call passed lambda with all defined foreign keys.
* @param lambda Lambda called for each column. Function signature: `void(auto& column)`
Expand Down
10 changes: 5 additions & 5 deletions dev/statement_serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -907,13 +907,13 @@ namespace sqlite_orm {
}
};

template<class T>
struct statement_serializer<primary_key_with_autoincrement<T>, void> {
using statement_type = primary_key_with_autoincrement<T>;
template<class PK>
struct statement_serializer<primary_key_with_autoincrement<PK>, void> {
using statement_type = primary_key_with_autoincrement<PK>;

template<class Ctx>
std::string operator()(const statement_type& statement, const Ctx& context) const {
return serialize(statement.primary_key, context) + " AUTOINCREMENT";
return serialize(statement.as_base(), context) + " AUTOINCREMENT";
}
};

Expand Down Expand Up @@ -1430,7 +1430,7 @@ namespace sqlite_orm {
ss << "REPLACE INTO " << streaming_identifier(table.name) << " ("
<< streaming_non_generated_column_names(table) << ")";
const auto valuesCount = std::distance(rep.range.first, rep.range.second);
const auto columnsCount = table.non_generated_columns_count();
const auto columnsCount = table.template count_of_columns_excluding<is_generated_always>();
ss << " VALUES " << streaming_values_placeholders(columnsCount, valuesCount);
return ss.str();
}
Expand Down
29 changes: 27 additions & 2 deletions dev/storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "functional/mpl.h"
#include "tuple_helper/tuple_traits.h"
#include "tuple_helper/tuple_filter.h"
#include "tuple_helper/tuple_transformer.h"
#include "tuple_helper/tuple_iteration.h"
#include "type_traits.h"
#include "alias.h"
Expand Down Expand Up @@ -171,6 +172,27 @@ namespace sqlite_orm {
"type is not mapped to storage");
}

template<class O>
void assert_updatable_type() const {
#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED)
using Table = storage_pick_table_t<O, db_objects_type>;
using elements_type = elements_type_t<Table>;
using col_index_sequence = filter_tuple_sequence_t<elements_type, is_column>;
using pk_index_sequence = filter_tuple_sequence_t<elements_type, is_primary_key>;
using pkcol_index_sequence = col_index_sequence_with<elements_type, is_primary_key>;
constexpr size_t dedicatedPrimaryKeyColumnsCount =
nested_tuple_size_for_t<columns_tuple_t, elements_type, pk_index_sequence>::value;

constexpr size_t primaryKeyColumnsCount =
dedicatedPrimaryKeyColumnsCount + pkcol_index_sequence::size();
constexpr ptrdiff_t nonPrimaryKeysColumnsCount = col_index_sequence::size() - primaryKeyColumnsCount;
static_assert(primaryKeyColumnsCount > 0, "A table without primary keys cannot be updated");
static_assert(
nonPrimaryKeysColumnsCount > 0,
"A table with only primary keys cannot be updated. You need at least 1 non-primary key column");
#endif
}

template<class O,
class Table = storage_pick_table_t<O, db_objects_type>,
std::enable_if_t<Table::is_without_rowid_v, bool> = true>
Expand Down Expand Up @@ -1081,8 +1103,11 @@ namespace sqlite_orm {
#endif // SQLITE_ORM_OPTIONAL_SUPPORTED

template<class T>
prepared_statement_t<update_t<T>> prepare(update_t<T> upd) {
return prepare_impl<update_t<T>>(std::move(upd));
prepared_statement_t<update_t<T>> prepare(update_t<T> statement) {
using object_type = typename expression_object_type<decltype(statement)>::type;
this->assert_mapped_type<object_type>();
this->assert_updatable_type<object_type>();
return prepare_impl<update_t<T>>(std::move(statement));
}

template<class T, class... Ids>
Expand Down
2 changes: 1 addition & 1 deletion dev/storage_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace sqlite_orm {
int foreign_keys_count(const DBOs& dbObjects) {
int res = 0;
iterate_tuple<true>(dbObjects, tables_index_sequence<DBOs>{}, [&res](const auto& table) {
res += table.foreign_keys_count();
res += table.template count_of<is_foreign_key>();
});
return res;
}
Expand Down
2 changes: 1 addition & 1 deletion dev/tuple_helper/tuple_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include <type_traits> // std::integral_constant, std::index_sequence, std::conditional, std::declval
#include <tuple> // std::tuple

#include "../functional/cxx_universal.h"
#include "../functional/cxx_universal.h" // ::size_t
#include "../functional/index_sequence_util.h"

namespace sqlite_orm {
Expand Down
17 changes: 1 addition & 16 deletions dev/tuple_helper/tuple_iteration.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
#pragma once

#include <tuple> // std::tuple, std::get, std::tuple_element, std::tuple_size
#include <type_traits> // std::remove_reference, std::index_sequence, std::make_index_sequence
#include <type_traits> // std::remove_reference, std::index_sequence, std::make_index_sequence, std::forward, std::move
#include <utility> // std::forward, std::move

#include "../functional/cxx_universal.h" // ::size_t
#include "../functional/cxx_type_traits_polyfill.h"
#include "../functional/cxx_functional_polyfill.h"

namespace sqlite_orm {
namespace internal {
Expand Down Expand Up @@ -92,19 +90,6 @@ namespace sqlite_orm {
iterate_tuple<Tpl>(std::make_index_sequence<std::tuple_size<Tpl>::value>{}, std::forward<L>(lambda));
}

template<class R, class Tpl, size_t... Idx, class Projection = polyfill::identity>
R create_from_tuple(Tpl&& tpl, std::index_sequence<Idx...>, Projection project = {}) {
return R{polyfill::invoke(project, std::get<Idx>(std::forward<Tpl>(tpl)))...};
}

template<class R, class Tpl, class Projection = polyfill::identity>
R create_from_tuple(Tpl&& tpl, Projection project = {}) {
return create_from_tuple<R>(
std::forward<Tpl>(tpl),
std::make_index_sequence<std::tuple_size<std::remove_reference_t<Tpl>>::value>{},
std::forward<Projection>(project));
}

template<template<class...> class Base, class L>
struct lambda_as_template_base : L {
#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED
Expand Down
Loading