Skip to content

Commit

Permalink
Merge pull request #1238 from fnc12/update-static-assert2
Browse files Browse the repository at this point in the history
added static_assert for update call
  • Loading branch information
fnc12 authored Oct 25, 2023
2 parents 0cf48ec + 87cee49 commit 4374014
Show file tree
Hide file tree
Showing 18 changed files with 536 additions and 237 deletions.
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

0 comments on commit 4374014

Please sign in to comment.