diff --git a/dev/column_names_getter.h b/dev/column_names_getter.h index d82a800ee..05820ed83 100644 --- a/dev/column_names_getter.h +++ b/dev/column_names_getter.h @@ -30,7 +30,7 @@ namespace sqlite_orm { const Ctx& context) { if(definedOrder) { auto& table = pick_table>(context.db_objects); - collectedExpressions.reserve(collectedExpressions.size() + table.columns_count); + collectedExpressions.reserve(collectedExpressions.size() + table.template count_of()); table.for_each_column([qualified = !context.skip_table_name, &tableName = table.name, &collectedExpressions](const column_identifier& column) { diff --git a/dev/constraints.h b/dev/constraints.h index 6fd308edf..e59bb42a9 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -379,6 +379,7 @@ namespace sqlite_orm { expression_type expression; }; +#if SQLITE_VERSION_NUMBER >= 3031000 struct basic_generated_always { enum class storage_type { not_specified, @@ -411,6 +412,7 @@ namespace sqlite_orm { return {std::move(this->expression), this->full, storage_type::stored}; } }; +#endif struct null_t {}; @@ -420,7 +422,12 @@ namespace sqlite_orm { namespace internal { template - SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = polyfill::is_specialization_of_v; + SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = +#if SQLITE_VERSION_NUMBER >= 3006019 + polyfill::is_specialization_of_v; +#else + false; +#endif template using is_foreign_key = polyfill::bool_constant>; @@ -438,10 +445,15 @@ namespace sqlite_orm { SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = is_primary_key::value; template - using is_generated_always = polyfill::is_specialization_of; + SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = +#if SQLITE_VERSION_NUMBER >= 3031000 + polyfill::is_specialization_of_v; +#else + false; +#endif template - SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = is_generated_always::value; + using is_generated_always = polyfill::bool_constant>; /** * PRIMARY KEY INSERTABLE traits. @@ -458,37 +470,17 @@ namespace sqlite_orm { }; template - struct primary_key_colums_count_t; - - template - struct primary_key_colums_count_t> - : std::integral_constant(sizeof...(Cs))> {}; - - template - struct flatten_primry_keys_columns { - using columns_tuple = typename conc_tuple::type; - }; - - template - struct flatten_primry_keys_columns> : flatten_primry_keys_columns {}; - - template - using is_constraint = - mpl::instantiate, - check_if, - check_if_is_type, - check_if_is_type, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_type, -#if SQLITE_VERSION_NUMBER >= 3031000 - check_if, -#endif - // dummy tail because of SQLITE_VERSION_NUMBER checks above - mpl::always>, - T>; + using is_constraint = mpl::instantiate, + check_if, + check_if_is_type, + check_if_is_type, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_type, + check_if>, + T>; } #if SQLITE_VERSION_NUMBER >= 3031000 diff --git a/dev/functional/index_sequence_util.h b/dev/functional/index_sequence_util.h index b13343663..7d7a40d73 100644 --- a/dev/functional/index_sequence_util.h +++ b/dev/functional/index_sequence_util.h @@ -1,6 +1,6 @@ #pragma once -#include // std::index_sequence, std::make_index_sequence +#include // std::index_sequence #include "../functional/cxx_universal.h" diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index 5a842491a..30d498b13 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -120,7 +120,7 @@ namespace sqlite_orm { const Table& table, const std::vector& columnsToIgnore) const { // must ignore generated columns std::vector> columnNames; - columnNames.reserve(table.columns_count); + columnNames.reserve(table.template count_of()); table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { auto& columnName = column.name; #if __cpp_lib_ranges >= 201911L diff --git a/dev/implementations/table_definitions.h b/dev/implementations/table_definitions.h index 8e3c24268..a869d2272 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -31,7 +31,7 @@ namespace sqlite_orm { column.is_not_null(), std::move(dft), column.template is(), - column.is_generated()); + column.template is()); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { diff --git a/dev/schema/column.h b/dev/schema/column.h index c3f81baf5..f7654a1cf 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -76,24 +76,13 @@ namespace sqlite_orm { constraints_type constraints; /** - * Checks whether contraints are of trait `Trait` + * Checks whether contraints contain specified type. */ template class Trait> constexpr static bool is() { return tuple_has::value; } - constexpr static bool is_primary_key = - mpl::invoke_t, constraints_type>::value; - - constexpr bool is_generated() const { -#if SQLITE_VERSION_NUMBER >= 3031000 - return is(); -#else - return false; -#endif - } - /** * Simplified interface for `DEFAULT` constraint * @return string representation of default value if it exists otherwise nullptr @@ -118,9 +107,6 @@ namespace sqlite_orm { template SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of_v; - template - using is_primary_key_column = polyfill::bool_constant; - template using is_column = polyfill::bool_constant>; diff --git a/dev/schema/table.h b/dev/schema/table.h index bc69e4d68..67916e4a8 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -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" @@ -41,16 +42,8 @@ namespace sqlite_orm { struct table_t : basic_table { using object_type = O; using elements_type = std::tuple; - using columns_tuple = filter_tuple_t; - using dedicated_primary_keys_tuple = filter_tuple_t; - using dedicated_primary_keys_columns_tuple = - typename flatten_primry_keys_columns::columns_tuple; static constexpr bool is_without_rowid_v = WithoutRowId; - static constexpr int columns_count = static_cast(std::tuple_size::value); - static constexpr int primary_key_columns_count = count_tuple::value; - static constexpr int dedicated_primary_key_columns_count = - static_cast(std::tuple_size::value); using is_without_rowid = polyfill::bool_constant; @@ -65,15 +58,32 @@ namespace sqlite_orm { return {this->name, this->elements}; } - /** - * Returns foreign keys count in table definition + /* + * Returns the number of elements of the specified type. */ - static constexpr int foreign_keys_count = -#if SQLITE_VERSION_NUMBER >= 3006019 - static_cast(filter_tuple_sequence_t::size()); -#else - 0; -#endif + template class Trait> + static constexpr int count_of() { + using sequence_of = filter_tuple_sequence_t; + return int(sequence_of::size()); + } + + /* + * Returns the number of columns having the specified constraint trait. + */ + template class Trait> + static constexpr int count_of_columns_with() { + using filtered_index_sequence = col_index_sequence_with; + return int(filtered_index_sequence::size()); + } + + /* + * Returns the number of columns having the specified constraint trait. + */ + template class Trait> + static constexpr int count_of_columns_excluding() { + using excluded_col_index_sequence = col_index_sequence_excluding; + return int(excluded_col_index_sequence::size()); + } /** * Function used to get field value from object by mapped member pointer/setter/getter. @@ -195,19 +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; - return int(non_generated_col_index_sequence::size()); -#else - return this->columns_count; -#endif - } - /** * Call passed lambda with all defined foreign keys. * @param lambda Lambda called for each column. Function signature: `void(auto& column)` diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 2877647b0..1b6c896cd 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -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(); ss << " VALUES " << streaming_values_placeholders(columnsCount, valuesCount); return ss.str(); } diff --git a/dev/storage.h b/dev/storage.h index 012898438..729d9ccc0 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -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" @@ -173,14 +174,23 @@ namespace sqlite_orm { template void assert_updatable_type() const { +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) using Table = storage_pick_table_t; - constexpr int primaryKeyColumnsCount = - Table::primary_key_columns_count + Table::dedicated_primary_key_columns_count; - constexpr int nonPrimaryKeysColumnsCount = Table::columns_count - primaryKeyColumnsCount; - static_assert(primaryKeyColumnsCount > 0, "Type with no primary keys can't be updated"); + using elements_type = elements_type_t; + using col_index_sequence = filter_tuple_sequence_t; + using pk_index_sequence = filter_tuple_sequence_t; + using pkcol_index_sequence = col_index_sequence_with; + constexpr size_t dedicatedPrimaryKeyColumnsCount = + nested_tuple_size_for_t::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, - "Type with primary keys only can't be updated. You need at least 1 non-primary key column"); + "A table with only primary keys cannot be updated. You need at least 1 non-primary key column"); +#endif } template(dbObjects, tables_index_sequence{}, [&res](const auto& table) { - res += table.foreign_keys_count; + res += table.template count_of(); }); return res; } diff --git a/dev/tuple_helper/tuple_filter.h b/dev/tuple_helper/tuple_filter.h index 2fea55d8e..0cd049d67 100644 --- a/dev/tuple_helper/tuple_filter.h +++ b/dev/tuple_helper/tuple_filter.h @@ -3,7 +3,7 @@ #include // std::integral_constant, std::index_sequence, std::conditional, std::declval #include // std::tuple -#include "../functional/cxx_universal.h" +#include "../functional/cxx_universal.h" // ::size_t #include "../functional/index_sequence_util.h" namespace sqlite_orm { diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index a0154a927..7c40910fe 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -1,12 +1,10 @@ #pragma once #include // std::tuple, std::get, std::tuple_element, std::tuple_size -#include // std::remove_reference, std::index_sequence, std::make_index_sequence +#include // std::remove_reference, std::index_sequence, std::make_index_sequence, std::forward, std::move #include // 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 { @@ -92,19 +90,6 @@ namespace sqlite_orm { iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); } - template - R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { - return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; - } - - template - R create_from_tuple(Tpl&& tpl, Projection project = {}) { - return create_from_tuple( - std::forward(tpl), - std::make_index_sequence>::value>{}, - std::forward(project)); - } - template class Base, class L> struct lambda_as_template_base : L { #ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index 9c7a73c43..72e3248bc 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -1,7 +1,11 @@ #pragma once -#include // std::tuple +#include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval +#include // std::tuple_size, std::get +#include "../functional/cxx_universal.h" // ::size_t +#include "../functional/cxx_type_traits_polyfill.h" +#include "../functional/cxx_functional_polyfill.h" #include "../functional/mpl.h" namespace sqlite_orm { @@ -10,9 +14,9 @@ namespace sqlite_orm { template class Op> struct tuple_transformer; - template class Op> - struct tuple_transformer, Op> { - using type = std::tuple...>; + template class Pack, class... Types, template class Op> + struct tuple_transformer, Op> { + using type = Pack...>; }; /* @@ -22,5 +26,81 @@ namespace sqlite_orm { */ template class Op> using transform_tuple_t = typename tuple_transformer::type; + +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + /* + * Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results. + * + * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. + * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + */ + template + constexpr auto recombine_tuple(CombineOp combine, + const Tpl& tpl, + std::index_sequence, + Projector project, + Init initial) { + return combine(initial, polyfill::invoke(project, std::get(tpl))...); + } + + /* + * Apply a projection to a tuple's elements, and combine the results. + * + * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. + * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + */ + template + constexpr auto recombine_tuple(CombineOp combine, const Tpl& tpl, Projector project, Init initial) { + return recombine_tuple(std::move(combine), + std::forward(tpl), + std::make_index_sequence::value>{}, + std::move(project), + std::move(initial)); + } + + /* + * Function object that takes integral constants and returns the sum of their values as an integral constant. + * Because it's a "transparent" functor, it must be called with at least one argument, otherwise it cannot deduce the integral constant type. + */ + struct plus_reduce_integrals { + template + constexpr auto operator()(const Integrals&... integrals) const { + using integral_type = std::common_type_t; + return std::integral_constant{}; + } + }; + + /* + * Function object that takes a type, applies a projection on it, and returns the tuple size of the projected type (as an integral constant). + * The projection is applied on the argument type, not the argument value/object. + */ + template class NestedProject> + struct project_nested_tuple_size { + template + constexpr auto operator()(const T&) const { + return typename std::tuple_size>::type{}; + } + }; + + template class NestedProject, class Tpl, class IdxSeq> + using nested_tuple_size_for_t = decltype(recombine_tuple(plus_reduce_integrals{}, + std::declval(), + IdxSeq{}, + project_nested_tuple_size{}, + std::integral_constant{})); +#endif + + template + R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } + + template + R create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } } } diff --git a/dev/type_traits.h b/dev/type_traits.h index 64bf1ccdb..dac8df271 100644 --- a/dev/type_traits.h +++ b/dev/type_traits.h @@ -49,6 +49,9 @@ namespace sqlite_orm { template using constraints_type_t = typename T::constraints_type; + template + using columns_tuple_t = typename T::columns_tuple; + template using object_type_t = typename T::object_type; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index d2428ce28..d7f16ed06 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -394,6 +394,9 @@ namespace sqlite_orm { template using constraints_type_t = typename T::constraints_type; + template + using columns_tuple_t = typename T::columns_tuple; + template using object_type_t = typename T::object_type; @@ -1133,10 +1136,10 @@ namespace sqlite_orm { #include // std::tuple // #include "../functional/cxx_universal.h" - +// ::size_t // #include "../functional/index_sequence_util.h" -#include // std::index_sequence, std::make_index_sequence +#include // std::index_sequence // #include "../functional/cxx_universal.h" @@ -1665,6 +1668,7 @@ namespace sqlite_orm { expression_type expression; }; +#if SQLITE_VERSION_NUMBER >= 3031000 struct basic_generated_always { enum class storage_type { not_specified, @@ -1697,6 +1701,7 @@ namespace sqlite_orm { return {std::move(this->expression), this->full, storage_type::stored}; } }; +#endif struct null_t {}; @@ -1706,7 +1711,12 @@ namespace sqlite_orm { namespace internal { template - SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = polyfill::is_specialization_of_v; + SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = +#if SQLITE_VERSION_NUMBER >= 3006019 + polyfill::is_specialization_of_v; +#else + false; +#endif template using is_foreign_key = polyfill::bool_constant>; @@ -1724,10 +1734,15 @@ namespace sqlite_orm { SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = is_primary_key::value; template - using is_generated_always = polyfill::is_specialization_of; + SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = +#if SQLITE_VERSION_NUMBER >= 3031000 + polyfill::is_specialization_of_v; +#else + false; +#endif template - SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = is_generated_always::value; + using is_generated_always = polyfill::bool_constant>; /** * PRIMARY KEY INSERTABLE traits. @@ -1744,37 +1759,17 @@ namespace sqlite_orm { }; template - struct primary_key_colums_count_t; - - template - struct primary_key_colums_count_t> - : std::integral_constant(sizeof...(Cs))> {}; - - template - struct flatten_primry_keys_columns { - using columns_tuple = typename conc_tuple::type; - }; - - template - struct flatten_primry_keys_columns> : flatten_primry_keys_columns {}; - - template - using is_constraint = - mpl::instantiate, - check_if, - check_if_is_type, - check_if_is_type, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_type, -#if SQLITE_VERSION_NUMBER >= 3031000 - check_if, -#endif - // dummy tail because of SQLITE_VERSION_NUMBER checks above - mpl::always>, - T>; + using is_constraint = mpl::instantiate, + check_if, + check_if_is_type, + check_if_is_type, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_type, + check_if>, + T>; } #if SQLITE_VERSION_NUMBER >= 3031000 @@ -2476,24 +2471,13 @@ namespace sqlite_orm { constraints_type constraints; /** - * Checks whether contraints are of trait `Trait` + * Checks whether contraints contain specified type. */ template class Trait> constexpr static bool is() { return tuple_has::value; } - constexpr static bool is_primary_key = - mpl::invoke_t, constraints_type>::value; - - constexpr bool is_generated() const { -#if SQLITE_VERSION_NUMBER >= 3031000 - return is(); -#else - return false; -#endif - } - /** * Simplified interface for `DEFAULT` constraint * @return string representation of default value if it exists otherwise nullptr @@ -2518,9 +2502,6 @@ namespace sqlite_orm { template SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of_v; - template - using is_primary_key_column = polyfill::bool_constant; - template using is_column = polyfill::bool_constant>; @@ -9617,14 +9598,11 @@ namespace sqlite_orm { // #include "../tuple_helper/tuple_iteration.h" #include // std::tuple, std::get, std::tuple_element, std::tuple_size -#include // std::remove_reference, std::index_sequence, std::make_index_sequence +#include // std::remove_reference, std::index_sequence, std::make_index_sequence, std::forward, std::move #include // 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 { @@ -9710,19 +9688,6 @@ namespace sqlite_orm { iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); } - template - R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { - return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; - } - - template - R create_from_tuple(Tpl&& tpl, Projection project = {}) { - return create_from_tuple( - std::forward(tpl), - std::make_index_sequence>::value>{}, - std::forward(project)); - } - template class Base, class L> struct lambda_as_template_base : L { #ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED @@ -9752,6 +9717,116 @@ namespace sqlite_orm { } } +// #include "../tuple_helper/tuple_transformer.h" + +#include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval +#include // std::tuple_size, std::get + +// #include "../functional/cxx_universal.h" +// ::size_t +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../functional/cxx_functional_polyfill.h" + +// #include "../functional/mpl.h" + +namespace sqlite_orm { + namespace internal { + + template class Op> + struct tuple_transformer; + + template class Pack, class... Types, template class Op> + struct tuple_transformer, Op> { + using type = Pack...>; + }; + + /* + * Transform specified tuple. + * + * `Op` is a metafunction operation. + */ + template class Op> + using transform_tuple_t = typename tuple_transformer::type; + +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + /* + * Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results. + * + * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. + * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + */ + template + constexpr auto recombine_tuple(CombineOp combine, + const Tpl& tpl, + std::index_sequence, + Projector project, + Init initial) { + return combine(initial, polyfill::invoke(project, std::get(tpl))...); + } + + /* + * Apply a projection to a tuple's elements, and combine the results. + * + * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. + * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + */ + template + constexpr auto recombine_tuple(CombineOp combine, const Tpl& tpl, Projector project, Init initial) { + return recombine_tuple(std::move(combine), + std::forward(tpl), + std::make_index_sequence::value>{}, + std::move(project), + std::move(initial)); + } + + /* + * Function object that takes integral constants and returns the sum of their values as an integral constant. + * Because it's a "transparent" functor, it must be called with at least one argument, otherwise it cannot deduce the integral constant type. + */ + struct plus_reduce_integrals { + template + constexpr auto operator()(const Integrals&... integrals) const { + using integral_type = std::common_type_t; + return std::integral_constant{}; + } + }; + + /* + * Function object that takes a type, applies a projection on it, and returns the tuple size of the projected type (as an integral constant). + * The projection is applied on the argument type, not the argument value/object. + */ + template class NestedProject> + struct project_nested_tuple_size { + template + constexpr auto operator()(const T&) const { + return typename std::tuple_size>::type{}; + } + }; + + template class NestedProject, class Tpl, class IdxSeq> + using nested_tuple_size_for_t = decltype(recombine_tuple(plus_reduce_integrals{}, + std::declval(), + IdxSeq{}, + project_nested_tuple_size{}, + std::integral_constant{})); +#endif + + template + R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } + + template + R create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } + } +} + // #include "../member_traits/member_traits.h" // #include "../typed_comparator.h" @@ -9783,16 +9858,8 @@ namespace sqlite_orm { struct table_t : basic_table { using object_type = O; using elements_type = std::tuple; - using columns_tuple = filter_tuple_t; - using dedicated_primary_keys_tuple = filter_tuple_t; - using dedicated_primary_keys_columns_tuple = - typename flatten_primry_keys_columns::columns_tuple; static constexpr bool is_without_rowid_v = WithoutRowId; - static constexpr int columns_count = static_cast(std::tuple_size::value); - static constexpr int primary_key_columns_count = count_tuple::value; - static constexpr int dedicated_primary_key_columns_count = - static_cast(std::tuple_size::value); using is_without_rowid = polyfill::bool_constant; @@ -9807,15 +9874,32 @@ namespace sqlite_orm { return {this->name, this->elements}; } - /** - * Returns foreign keys count in table definition + /* + * Returns the number of elements of the specified type. */ - static constexpr int foreign_keys_count = -#if SQLITE_VERSION_NUMBER >= 3006019 - static_cast(filter_tuple_sequence_t::size()); -#else - 0; -#endif + template class Trait> + static constexpr int count_of() { + using sequence_of = filter_tuple_sequence_t; + return int(sequence_of::size()); + } + + /* + * Returns the number of columns having the specified constraint trait. + */ + template class Trait> + static constexpr int count_of_columns_with() { + using filtered_index_sequence = col_index_sequence_with; + return int(filtered_index_sequence::size()); + } + + /* + * Returns the number of columns having the specified constraint trait. + */ + template class Trait> + static constexpr int count_of_columns_excluding() { + using excluded_col_index_sequence = col_index_sequence_excluding; + return int(excluded_col_index_sequence::size()); + } /** * Function used to get field value from object by mapped member pointer/setter/getter. @@ -9937,19 +10021,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; - return int(non_generated_col_index_sequence::size()); -#else - return this->columns_count; -#endif - } - /** * Call passed lambda with all defined foreign keys. * @param lambda Lambda called for each column. Function signature: `void(auto& column)` @@ -10327,7 +10398,7 @@ namespace sqlite_orm { int foreign_keys_count(const DBOs& dbObjects) { int res = 0; iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { - res += table.foreign_keys_count; + res += table.template count_of(); }); return res; } @@ -10427,6 +10498,8 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_filter.h" +// #include "tuple_helper/tuple_transformer.h" + // #include "tuple_helper/tuple_iteration.h" // #include "type_traits.h" @@ -10538,31 +10611,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_transformer.h" -#include // std::tuple - -// #include "../functional/mpl.h" - -namespace sqlite_orm { - namespace internal { - - template class Op> - struct tuple_transformer; - - template class Op> - struct tuple_transformer, Op> { - using type = std::tuple...>; - }; - - /* - * Transform specified tuple. - * - * `Op` is a metafunction operation. - */ - template class Op> - using transform_tuple_t = typename tuple_transformer::type; - } -} - // #include "type_traits.h" // #include "storage_lookup.h" @@ -15528,7 +15576,7 @@ namespace sqlite_orm { const Ctx& context) { if(definedOrder) { auto& table = pick_table>(context.db_objects); - collectedExpressions.reserve(collectedExpressions.size() + table.columns_count); + collectedExpressions.reserve(collectedExpressions.size() + table.template count_of()); table.for_each_column([qualified = !context.skip_table_name, &tableName = table.name, &collectedExpressions](const column_identifier& column) { @@ -17091,7 +17139,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(); ss << " VALUES " << streaming_values_placeholders(columnsCount, valuesCount); return ss.str(); } @@ -17967,14 +18015,23 @@ namespace sqlite_orm { template void assert_updatable_type() const { +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) using Table = storage_pick_table_t; - constexpr int primaryKeyColumnsCount = - Table::primary_key_columns_count + Table::dedicated_primary_key_columns_count; - constexpr int nonPrimaryKeysColumnsCount = Table::columns_count - primaryKeyColumnsCount; - static_assert(primaryKeyColumnsCount > 0, "Type with no primary keys can't be updated"); + using elements_type = elements_type_t
; + using col_index_sequence = filter_tuple_sequence_t; + using pk_index_sequence = filter_tuple_sequence_t; + using pkcol_index_sequence = col_index_sequence_with; + constexpr size_t dedicatedPrimaryKeyColumnsCount = + nested_tuple_size_for_t::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, - "Type with primary keys only can't be updated. You need at least 1 non-primary key column"); + "A table with only primary keys cannot be updated. You need at least 1 non-primary key column"); +#endif } template(), - column.is_generated()); + column.template is()); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { @@ -20221,7 +20278,7 @@ namespace sqlite_orm { const Table& table, const std::vector& columnsToIgnore) const { // must ignore generated columns std::vector> columnNames; - columnNames.reserve(table.columns_count); + columnNames.reserve(table.template count_of()); table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { auto& columnName = column.name; #if __cpp_lib_ranges >= 201911L diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 171497517..b15a6baf6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -32,7 +32,6 @@ add_executable(unit_tests static_tests/is_primary_key_insertable.cpp static_tests/is_column_with_insertable_primary_key.cpp static_tests/operators_adl.cpp - static_tests/flatten_primary_keys_columns.cpp static_tests/table_static_tests.cpp tuple_iteration.cpp sync_schema_tests.cpp diff --git a/tests/schema/column_tests.cpp b/tests/schema/column_tests.cpp index da245ad3e..ee661a7c7 100644 --- a/tests/schema/column_tests.cpp +++ b/tests/schema/column_tests.cpp @@ -2,32 +2,33 @@ #include using namespace sqlite_orm; +using internal::is_generated_always; -TEST_CASE("column tests is_generated") { +TEST_CASE("column tests is") { struct User { int id = 0; int age = 0; }; SECTION("no constraints") { auto column = make_column("id", &User::id); - REQUIRE_FALSE(column.is_generated()); + REQUIRE_FALSE(column.is()); } #if SQLITE_VERSION_NUMBER >= 3031000 SECTION("1 constraint: generated") { SECTION("full") { auto column = make_column("age", &User::age, generated_always_as(add(&User::id, 5))); - REQUIRE(column.is_generated()); + REQUIRE(column.is()); } SECTION("not full") { auto column = make_column("age", &User::age, as(add(&User::id, 5))); - REQUIRE(column.is_generated()); + REQUIRE(column.is()); } } #endif SECTION("1 constraint: primary key") { auto column = make_column("id", &User::id, primary_key()); - REQUIRE_FALSE(column.is_generated()); + REQUIRE_FALSE(column.is()); } } diff --git a/tests/static_tests/flatten_primary_keys_columns.cpp b/tests/static_tests/flatten_primary_keys_columns.cpp deleted file mode 100644 index e9aa9c3b6..000000000 --- a/tests/static_tests/flatten_primary_keys_columns.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include // std::is_same - -using namespace sqlite_orm; - -TEST_CASE("flatten_primry_keys_columns") { - struct User { - int id = 0; - std::string name; - }; - { // id + name - auto pk1 = primary_key(&User::id); - auto pk2 = primary_key(&User::name); - - using Pk1 = decltype(pk1); - using Pk2 = decltype(pk2); - using Result = internal::flatten_primry_keys_columns::columns_tuple; - using Expected = std::tuple; - STATIC_REQUIRE(std::is_same::value); - } - { // (empty) + name - auto pk1 = primary_key(); - auto pk2 = primary_key(&User::name); - - using Pk1 = decltype(pk1); - using Pk2 = decltype(pk2); - using Result = internal::flatten_primry_keys_columns::columns_tuple; - using Expected = std::tuple; - STATIC_REQUIRE(std::is_same::value); - } - { // id + (empty) - auto pk1 = primary_key(&User::id); - auto pk2 = primary_key(); - - using Pk1 = decltype(pk1); - using Pk2 = decltype(pk2); - using Result = internal::flatten_primry_keys_columns::columns_tuple; - using Expected = std::tuple; - STATIC_REQUIRE(std::is_same::value); - } - { // (empty) + (empty) - auto pk1 = primary_key(); - auto pk2 = primary_key(); - - using Pk1 = decltype(pk1); - using Pk2 = decltype(pk2); - using Result = internal::flatten_primry_keys_columns::columns_tuple; - using Expected = std::tuple<>; - STATIC_REQUIRE(std::is_same::value); - } -} diff --git a/tests/static_tests/table_static_tests.cpp b/tests/static_tests/table_static_tests.cpp index 5f3099665..e96895de0 100644 --- a/tests/static_tests/table_static_tests.cpp +++ b/tests/static_tests/table_static_tests.cpp @@ -2,75 +2,115 @@ #include using namespace sqlite_orm; +using internal::is_column; +using internal::is_primary_key; -TEST_CASE("table static columns_count") { +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) +template +using dedicated_pk_columns_count_t = + internal::nested_tuple_size_for_t>; +#endif + +TEST_CASE("table static count_of()") { struct User { int id = 0; std::string name; }; { // 1 column no pk auto table = make_table("users", make_column("id", &User::id)); - STATIC_REQUIRE(table.columns_count == 1); - STATIC_REQUIRE(table.primary_key_columns_count == 0); - STATIC_REQUIRE(table.dedicated_primary_key_columns_count == 0); + STATIC_REQUIRE(table.count_of() == 1); + STATIC_REQUIRE(table.count_of() == 0); + STATIC_REQUIRE(table.count_of_columns_with() == 0); +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); +#endif } { // 1 column with 1 inline pk auto table = make_table("users", make_column("id", &User::id, primary_key())); - STATIC_REQUIRE(table.columns_count == 1); - STATIC_REQUIRE(table.primary_key_columns_count == 1); - STATIC_REQUIRE(table.dedicated_primary_key_columns_count == 0); + STATIC_REQUIRE(table.count_of() == 1); + STATIC_REQUIRE(table.count_of() == 0); + STATIC_REQUIRE(table.count_of_columns_with() == 1); +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); +#endif } { // 1 column with 1 inline pk autoincrement auto table = make_table("users", make_column("id", &User::id, primary_key().autoincrement())); - STATIC_REQUIRE(table.columns_count == 1); - STATIC_REQUIRE(table.primary_key_columns_count == 1); - STATIC_REQUIRE(table.dedicated_primary_key_columns_count == 0); + STATIC_REQUIRE(table.count_of() == 1); + STATIC_REQUIRE(table.count_of() == 0); + STATIC_REQUIRE(table.count_of_columns_with() == 1); +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); +#endif } { // 1 column with 1 dedicated pk auto table = make_table("users", make_column("id", &User::id), primary_key(&User::id)); - STATIC_REQUIRE(table.columns_count == 1); - STATIC_REQUIRE(table.primary_key_columns_count == 0); - STATIC_REQUIRE(table.dedicated_primary_key_columns_count == 1); + STATIC_REQUIRE(table.count_of() == 1); + STATIC_REQUIRE(table.count_of() == 1); + STATIC_REQUIRE(table.count_of_columns_with() == 0); +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); +#endif } { // 2 columns no pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name)); - STATIC_REQUIRE(table.columns_count == 2); - STATIC_REQUIRE(table.primary_key_columns_count == 0); - STATIC_REQUIRE(table.dedicated_primary_key_columns_count == 0); + STATIC_REQUIRE(table.count_of() == 2); + STATIC_REQUIRE(table.count_of() == 0); + STATIC_REQUIRE(table.count_of_columns_with() == 0); +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); +#endif } { // 2 columns with 1 inline id pk auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("id", &User::name)); - STATIC_REQUIRE(table.columns_count == 2); - STATIC_REQUIRE(table.primary_key_columns_count == 1); - STATIC_REQUIRE(table.dedicated_primary_key_columns_count == 0); + STATIC_REQUIRE(table.count_of() == 2); + STATIC_REQUIRE(table.count_of() == 0); + STATIC_REQUIRE(table.count_of_columns_with() == 1); +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); +#endif } { // 2 columns with 1 inline name pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name, primary_key())); - STATIC_REQUIRE(table.columns_count == 2); - STATIC_REQUIRE(table.primary_key_columns_count == 1); - STATIC_REQUIRE(table.dedicated_primary_key_columns_count == 0); + STATIC_REQUIRE(table.count_of() == 2); + STATIC_REQUIRE(table.count_of() == 0); + STATIC_REQUIRE(table.count_of_columns_with() == 1); +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); +#endif } { // 2 columns with 1 dedicated id pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name), primary_key(&User::id)); - STATIC_REQUIRE(table.columns_count == 2); - STATIC_REQUIRE(table.primary_key_columns_count == 0); - STATIC_REQUIRE(table.dedicated_primary_key_columns_count == 1); + STATIC_REQUIRE(table.count_of() == 2); + STATIC_REQUIRE(table.count_of() == 1); + STATIC_REQUIRE(table.count_of_columns_with() == 0); +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); +#endif } { // 2 columns with 1 dedicated name pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name), primary_key(&User::name)); - STATIC_REQUIRE(table.columns_count == 2); - STATIC_REQUIRE(table.primary_key_columns_count == 0); - STATIC_REQUIRE(table.dedicated_primary_key_columns_count == 1); + STATIC_REQUIRE(table.count_of() == 2); + STATIC_REQUIRE(table.count_of() == 1); + STATIC_REQUIRE(table.count_of_columns_with() == 0); +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); +#endif } { // 2 columns with 2 dedicated pks auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name), primary_key(&User::id, &User::name)); - STATIC_REQUIRE(table.columns_count == 2); - STATIC_REQUIRE(table.primary_key_columns_count == 0); - STATIC_REQUIRE(table.dedicated_primary_key_columns_count == 2); + STATIC_REQUIRE(table.count_of() == 2); + STATIC_REQUIRE(table.count_of() == 1); + STATIC_REQUIRE(table.count_of_columns_with() == 0); +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 2); +#endif } }