From d617eb13894e36ac340727323b84e250f3f50198 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sat, 21 Oct 2023 21:45:37 +0600 Subject: [PATCH 01/10] added assert_updatable_type --- dev/column_names_getter.h | 2 +- dev/constraints.h | 14 + dev/implementations/storage_definitions.h | 2 +- dev/schema/column.h | 7 +- dev/schema/table.h | 25 +- dev/storage.h | 18 +- dev/storage_impl.h | 2 +- include/sqlite_orm/sqlite_orm.h | 239 +++++++++++++++--- tests/CMakeLists.txt | 2 + .../flatten_primary_keys_columns.cpp | 52 ++++ tests/static_tests/table_static_tests.cpp | 91 +++++++ 11 files changed, 405 insertions(+), 49 deletions(-) create mode 100644 tests/static_tests/flatten_primary_keys_columns.cpp create mode 100644 tests/static_tests/table_static_tests.cpp diff --git a/dev/column_names_getter.h b/dev/column_names_getter.h index 63ea1082a..d82a800ee 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.count_columns_amount()); + collectedExpressions.reserve(collectedExpressions.size() + table.columns_count); 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 4d54c81ad..365d24db7 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -457,6 +457,20 @@ namespace sqlite_orm { static_assert(tuple_has>::value, "an unexpected type was passed"); }; + 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, diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index 89e464ef2..5a842491a 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.count_columns_amount()); + columnNames.reserve(table.columns_count); table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { auto& columnName = column.name; #if __cpp_lib_ranges >= 201911L diff --git a/dev/schema/column.h b/dev/schema/column.h index b723a5205..79b8c2eaf 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -79,10 +79,12 @@ namespace sqlite_orm { * Checks whether contraints are of trait `Trait` */ template class Trait> - constexpr bool is() const { + 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(); @@ -115,6 +117,9 @@ 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 6bc477f4c..3b788ed4f 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -41,8 +41,15 @@ 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; elements_type elements; @@ -59,14 +66,12 @@ namespace sqlite_orm { /** * Returns foreign keys count in table definition */ - constexpr int foreign_keys_count() const { + static constexpr int foreign_keys_count = #if SQLITE_VERSION_NUMBER >= 3006019 - using fk_index_sequence = filter_tuple_sequence_t; - return int(fk_index_sequence::size()); + static_cast(filter_tuple_sequence_t::size()); #else - return 0; + 0; #endif - } /** * Function used to get field value from object by mapped member pointer/setter/getter. @@ -197,18 +202,10 @@ namespace sqlite_orm { col_index_sequence_excluding; return int(non_generated_col_index_sequence::size()); #else - return this->count_columns_amount(); + return this->columns_count; #endif } - /** - * Counts and returns amount of columns. Skips constraints. - */ - constexpr int count_columns_amount() const { - using col_index_sequence = filter_tuple_sequence_t; - 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)` diff --git a/dev/storage.h b/dev/storage.h index 854a8c385..85413f06c 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -171,6 +171,17 @@ namespace sqlite_orm { "type is not mapped to storage"); } + template + void assert_updatable_type() const { + 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"); + static_assert(nonPrimaryKeysColumnsCount > 0, + "Type with primary keys only can't be updated. You need at least 1 non-primary key column"); + } + template, std::enable_if_t = true> @@ -1081,8 +1092,11 @@ namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - prepared_statement_t> prepare(update_t upd) { - return prepare_impl>(std::move(upd)); + prepared_statement_t> prepare(update_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + this->assert_updatable_type(); + return prepare_impl>(std::move(statement)); } template diff --git a/dev/storage_impl.h b/dev/storage_impl.h index eb0d8ea07..6fbbc6397 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -21,7 +21,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.foreign_keys_count; }); return res; } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 37eb7dec4..9e9ca10cf 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -10,6 +10,7 @@ __pragma(push_macro("max")) // #include "cxx_universal.h" + /* * This header makes central C++ functionality on which sqlite_orm depends universally available: * - alternative operator representations @@ -28,6 +29,7 @@ using std::nullptr_t; // #include "cxx_core_features.h" + /* * This header detects core C++ language features on which sqlite_orm depends. * May be updated/overwritten by cxx_compiler_quirks.h @@ -108,6 +110,7 @@ using std::nullptr_t; // #include "cxx_compiler_quirks.h" + /* * This header defines macros for circumventing compiler quirks on which sqlite_orm depends. * May amend cxx_core_features.h @@ -167,6 +170,8 @@ using std::nullptr_t; #define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS #endif + + #if SQLITE_ORM_HAS_INCLUDE() #include #endif @@ -211,6 +216,7 @@ using std::nullptr_t; // #include "cxx_universal.h" + namespace sqlite_orm { namespace internal { namespace polyfill { @@ -352,6 +358,7 @@ namespace sqlite_orm { namespace polyfill = internal::polyfill; } + namespace sqlite_orm { // C++ generic traits used throughout the library namespace internal { @@ -588,8 +595,10 @@ namespace sqlite_orm { #include // std::vector // #include "functional/cxx_optional.h" + // #include "cxx_core_features.h" + #if SQLITE_ORM_HAS_INCLUDE() #include #endif @@ -598,6 +607,7 @@ namespace sqlite_orm { #define SQLITE_ORM_OPTIONAL_SUPPORTED #endif + // #include "functional/cxx_type_traits_polyfill.h" // #include "type_traits.h" @@ -634,6 +644,7 @@ namespace sqlite_orm { }; } + namespace sqlite_orm { /** @@ -732,6 +743,7 @@ namespace sqlite_orm { // #include "functional/mpl.h" + /* * Symbols for 'template metaprogramming' (compile-time template programming), * inspired by the MPL of Aleksey Gurtovoy and David Abrahams. @@ -760,6 +772,7 @@ namespace sqlite_orm { // #include "cxx_type_traits_polyfill.h" + namespace sqlite_orm { namespace internal { namespace mpl { @@ -1045,6 +1058,7 @@ namespace sqlite_orm { // #include "tuple_helper/same_or_void.h" + namespace sqlite_orm { namespace internal { @@ -1074,6 +1088,7 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_traits.h" + #include // std::is_same #include @@ -1081,6 +1096,7 @@ namespace sqlite_orm { // #include "../functional/mpl.h" + namespace sqlite_orm { namespace internal { /* @@ -1129,6 +1145,7 @@ namespace sqlite_orm { } // #include "tuple_helper/tuple_filter.h" + #include // std::integral_constant, std::index_sequence, std::conditional, std::declval #include // std::tuple @@ -1136,10 +1153,12 @@ namespace sqlite_orm { // #include "../functional/index_sequence_util.h" + #include // std::index_sequence, std::make_index_sequence // #include "../functional/cxx_universal.h" + namespace sqlite_orm { namespace internal { /** @@ -1169,6 +1188,7 @@ namespace sqlite_orm { } } + namespace sqlite_orm { namespace internal { @@ -1260,6 +1280,7 @@ namespace sqlite_orm { // #include "table_type_of.h" + namespace sqlite_orm { namespace internal { @@ -1304,6 +1325,7 @@ namespace sqlite_orm { // #include "type_printer.h" + namespace sqlite_orm { namespace internal { @@ -1743,6 +1765,20 @@ namespace sqlite_orm { static_assert(tuple_has>::value, "an unexpected type was passed"); }; + 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, @@ -1842,8 +1878,10 @@ namespace sqlite_orm { #include // std::shared_ptr, std::unique_ptr // #include "functional/cxx_optional.h" + // #include "functional/cxx_type_traits_polyfill.h" + namespace sqlite_orm { /** @@ -1883,6 +1921,7 @@ namespace sqlite_orm { // #include "tags.h" + // #include "functional/cxx_functional_polyfill.h" #include @@ -1897,12 +1936,14 @@ namespace sqlite_orm { #endif // #include "../member_traits/member_traits.h" + #include // std::enable_if, std::is_function, std::true_type, std::false_type // #include "../functional/cxx_universal.h" // #include "../functional/cxx_type_traits_polyfill.h" + namespace sqlite_orm { namespace internal { // SFINAE friendly trait to get a member object pointer's field type @@ -1990,6 +2031,7 @@ namespace sqlite_orm { } } + namespace sqlite_orm { namespace internal { namespace polyfill { @@ -2061,6 +2103,7 @@ namespace sqlite_orm { namespace polyfill = internal::polyfill; } + namespace sqlite_orm { namespace internal { struct negatable_t {}; @@ -2088,10 +2131,13 @@ namespace sqlite_orm { // #include "serialize_result_type.h" + // #include "functional/cxx_string_view.h" + // #include "cxx_core_features.h" + #if SQLITE_ORM_HAS_INCLUDE() #include #endif @@ -2116,6 +2162,7 @@ namespace sqlite_orm { } } + namespace sqlite_orm { namespace internal { @@ -2400,6 +2447,7 @@ namespace sqlite_orm { // #include "../constraints.h" + namespace sqlite_orm { namespace internal { @@ -2464,10 +2512,12 @@ namespace sqlite_orm { * Checks whether contraints are of trait `Trait` */ template class Trait> - constexpr bool is() const { + 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(); @@ -2500,6 +2550,9 @@ 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>; @@ -2587,12 +2640,14 @@ namespace sqlite_orm { #endif // SQLITE_ORM_OMITS_CODECVT // #include "functional/cxx_optional.h" + // #include "functional/cxx_universal.h" // #include "functional/cxx_type_traits_polyfill.h" // #include "is_std_ptr.h" + namespace sqlite_orm { /** @@ -2757,6 +2812,7 @@ namespace sqlite_orm { // #include "is_base_of_template.h" + #include // std::true_type, std::false_type, std::declval namespace sqlite_orm { @@ -2802,6 +2858,7 @@ namespace sqlite_orm { // #include "optional_container.h" + namespace sqlite_orm { namespace internal { @@ -2836,6 +2893,7 @@ namespace sqlite_orm { // #include "serializer_context.h" + namespace sqlite_orm { namespace internal { @@ -2877,6 +2935,7 @@ namespace sqlite_orm { // #include "alias_traits.h" + #include // std::remove_const, std::is_base_of, std::is_same #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include @@ -2888,6 +2947,7 @@ namespace sqlite_orm { // #include "type_traits.h" + namespace sqlite_orm { /** @short Base class for a custom table alias, column alias or expression alias. @@ -2966,17 +3026,20 @@ namespace sqlite_orm { // #include "expression.h" + #include #include // std::enable_if #include // std::move, std::forward, std::declval // #include "functional/cxx_optional.h" + // #include "functional/cxx_universal.h" // #include "functional/cxx_type_traits_polyfill.h" // #include "tags.h" + namespace sqlite_orm { namespace internal { @@ -3061,6 +3124,7 @@ namespace sqlite_orm { // #include "column_pointer.h" + #include // std::enable_if #include // std::move @@ -3068,6 +3132,7 @@ namespace sqlite_orm { // #include "tags.h" + namespace sqlite_orm { namespace internal { /** @@ -3110,6 +3175,7 @@ namespace sqlite_orm { // #include "literal.h" + namespace sqlite_orm { namespace internal { @@ -3126,6 +3192,7 @@ namespace sqlite_orm { } } + namespace sqlite_orm { namespace internal { @@ -4390,7 +4457,7 @@ namespace sqlite_orm { #include // std::copy_n // #include "functional/cxx_universal.h" -// ::size_t + // ::size_t // #include "functional/cxx_type_traits_polyfill.h" // #include "type_traits.h" @@ -4399,6 +4466,7 @@ namespace sqlite_orm { // #include "tags.h" + namespace sqlite_orm { namespace internal { @@ -4734,8 +4802,10 @@ namespace sqlite_orm { // #include "ast/into.h" + // #include "../functional/cxx_type_traits_polyfill.h" + namespace sqlite_orm { namespace internal { @@ -4754,6 +4824,7 @@ namespace sqlite_orm { } } + namespace sqlite_orm { using int64 = sqlite_int64; @@ -6854,6 +6925,7 @@ namespace sqlite_orm { #include // std::tuple, std::get, std::tuple_size // #include "functional/cxx_optional.h" + // #include "functional/cxx_universal.h" // #include "functional/cxx_type_traits_polyfill.h" @@ -6866,6 +6938,7 @@ namespace sqlite_orm { // #include "ast/where.h" + #include // std::false_type, std::true_type #include // std::move @@ -6875,6 +6948,7 @@ namespace sqlite_orm { // #include "../serialize_result_type.h" + namespace sqlite_orm { namespace internal { @@ -6922,12 +6996,14 @@ namespace sqlite_orm { // #include "ast/group_by.h" + #include // std::tuple, std::make_tuple #include // std::true_type, std::false_type #include // std::forward, std::move // #include "../functional/cxx_type_traits_polyfill.h" + namespace sqlite_orm { namespace internal { @@ -6974,6 +7050,7 @@ namespace sqlite_orm { // #include "alias_traits.h" + namespace sqlite_orm { namespace internal { @@ -7397,6 +7474,7 @@ namespace sqlite_orm { // #include "functional/cxx_universal.h" + namespace sqlite_orm { struct table_info { @@ -7454,6 +7532,7 @@ namespace sqlite_orm { // #include "../optional_container.h" + // NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? // (Could be implemented with a normal trigger that insert or update an internal table and then retreive // the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) @@ -7777,6 +7856,7 @@ namespace sqlite_orm { // #include "xdestroy_handling.h" + #include // std::integral_constant #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include @@ -7786,6 +7866,7 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" + namespace sqlite_orm { using xdestroy_fn_t = void (*)(void*); @@ -8027,6 +8108,7 @@ namespace sqlite_orm { #endif } + namespace sqlite_orm { /** @@ -8223,6 +8305,7 @@ namespace sqlite_orm { // #include "pointer_value.h" + namespace sqlite_orm { /** @@ -8593,6 +8676,7 @@ namespace sqlite_orm { // #include "journal_mode.h" + #include // std::back_inserter #include // std::string #include // std::unique_ptr @@ -8669,6 +8753,7 @@ namespace sqlite_orm { // #include "is_std_ptr.h" + namespace sqlite_orm { /** @@ -9099,6 +9184,7 @@ namespace sqlite_orm { // #include "error_code.h" + namespace sqlite_orm { /** @@ -9294,6 +9380,7 @@ namespace sqlite_orm { // #include "../indexed_column.h" + #include // std::string #include // std::move @@ -9301,6 +9388,7 @@ namespace sqlite_orm { // #include "ast/where.h" + namespace sqlite_orm { namespace internal { @@ -9366,6 +9454,7 @@ namespace sqlite_orm { // #include "../table_type_of.h" + namespace sqlite_orm { namespace internal { @@ -9504,13 +9593,14 @@ namespace sqlite_orm { #include // std::forward, std::move // #include "../functional/cxx_universal.h" -// ::size_t + // ::size_t // #include "../functional/cxx_type_traits_polyfill.h" // #include "../functional/cxx_functional_polyfill.h" // #include "../functional/static_magic.h" + #ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED #include // std::false_type, std::true_type, std::integral_constant #endif @@ -9595,16 +9685,18 @@ 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::forward, std::move // #include "../functional/cxx_universal.h" -// ::size_t + // ::size_t // #include "../functional/cxx_type_traits_polyfill.h" // #include "../functional/cxx_functional_polyfill.h" + namespace sqlite_orm { namespace internal { @@ -9743,6 +9835,7 @@ namespace sqlite_orm { // #include "column.h" + namespace sqlite_orm { namespace internal { @@ -9762,8 +9855,15 @@ 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; elements_type elements; @@ -9780,14 +9880,12 @@ namespace sqlite_orm { /** * Returns foreign keys count in table definition */ - constexpr int foreign_keys_count() const { + static constexpr int foreign_keys_count = #if SQLITE_VERSION_NUMBER >= 3006019 - using fk_index_sequence = filter_tuple_sequence_t; - return int(fk_index_sequence::size()); + static_cast(filter_tuple_sequence_t::size()); #else - return 0; + 0; #endif - } /** * Function used to get field value from object by mapped member pointer/setter/getter. @@ -9918,18 +10016,10 @@ namespace sqlite_orm { col_index_sequence_excluding; return int(non_generated_col_index_sequence::size()); #else - return this->count_columns_amount(); + return this->columns_count; #endif } - /** - * Counts and returns amount of columns. Skips constraints. - */ - constexpr int count_columns_amount() const { - using col_index_sequence = filter_tuple_sequence_t; - 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)` @@ -10141,7 +10231,7 @@ namespace sqlite_orm { #include // std::string // #include "functional/cxx_universal.h" -// ::nullptr_t + // ::nullptr_t // #include "functional/static_magic.h" // #include "tuple_helper/tuple_filter.h" @@ -10154,6 +10244,7 @@ namespace sqlite_orm { // #include "storage_lookup.h" + #include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void #include #include // std::index_sequence, std::make_index_sequence @@ -10164,6 +10255,7 @@ namespace sqlite_orm { // #include "type_traits.h" + namespace sqlite_orm { namespace internal { @@ -10296,6 +10388,7 @@ namespace sqlite_orm { } } + // interface functions namespace sqlite_orm { namespace internal { @@ -10307,7 +10400,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.foreign_keys_count; }); return res; } @@ -10359,6 +10452,7 @@ namespace sqlite_orm { // #include "storage_lookup.h" + namespace sqlite_orm { namespace internal { @@ -10395,6 +10489,7 @@ namespace sqlite_orm { #include // std::for_each, std::ranges::for_each // #include "functional/cxx_optional.h" + // #include "functional/cxx_universal.h" // #include "functional/cxx_functional_polyfill.h" @@ -10435,6 +10530,7 @@ namespace sqlite_orm { // #include "column_result.h" + #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of #include // std::tuple #include // std::reference_wrapper @@ -10445,6 +10541,7 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_fy.h" + #include namespace sqlite_orm { @@ -10473,12 +10570,14 @@ namespace sqlite_orm { // #include "mapped_type_proxy.h" + #include // std::remove_const // #include "type_traits.h" // #include "alias_traits.h" + namespace sqlite_orm { namespace internal { @@ -10510,6 +10609,7 @@ namespace sqlite_orm { // #include "storage_traits.h" + #include // std::tuple // #include "functional/cxx_type_traits_polyfill.h" @@ -10518,10 +10618,12 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_transformer.h" + #include // std::tuple // #include "../functional/mpl.h" + namespace sqlite_orm { namespace internal { @@ -10547,6 +10649,7 @@ namespace sqlite_orm { // #include "storage_lookup.h" + namespace sqlite_orm { namespace internal { @@ -10577,6 +10680,7 @@ namespace sqlite_orm { // #include "function.h" + #include #include #include // std::string @@ -10589,6 +10693,7 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" + namespace sqlite_orm { struct arg_values; @@ -10825,6 +10930,7 @@ namespace sqlite_orm { // #include "ast/special_keywords.h" + namespace sqlite_orm { namespace internal { struct current_time_t {}; @@ -11146,6 +11252,7 @@ namespace sqlite_orm { // #include "view.h" + #include #include // std::string #include // std::forward, std::move @@ -11157,6 +11264,7 @@ namespace sqlite_orm { // #include "iterator.h" + #include #include // std::shared_ptr, std::unique_ptr, std::make_shared #include // std::decay @@ -11173,6 +11281,7 @@ namespace sqlite_orm { // #include "object_from_column_builder.h" + #include #include // std::is_member_object_pointer @@ -11180,6 +11289,7 @@ namespace sqlite_orm { // #include "row_extractor.h" + namespace sqlite_orm { namespace internal { @@ -11225,6 +11335,7 @@ namespace sqlite_orm { // #include "util.h" + namespace sqlite_orm { namespace internal { @@ -11312,6 +11423,7 @@ namespace sqlite_orm { // #include "ast_iterator.h" + #include // std::vector #include // std::reference_wrapper @@ -11331,6 +11443,7 @@ namespace sqlite_orm { // #include "prepared_statement.h" + #include #include // std::unique_ptr #include // std::iterator_traits @@ -11348,12 +11461,14 @@ namespace sqlite_orm { // #include "connection_holder.h" + #include #include #include // std::string // #include "error_code.h" + namespace sqlite_orm { namespace internal { @@ -11426,6 +11541,7 @@ namespace sqlite_orm { // #include "values.h" + #include // std::vector #include // std::tuple #include // std::forward @@ -11434,6 +11550,7 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" + namespace sqlite_orm { namespace internal { @@ -11474,6 +11591,7 @@ namespace sqlite_orm { // #include "ast/upsert_clause.h" + #if SQLITE_VERSION_NUMBER >= 3024000 #include // std::tuple #include // std::forward, std::move @@ -11481,6 +11599,7 @@ namespace sqlite_orm { // #include "../functional/cxx_type_traits_polyfill.h" + namespace sqlite_orm { namespace internal { #if SQLITE_VERSION_NUMBER >= 3024000 @@ -11543,6 +11662,7 @@ namespace sqlite_orm { // #include "ast/set.h" + #include // std::tuple, std::tuple_size #include // std::string #include // std::vector @@ -11551,6 +11671,7 @@ namespace sqlite_orm { // #include "../table_name_collector.h" + #include // std::set #include // std::string #include // std::pair, std::move @@ -11569,6 +11690,7 @@ namespace sqlite_orm { // #include "storage_lookup.h" + namespace sqlite_orm { namespace internal { @@ -11658,6 +11780,7 @@ namespace sqlite_orm { } + namespace sqlite_orm { namespace internal { @@ -11757,6 +11880,7 @@ namespace sqlite_orm { } } + namespace sqlite_orm { namespace internal { @@ -12474,6 +12598,7 @@ namespace sqlite_orm { // #include "ast/excluded.h" + #include // std::move namespace sqlite_orm { @@ -12503,10 +12628,12 @@ namespace sqlite_orm { // #include "ast/exists.h" + #include // std::move // #include "../tags.h" + namespace sqlite_orm { namespace internal { @@ -12539,6 +12666,7 @@ namespace sqlite_orm { // #include "ast/match.h" + namespace sqlite_orm { namespace internal { @@ -12559,6 +12687,7 @@ namespace sqlite_orm { } } + namespace sqlite_orm { namespace internal { @@ -13248,6 +13377,7 @@ namespace sqlite_orm { // #include "util.h" + namespace sqlite_orm { namespace internal { @@ -13304,6 +13434,7 @@ namespace sqlite_orm { // #include "storage_base.h" + #include #include // std::function, std::bind #include // std::string @@ -13317,11 +13448,12 @@ namespace sqlite_orm { #include // std::find_if // #include "functional/cxx_universal.h" -// ::size_t + // ::size_t // #include "tuple_helper/tuple_iteration.h" // #include "pragma.h" + #include #include // std::string #include // std::function @@ -13341,6 +13473,7 @@ namespace sqlite_orm { // #include "serializing_util.h" + #include // std::index_sequence #include #include @@ -13349,7 +13482,7 @@ namespace sqlite_orm { #include // std::exchange, std::tuple_size // #include "functional/cxx_universal.h" -// ::size_t + // ::size_t // #include "functional/cxx_type_traits_polyfill.h" // #include "tuple_helper/tuple_iteration.h" @@ -13362,6 +13495,7 @@ namespace sqlite_orm { // #include "util.h" + namespace sqlite_orm { namespace internal { template @@ -13737,6 +13871,7 @@ namespace sqlite_orm { } } + namespace sqlite_orm { namespace internal { @@ -13956,6 +14091,7 @@ namespace sqlite_orm { // #include "limit_accessor.h" + #include #include // std::map #include // std::function @@ -13963,6 +14099,7 @@ namespace sqlite_orm { // #include "connection_holder.h" + namespace sqlite_orm { namespace internal { @@ -14096,11 +14233,13 @@ namespace sqlite_orm { // #include "transaction_guard.h" + #include // std::function #include // std::move // #include "connection_holder.h" + namespace sqlite_orm { namespace internal { @@ -14181,6 +14320,7 @@ namespace sqlite_orm { // #include "backup.h" + #include #include // std::system_error #include // std::string @@ -14191,6 +14331,7 @@ namespace sqlite_orm { // #include "connection_holder.h" + namespace sqlite_orm { namespace internal { @@ -14258,6 +14399,7 @@ namespace sqlite_orm { // #include "values_to_tuple.h" + #include #include // std::index_sequence, std::make_index_sequence #include // std::tuple, std::tuple_size, std::get @@ -14268,10 +14410,12 @@ namespace sqlite_orm { // #include "arg_values.h" + #include // #include "row_extractor.h" + namespace sqlite_orm { /** @short Wrapper around a dynamically typed value object. @@ -14415,6 +14559,7 @@ namespace sqlite_orm { }; } + namespace sqlite_orm { namespace internal { @@ -14459,6 +14604,7 @@ namespace sqlite_orm { // #include "serializing_util.h" + namespace sqlite_orm { namespace internal { @@ -15257,11 +15403,13 @@ namespace sqlite_orm { // #include "expression_object_type.h" + #include // std::decay #include // std::reference_wrapper // #include "prepared_statement.h" + namespace sqlite_orm { namespace internal { @@ -15395,6 +15543,7 @@ namespace sqlite_orm { // #include "statement_serializer.h" + #include // std::stringstream #include // std::string #include // std::enable_if, std::remove_pointer @@ -15410,6 +15559,7 @@ namespace sqlite_orm { // #include "functional/cxx_optional.h" + // #include "functional/cxx_universal.h" // #include "functional/cxx_functional_polyfill.h" @@ -15430,6 +15580,7 @@ namespace sqlite_orm { // #include "ast/rank.h" + namespace sqlite_orm { namespace internal { struct rank_t {}; @@ -15470,6 +15621,7 @@ namespace sqlite_orm { // #include "column_names_getter.h" + #include // std::is_base_of #include // std::string #include // std::vector @@ -15490,11 +15642,12 @@ namespace sqlite_orm { // #include "select_constraints.h" // #include "storage_lookup.h" -// pick_table + // pick_table // #include "serializer_context.h" // #include "util.h" + namespace sqlite_orm { namespace internal { @@ -15508,7 +15661,7 @@ namespace sqlite_orm { const Ctx& context) { if(definedOrder) { auto& table = pick_table>(context.db_objects); - collectedExpressions.reserve(collectedExpressions.size() + table.count_columns_amount()); + collectedExpressions.reserve(collectedExpressions.size() + table.columns_count); table.for_each_column([qualified = !context.skip_table_name, &tableName = table.name, &collectedExpressions](const column_identifier& column) { @@ -15597,6 +15750,7 @@ namespace sqlite_orm { // #include "order_by_serializer.h" + #include // std::string #include // std::stringstream @@ -15690,6 +15844,7 @@ namespace sqlite_orm { // #include "util.h" + namespace sqlite_orm { namespace internal { @@ -17827,6 +17982,7 @@ namespace sqlite_orm { // #include "serializing_util.h" + namespace sqlite_orm { namespace internal { @@ -17945,6 +18101,17 @@ namespace sqlite_orm { "type is not mapped to storage"); } + template + void assert_updatable_type() const { + 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"); + static_assert(nonPrimaryKeysColumnsCount > 0, + "Type with primary keys only can't be updated. You need at least 1 non-primary key column"); + } + template, std::enable_if_t = true> @@ -18855,8 +19022,11 @@ namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - prepared_statement_t> prepare(update_t upd) { - return prepare_impl>(std::move(upd)); + prepared_statement_t> prepare(update_t statement) { + using object_type = typename expression_object_type::type; + this->assert_mapped_type(); + this->assert_updatable_type(); + return prepare_impl>(std::move(statement)); } template @@ -19265,7 +19435,7 @@ namespace sqlite_orm { #include // std::get // #include "functional/cxx_universal.h" -// ::size_t + // ::size_t // #include "functional/static_magic.h" // #include "prepared_statement.h" @@ -19274,12 +19444,14 @@ namespace sqlite_orm { // #include "node_tuple.h" + #include // std::enable_if #include // std::tuple #include // std::pair #include // std::reference_wrapper // #include "functional/cxx_optional.h" + // #include "functional/cxx_type_traits_polyfill.h" // #include "tuple_helper/tuple_filter.h" @@ -19310,6 +19482,7 @@ namespace sqlite_orm { // #include "ast/match.h" + namespace sqlite_orm { namespace internal { @@ -19620,6 +19793,7 @@ namespace sqlite_orm { // #include "expression_object_type.h" + namespace sqlite_orm { template @@ -19799,6 +19973,7 @@ namespace sqlite_orm { // #include "pointer_value.h" + namespace sqlite_orm { inline constexpr const char carray_pvt_name[] = "carray"; @@ -19873,6 +20048,7 @@ namespace sqlite_orm { // #include "alias.h" + namespace sqlite_orm { /** * SQLite's "schema table" that stores the schema for a database. @@ -19918,6 +20094,7 @@ namespace sqlite_orm { // #include "../schema/table.h" + namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB struct dbstat { @@ -19960,6 +20137,7 @@ namespace sqlite_orm { * this file is also used to provide definitions of interface methods 'hitting the database'. */ + #include // std::make_unique // #include "../functional/cxx_core_features.h" @@ -19976,6 +20154,7 @@ namespace sqlite_orm { // #include "../schema/column.h" + namespace sqlite_orm { namespace internal { @@ -20010,13 +20189,14 @@ namespace sqlite_orm { #include // std::find_if, std::ranges::find // #include "../functional/cxx_universal.h" -// ::size_t + // ::size_t // #include "../type_printer.h" // #include "../schema/column.h" // #include "../schema/table.h" + namespace sqlite_orm { namespace internal { @@ -20081,6 +20261,7 @@ namespace sqlite_orm { // #include "../storage.h" + namespace sqlite_orm { namespace internal { @@ -20186,7 +20367,7 @@ namespace sqlite_orm { const Table& table, const std::vector& columnsToIgnore) const { // must ignore generated columns std::vector> columnNames; - columnNames.reserve(table.count_columns_amount()); + columnNames.reserve(table.columns_count); 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 ff037642b..171497517 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -32,6 +32,8 @@ 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 tests.cpp diff --git a/tests/static_tests/flatten_primary_keys_columns.cpp b/tests/static_tests/flatten_primary_keys_columns.cpp new file mode 100644 index 000000000..b46b6233a --- /dev/null +++ b/tests/static_tests/flatten_primary_keys_columns.cpp @@ -0,0 +1,52 @@ +#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 new file mode 100644 index 000000000..68819f09d --- /dev/null +++ b/tests/static_tests/table_static_tests.cpp @@ -0,0 +1,91 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("table static columns_count") { + 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); + } + { // 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); + } + { // 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); + } + { // 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); + } + { // 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); + } + { // 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); + } + { // 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); + } + { // 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); + } + { // 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); + } + { // 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); + } +} From db83d84c63681282ca5767c2ef5b17b77cda3357 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sat, 21 Oct 2023 21:49:50 +0600 Subject: [PATCH 02/10] code format --- dev/constraints.h | 11 +- dev/schema/column.h | 3 +- dev/schema/table.h | 8 +- dev/storage.h | 9 +- include/sqlite_orm/sqlite_orm.h | 200 +++--------------- .../flatten_primary_keys_columns.cpp | 16 +- tests/static_tests/table_static_tests.cpp | 57 ++--- 7 files changed, 74 insertions(+), 230 deletions(-) diff --git a/dev/constraints.h b/dev/constraints.h index 365d24db7..6fd308edf 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -460,16 +460,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 primary_key_colums_count_t> + : std::integral_constant(sizeof...(Cs))> {}; - template + template struct flatten_primry_keys_columns { using columns_tuple = typename conc_tuple::type; }; - template - struct flatten_primry_keys_columns>: flatten_primry_keys_columns {}; + template + struct flatten_primry_keys_columns> : flatten_primry_keys_columns {}; template using is_constraint = diff --git a/dev/schema/column.h b/dev/schema/column.h index 79b8c2eaf..c3f81baf5 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -83,7 +83,8 @@ namespace sqlite_orm { return tuple_has::value; } - constexpr static bool is_primary_key = mpl::invoke_t, constraints_type>::value; + constexpr static bool is_primary_key = + mpl::invoke_t, constraints_type>::value; constexpr bool is_generated() const { #if SQLITE_VERSION_NUMBER >= 3031000 diff --git a/dev/schema/table.h b/dev/schema/table.h index 3b788ed4f..bc69e4d68 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -43,12 +43,14 @@ namespace sqlite_orm { 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; + 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); + static constexpr int dedicated_primary_key_columns_count = + static_cast(std::tuple_size::value); using is_without_rowid = polyfill::bool_constant; @@ -66,7 +68,7 @@ namespace sqlite_orm { /** * Returns foreign keys count in table definition */ - static constexpr int foreign_keys_count = + static constexpr int foreign_keys_count = #if SQLITE_VERSION_NUMBER >= 3006019 static_cast(filter_tuple_sequence_t::size()); #else diff --git a/dev/storage.h b/dev/storage.h index 85413f06c..012898438 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -174,11 +174,12 @@ namespace sqlite_orm { template void assert_updatable_type() const { using Table = storage_pick_table_t; - constexpr int primaryKeyColumnsCount = Table::primary_key_columns_count + Table::dedicated_primary_key_columns_count; + 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"); - static_assert(nonPrimaryKeysColumnsCount > 0, + static_assert(primaryKeyColumnsCount > 0, "Type with no primary keys can't be updated"); + static_assert( + nonPrimaryKeysColumnsCount > 0, "Type with primary keys only can't be updated. You need at least 1 non-primary key column"); } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 9e9ca10cf..d2428ce28 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -10,7 +10,6 @@ __pragma(push_macro("max")) // #include "cxx_universal.h" - /* * This header makes central C++ functionality on which sqlite_orm depends universally available: * - alternative operator representations @@ -29,7 +28,6 @@ using std::nullptr_t; // #include "cxx_core_features.h" - /* * This header detects core C++ language features on which sqlite_orm depends. * May be updated/overwritten by cxx_compiler_quirks.h @@ -110,7 +108,6 @@ using std::nullptr_t; // #include "cxx_compiler_quirks.h" - /* * This header defines macros for circumventing compiler quirks on which sqlite_orm depends. * May amend cxx_core_features.h @@ -170,8 +167,6 @@ using std::nullptr_t; #define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS #endif - - #if SQLITE_ORM_HAS_INCLUDE() #include #endif @@ -216,7 +211,6 @@ using std::nullptr_t; // #include "cxx_universal.h" - namespace sqlite_orm { namespace internal { namespace polyfill { @@ -358,7 +352,6 @@ namespace sqlite_orm { namespace polyfill = internal::polyfill; } - namespace sqlite_orm { // C++ generic traits used throughout the library namespace internal { @@ -595,10 +588,8 @@ namespace sqlite_orm { #include // std::vector // #include "functional/cxx_optional.h" - // #include "cxx_core_features.h" - #if SQLITE_ORM_HAS_INCLUDE() #include #endif @@ -607,7 +598,6 @@ namespace sqlite_orm { #define SQLITE_ORM_OPTIONAL_SUPPORTED #endif - // #include "functional/cxx_type_traits_polyfill.h" // #include "type_traits.h" @@ -644,7 +634,6 @@ namespace sqlite_orm { }; } - namespace sqlite_orm { /** @@ -743,7 +732,6 @@ namespace sqlite_orm { // #include "functional/mpl.h" - /* * Symbols for 'template metaprogramming' (compile-time template programming), * inspired by the MPL of Aleksey Gurtovoy and David Abrahams. @@ -772,7 +760,6 @@ namespace sqlite_orm { // #include "cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { namespace mpl { @@ -1058,7 +1045,6 @@ namespace sqlite_orm { // #include "tuple_helper/same_or_void.h" - namespace sqlite_orm { namespace internal { @@ -1088,7 +1074,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_traits.h" - #include // std::is_same #include @@ -1096,7 +1081,6 @@ namespace sqlite_orm { // #include "../functional/mpl.h" - namespace sqlite_orm { namespace internal { /* @@ -1145,7 +1129,6 @@ namespace sqlite_orm { } // #include "tuple_helper/tuple_filter.h" - #include // std::integral_constant, std::index_sequence, std::conditional, std::declval #include // std::tuple @@ -1153,12 +1136,10 @@ namespace sqlite_orm { // #include "../functional/index_sequence_util.h" - #include // std::index_sequence, std::make_index_sequence // #include "../functional/cxx_universal.h" - namespace sqlite_orm { namespace internal { /** @@ -1188,7 +1169,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -1280,7 +1260,6 @@ namespace sqlite_orm { // #include "table_type_of.h" - namespace sqlite_orm { namespace internal { @@ -1325,7 +1304,6 @@ namespace sqlite_orm { // #include "type_printer.h" - namespace sqlite_orm { namespace internal { @@ -1768,16 +1746,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 primary_key_colums_count_t> + : std::integral_constant(sizeof...(Cs))> {}; - template + template struct flatten_primry_keys_columns { using columns_tuple = typename conc_tuple::type; }; - template - struct flatten_primry_keys_columns>: flatten_primry_keys_columns {}; + template + struct flatten_primry_keys_columns> : flatten_primry_keys_columns {}; template using is_constraint = @@ -1878,10 +1857,8 @@ namespace sqlite_orm { #include // std::shared_ptr, std::unique_ptr // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { /** @@ -1921,7 +1898,6 @@ namespace sqlite_orm { // #include "tags.h" - // #include "functional/cxx_functional_polyfill.h" #include @@ -1936,14 +1912,12 @@ namespace sqlite_orm { #endif // #include "../member_traits/member_traits.h" - #include // std::enable_if, std::is_function, std::true_type, std::false_type // #include "../functional/cxx_universal.h" // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { // SFINAE friendly trait to get a member object pointer's field type @@ -2031,7 +2005,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { namespace polyfill { @@ -2103,7 +2076,6 @@ namespace sqlite_orm { namespace polyfill = internal::polyfill; } - namespace sqlite_orm { namespace internal { struct negatable_t {}; @@ -2131,13 +2103,10 @@ namespace sqlite_orm { // #include "serialize_result_type.h" - // #include "functional/cxx_string_view.h" - // #include "cxx_core_features.h" - #if SQLITE_ORM_HAS_INCLUDE() #include #endif @@ -2162,7 +2131,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -2447,7 +2415,6 @@ namespace sqlite_orm { // #include "../constraints.h" - namespace sqlite_orm { namespace internal { @@ -2516,7 +2483,8 @@ namespace sqlite_orm { return tuple_has::value; } - constexpr static bool is_primary_key = mpl::invoke_t, constraints_type>::value; + constexpr static bool is_primary_key = + mpl::invoke_t, constraints_type>::value; constexpr bool is_generated() const { #if SQLITE_VERSION_NUMBER >= 3031000 @@ -2640,14 +2608,12 @@ namespace sqlite_orm { #endif // SQLITE_ORM_OMITS_CODECVT // #include "functional/cxx_optional.h" - // #include "functional/cxx_universal.h" // #include "functional/cxx_type_traits_polyfill.h" // #include "is_std_ptr.h" - namespace sqlite_orm { /** @@ -2812,7 +2778,6 @@ namespace sqlite_orm { // #include "is_base_of_template.h" - #include // std::true_type, std::false_type, std::declval namespace sqlite_orm { @@ -2858,7 +2823,6 @@ namespace sqlite_orm { // #include "optional_container.h" - namespace sqlite_orm { namespace internal { @@ -2893,7 +2857,6 @@ namespace sqlite_orm { // #include "serializer_context.h" - namespace sqlite_orm { namespace internal { @@ -2935,7 +2898,6 @@ namespace sqlite_orm { // #include "alias_traits.h" - #include // std::remove_const, std::is_base_of, std::is_same #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include @@ -2947,7 +2909,6 @@ namespace sqlite_orm { // #include "type_traits.h" - namespace sqlite_orm { /** @short Base class for a custom table alias, column alias or expression alias. @@ -3026,20 +2987,17 @@ namespace sqlite_orm { // #include "expression.h" - #include #include // std::enable_if #include // std::move, std::forward, std::declval // #include "functional/cxx_optional.h" - // #include "functional/cxx_universal.h" // #include "functional/cxx_type_traits_polyfill.h" // #include "tags.h" - namespace sqlite_orm { namespace internal { @@ -3124,7 +3082,6 @@ namespace sqlite_orm { // #include "column_pointer.h" - #include // std::enable_if #include // std::move @@ -3132,7 +3089,6 @@ namespace sqlite_orm { // #include "tags.h" - namespace sqlite_orm { namespace internal { /** @@ -3175,7 +3131,6 @@ namespace sqlite_orm { // #include "literal.h" - namespace sqlite_orm { namespace internal { @@ -3192,7 +3147,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -4457,7 +4411,7 @@ namespace sqlite_orm { #include // std::copy_n // #include "functional/cxx_universal.h" - // ::size_t +// ::size_t // #include "functional/cxx_type_traits_polyfill.h" // #include "type_traits.h" @@ -4466,7 +4420,6 @@ namespace sqlite_orm { // #include "tags.h" - namespace sqlite_orm { namespace internal { @@ -4802,10 +4755,8 @@ namespace sqlite_orm { // #include "ast/into.h" - // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { @@ -4824,7 +4775,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { using int64 = sqlite_int64; @@ -6925,7 +6875,6 @@ namespace sqlite_orm { #include // std::tuple, std::get, std::tuple_size // #include "functional/cxx_optional.h" - // #include "functional/cxx_universal.h" // #include "functional/cxx_type_traits_polyfill.h" @@ -6938,7 +6887,6 @@ namespace sqlite_orm { // #include "ast/where.h" - #include // std::false_type, std::true_type #include // std::move @@ -6948,7 +6896,6 @@ namespace sqlite_orm { // #include "../serialize_result_type.h" - namespace sqlite_orm { namespace internal { @@ -6996,14 +6943,12 @@ namespace sqlite_orm { // #include "ast/group_by.h" - #include // std::tuple, std::make_tuple #include // std::true_type, std::false_type #include // std::forward, std::move // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { @@ -7050,7 +6995,6 @@ namespace sqlite_orm { // #include "alias_traits.h" - namespace sqlite_orm { namespace internal { @@ -7474,7 +7418,6 @@ namespace sqlite_orm { // #include "functional/cxx_universal.h" - namespace sqlite_orm { struct table_info { @@ -7532,7 +7475,6 @@ namespace sqlite_orm { // #include "../optional_container.h" - // NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? // (Could be implemented with a normal trigger that insert or update an internal table and then retreive // the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) @@ -7856,7 +7798,6 @@ namespace sqlite_orm { // #include "xdestroy_handling.h" - #include // std::integral_constant #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include @@ -7866,7 +7807,6 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { using xdestroy_fn_t = void (*)(void*); @@ -8108,7 +8048,6 @@ namespace sqlite_orm { #endif } - namespace sqlite_orm { /** @@ -8305,7 +8244,6 @@ namespace sqlite_orm { // #include "pointer_value.h" - namespace sqlite_orm { /** @@ -8676,7 +8614,6 @@ namespace sqlite_orm { // #include "journal_mode.h" - #include // std::back_inserter #include // std::string #include // std::unique_ptr @@ -8753,7 +8690,6 @@ namespace sqlite_orm { // #include "is_std_ptr.h" - namespace sqlite_orm { /** @@ -9184,7 +9120,6 @@ namespace sqlite_orm { // #include "error_code.h" - namespace sqlite_orm { /** @@ -9380,7 +9315,6 @@ namespace sqlite_orm { // #include "../indexed_column.h" - #include // std::string #include // std::move @@ -9388,7 +9322,6 @@ namespace sqlite_orm { // #include "ast/where.h" - namespace sqlite_orm { namespace internal { @@ -9454,7 +9387,6 @@ namespace sqlite_orm { // #include "../table_type_of.h" - namespace sqlite_orm { namespace internal { @@ -9593,14 +9525,13 @@ namespace sqlite_orm { #include // std::forward, std::move // #include "../functional/cxx_universal.h" - // ::size_t +// ::size_t // #include "../functional/cxx_type_traits_polyfill.h" // #include "../functional/cxx_functional_polyfill.h" // #include "../functional/static_magic.h" - #ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED #include // std::false_type, std::true_type, std::integral_constant #endif @@ -9685,18 +9616,16 @@ 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::forward, std::move // #include "../functional/cxx_universal.h" - // ::size_t +// ::size_t // #include "../functional/cxx_type_traits_polyfill.h" // #include "../functional/cxx_functional_polyfill.h" - namespace sqlite_orm { namespace internal { @@ -9835,7 +9764,6 @@ namespace sqlite_orm { // #include "column.h" - namespace sqlite_orm { namespace internal { @@ -9857,12 +9785,14 @@ namespace sqlite_orm { 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; + 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); + static constexpr int dedicated_primary_key_columns_count = + static_cast(std::tuple_size::value); using is_without_rowid = polyfill::bool_constant; @@ -9880,7 +9810,7 @@ namespace sqlite_orm { /** * Returns foreign keys count in table definition */ - static constexpr int foreign_keys_count = + static constexpr int foreign_keys_count = #if SQLITE_VERSION_NUMBER >= 3006019 static_cast(filter_tuple_sequence_t::size()); #else @@ -10231,7 +10161,7 @@ namespace sqlite_orm { #include // std::string // #include "functional/cxx_universal.h" - // ::nullptr_t +// ::nullptr_t // #include "functional/static_magic.h" // #include "tuple_helper/tuple_filter.h" @@ -10244,7 +10174,6 @@ namespace sqlite_orm { // #include "storage_lookup.h" - #include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void #include #include // std::index_sequence, std::make_index_sequence @@ -10255,7 +10184,6 @@ namespace sqlite_orm { // #include "type_traits.h" - namespace sqlite_orm { namespace internal { @@ -10388,7 +10316,6 @@ namespace sqlite_orm { } } - // interface functions namespace sqlite_orm { namespace internal { @@ -10452,7 +10379,6 @@ namespace sqlite_orm { // #include "storage_lookup.h" - namespace sqlite_orm { namespace internal { @@ -10489,7 +10415,6 @@ namespace sqlite_orm { #include // std::for_each, std::ranges::for_each // #include "functional/cxx_optional.h" - // #include "functional/cxx_universal.h" // #include "functional/cxx_functional_polyfill.h" @@ -10530,7 +10455,6 @@ namespace sqlite_orm { // #include "column_result.h" - #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of #include // std::tuple #include // std::reference_wrapper @@ -10541,7 +10465,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_fy.h" - #include namespace sqlite_orm { @@ -10570,14 +10493,12 @@ namespace sqlite_orm { // #include "mapped_type_proxy.h" - #include // std::remove_const // #include "type_traits.h" // #include "alias_traits.h" - namespace sqlite_orm { namespace internal { @@ -10609,7 +10530,6 @@ namespace sqlite_orm { // #include "storage_traits.h" - #include // std::tuple // #include "functional/cxx_type_traits_polyfill.h" @@ -10618,12 +10538,10 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_transformer.h" - #include // std::tuple // #include "../functional/mpl.h" - namespace sqlite_orm { namespace internal { @@ -10649,7 +10567,6 @@ namespace sqlite_orm { // #include "storage_lookup.h" - namespace sqlite_orm { namespace internal { @@ -10680,7 +10597,6 @@ namespace sqlite_orm { // #include "function.h" - #include #include #include // std::string @@ -10693,7 +10609,6 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { struct arg_values; @@ -10930,7 +10845,6 @@ namespace sqlite_orm { // #include "ast/special_keywords.h" - namespace sqlite_orm { namespace internal { struct current_time_t {}; @@ -11252,7 +11166,6 @@ namespace sqlite_orm { // #include "view.h" - #include #include // std::string #include // std::forward, std::move @@ -11264,7 +11177,6 @@ namespace sqlite_orm { // #include "iterator.h" - #include #include // std::shared_ptr, std::unique_ptr, std::make_shared #include // std::decay @@ -11281,7 +11193,6 @@ namespace sqlite_orm { // #include "object_from_column_builder.h" - #include #include // std::is_member_object_pointer @@ -11289,7 +11200,6 @@ namespace sqlite_orm { // #include "row_extractor.h" - namespace sqlite_orm { namespace internal { @@ -11335,7 +11245,6 @@ namespace sqlite_orm { // #include "util.h" - namespace sqlite_orm { namespace internal { @@ -11423,7 +11332,6 @@ namespace sqlite_orm { // #include "ast_iterator.h" - #include // std::vector #include // std::reference_wrapper @@ -11443,7 +11351,6 @@ namespace sqlite_orm { // #include "prepared_statement.h" - #include #include // std::unique_ptr #include // std::iterator_traits @@ -11461,14 +11368,12 @@ namespace sqlite_orm { // #include "connection_holder.h" - #include #include #include // std::string // #include "error_code.h" - namespace sqlite_orm { namespace internal { @@ -11541,7 +11446,6 @@ namespace sqlite_orm { // #include "values.h" - #include // std::vector #include // std::tuple #include // std::forward @@ -11550,7 +11454,6 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { @@ -11591,7 +11494,6 @@ namespace sqlite_orm { // #include "ast/upsert_clause.h" - #if SQLITE_VERSION_NUMBER >= 3024000 #include // std::tuple #include // std::forward, std::move @@ -11599,7 +11501,6 @@ namespace sqlite_orm { // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { #if SQLITE_VERSION_NUMBER >= 3024000 @@ -11662,7 +11563,6 @@ namespace sqlite_orm { // #include "ast/set.h" - #include // std::tuple, std::tuple_size #include // std::string #include // std::vector @@ -11671,7 +11571,6 @@ namespace sqlite_orm { // #include "../table_name_collector.h" - #include // std::set #include // std::string #include // std::pair, std::move @@ -11690,7 +11589,6 @@ namespace sqlite_orm { // #include "storage_lookup.h" - namespace sqlite_orm { namespace internal { @@ -11780,7 +11678,6 @@ namespace sqlite_orm { } - namespace sqlite_orm { namespace internal { @@ -11880,7 +11777,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -12598,7 +12494,6 @@ namespace sqlite_orm { // #include "ast/excluded.h" - #include // std::move namespace sqlite_orm { @@ -12628,12 +12523,10 @@ namespace sqlite_orm { // #include "ast/exists.h" - #include // std::move // #include "../tags.h" - namespace sqlite_orm { namespace internal { @@ -12666,7 +12559,6 @@ namespace sqlite_orm { // #include "ast/match.h" - namespace sqlite_orm { namespace internal { @@ -12687,7 +12579,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -13377,7 +13268,6 @@ namespace sqlite_orm { // #include "util.h" - namespace sqlite_orm { namespace internal { @@ -13434,7 +13324,6 @@ namespace sqlite_orm { // #include "storage_base.h" - #include #include // std::function, std::bind #include // std::string @@ -13448,12 +13337,11 @@ namespace sqlite_orm { #include // std::find_if // #include "functional/cxx_universal.h" - // ::size_t +// ::size_t // #include "tuple_helper/tuple_iteration.h" // #include "pragma.h" - #include #include // std::string #include // std::function @@ -13473,7 +13361,6 @@ namespace sqlite_orm { // #include "serializing_util.h" - #include // std::index_sequence #include #include @@ -13482,7 +13369,7 @@ namespace sqlite_orm { #include // std::exchange, std::tuple_size // #include "functional/cxx_universal.h" - // ::size_t +// ::size_t // #include "functional/cxx_type_traits_polyfill.h" // #include "tuple_helper/tuple_iteration.h" @@ -13495,7 +13382,6 @@ namespace sqlite_orm { // #include "util.h" - namespace sqlite_orm { namespace internal { template @@ -13871,7 +13757,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -14091,7 +13976,6 @@ namespace sqlite_orm { // #include "limit_accessor.h" - #include #include // std::map #include // std::function @@ -14099,7 +13983,6 @@ namespace sqlite_orm { // #include "connection_holder.h" - namespace sqlite_orm { namespace internal { @@ -14233,13 +14116,11 @@ namespace sqlite_orm { // #include "transaction_guard.h" - #include // std::function #include // std::move // #include "connection_holder.h" - namespace sqlite_orm { namespace internal { @@ -14320,7 +14201,6 @@ namespace sqlite_orm { // #include "backup.h" - #include #include // std::system_error #include // std::string @@ -14331,7 +14211,6 @@ namespace sqlite_orm { // #include "connection_holder.h" - namespace sqlite_orm { namespace internal { @@ -14399,7 +14278,6 @@ namespace sqlite_orm { // #include "values_to_tuple.h" - #include #include // std::index_sequence, std::make_index_sequence #include // std::tuple, std::tuple_size, std::get @@ -14410,12 +14288,10 @@ namespace sqlite_orm { // #include "arg_values.h" - #include // #include "row_extractor.h" - namespace sqlite_orm { /** @short Wrapper around a dynamically typed value object. @@ -14559,7 +14435,6 @@ namespace sqlite_orm { }; } - namespace sqlite_orm { namespace internal { @@ -14604,7 +14479,6 @@ namespace sqlite_orm { // #include "serializing_util.h" - namespace sqlite_orm { namespace internal { @@ -15403,13 +15277,11 @@ namespace sqlite_orm { // #include "expression_object_type.h" - #include // std::decay #include // std::reference_wrapper // #include "prepared_statement.h" - namespace sqlite_orm { namespace internal { @@ -15543,7 +15415,6 @@ namespace sqlite_orm { // #include "statement_serializer.h" - #include // std::stringstream #include // std::string #include // std::enable_if, std::remove_pointer @@ -15559,7 +15430,6 @@ namespace sqlite_orm { // #include "functional/cxx_optional.h" - // #include "functional/cxx_universal.h" // #include "functional/cxx_functional_polyfill.h" @@ -15580,7 +15450,6 @@ namespace sqlite_orm { // #include "ast/rank.h" - namespace sqlite_orm { namespace internal { struct rank_t {}; @@ -15621,7 +15490,6 @@ namespace sqlite_orm { // #include "column_names_getter.h" - #include // std::is_base_of #include // std::string #include // std::vector @@ -15642,12 +15510,11 @@ namespace sqlite_orm { // #include "select_constraints.h" // #include "storage_lookup.h" - // pick_table +// pick_table // #include "serializer_context.h" // #include "util.h" - namespace sqlite_orm { namespace internal { @@ -15750,7 +15617,6 @@ namespace sqlite_orm { // #include "order_by_serializer.h" - #include // std::string #include // std::stringstream @@ -15844,7 +15710,6 @@ namespace sqlite_orm { // #include "util.h" - namespace sqlite_orm { namespace internal { @@ -17982,7 +17847,6 @@ namespace sqlite_orm { // #include "serializing_util.h" - namespace sqlite_orm { namespace internal { @@ -18104,11 +17968,12 @@ namespace sqlite_orm { template void assert_updatable_type() const { using Table = storage_pick_table_t; - constexpr int primaryKeyColumnsCount = Table::primary_key_columns_count + Table::dedicated_primary_key_columns_count; + 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"); - static_assert(nonPrimaryKeysColumnsCount > 0, + static_assert(primaryKeyColumnsCount > 0, "Type with no primary keys can't be updated"); + static_assert( + nonPrimaryKeysColumnsCount > 0, "Type with primary keys only can't be updated. You need at least 1 non-primary key column"); } @@ -19435,7 +19300,7 @@ namespace sqlite_orm { #include // std::get // #include "functional/cxx_universal.h" - // ::size_t +// ::size_t // #include "functional/static_magic.h" // #include "prepared_statement.h" @@ -19444,14 +19309,12 @@ namespace sqlite_orm { // #include "node_tuple.h" - #include // std::enable_if #include // std::tuple #include // std::pair #include // std::reference_wrapper // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "tuple_helper/tuple_filter.h" @@ -19482,7 +19345,6 @@ namespace sqlite_orm { // #include "ast/match.h" - namespace sqlite_orm { namespace internal { @@ -19793,7 +19655,6 @@ namespace sqlite_orm { // #include "expression_object_type.h" - namespace sqlite_orm { template @@ -19973,7 +19834,6 @@ namespace sqlite_orm { // #include "pointer_value.h" - namespace sqlite_orm { inline constexpr const char carray_pvt_name[] = "carray"; @@ -20048,7 +19908,6 @@ namespace sqlite_orm { // #include "alias.h" - namespace sqlite_orm { /** * SQLite's "schema table" that stores the schema for a database. @@ -20094,7 +19953,6 @@ namespace sqlite_orm { // #include "../schema/table.h" - namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB struct dbstat { @@ -20137,7 +19995,6 @@ namespace sqlite_orm { * this file is also used to provide definitions of interface methods 'hitting the database'. */ - #include // std::make_unique // #include "../functional/cxx_core_features.h" @@ -20154,7 +20011,6 @@ namespace sqlite_orm { // #include "../schema/column.h" - namespace sqlite_orm { namespace internal { @@ -20189,14 +20045,13 @@ namespace sqlite_orm { #include // std::find_if, std::ranges::find // #include "../functional/cxx_universal.h" - // ::size_t +// ::size_t // #include "../type_printer.h" // #include "../schema/column.h" // #include "../schema/table.h" - namespace sqlite_orm { namespace internal { @@ -20261,7 +20116,6 @@ namespace sqlite_orm { // #include "../storage.h" - namespace sqlite_orm { namespace internal { diff --git a/tests/static_tests/flatten_primary_keys_columns.cpp b/tests/static_tests/flatten_primary_keys_columns.cpp index b46b6233a..e9aa9c3b6 100644 --- a/tests/static_tests/flatten_primary_keys_columns.cpp +++ b/tests/static_tests/flatten_primary_keys_columns.cpp @@ -9,40 +9,40 @@ TEST_CASE("flatten_primry_keys_columns") { int id = 0; std::string name; }; - { // id + 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 + { // (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) + { // 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) + { // (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; diff --git a/tests/static_tests/table_static_tests.cpp b/tests/static_tests/table_static_tests.cpp index 68819f09d..5f3099665 100644 --- a/tests/static_tests/table_static_tests.cpp +++ b/tests/static_tests/table_static_tests.cpp @@ -8,78 +8,63 @@ TEST_CASE("table static columns_count") { int id = 0; std::string name; }; - { // 1 column no pk - auto table = make_table("users", - make_column("id", &User::id)); + { // 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); } - { // 1 column with 1 inline pk - auto table = make_table("users", - make_column("id", &User::id, primary_key())); + { // 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); } - { // 1 column with 1 inline pk autoincrement - auto table = make_table("users", - make_column("id", &User::id, primary_key().autoincrement())); + { // 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); } - { // 1 column with 1 dedicated pk - auto table = make_table("users", - make_column("id", &User::id), - primary_key(&User::id)); + { // 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); } - { // 2 columns no pk - auto table = make_table("users", - make_column("id", &User::id), - make_column("id", &User::name)); + { // 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); } - { // 2 columns with 1 inline id pk - auto table = make_table("users", - make_column("id", &User::id, primary_key()), - make_column("id", &User::name)); + { // 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); } - { // 2 columns with 1 inline name pk - auto table = make_table("users", - make_column("id", &User::id), - make_column("id", &User::name, primary_key())); + { // 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); } - { // 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)); + { // 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); } - { // 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)); + { // 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); } - { // 2 columns with 2 dedicated pks + { // 2 columns with 2 dedicated pks auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name), From d5f7bcc76f58fe5a767d2043d5940d0bd4ddabd0 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 21 Oct 2023 21:21:41 +0200 Subject: [PATCH 03/10] Compound select with more than two simple select statements --- dev/ast_iterator.h | 3 +- dev/column_result.h | 10 +- dev/constraints.h | 4 +- dev/functional/mpl.h | 46 +- dev/node_tuple.h | 9 +- dev/select_constraints.h | 91 ++-- dev/serializing_util.h | 21 + dev/statement_serializer.h | 4 +- dev/tuple_helper/same_or_void.h | 11 + dev/tuple_helper/tuple_iteration.h | 10 +- dev/tuple_helper/tuple_transformer.h | 16 +- include/sqlite_orm/sqlite_orm.h | 490 ++++++++++-------- .../statements/select.cpp | 25 +- tests/static_tests/compound_operators.cpp | 21 +- .../static_tests/functional/same_or_void.cpp | 6 + 15 files changed, 456 insertions(+), 311 deletions(-) diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index 8922f71c6..c09504d4a 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -241,8 +241,7 @@ namespace sqlite_orm { template void operator()(const node_type& c, L& lambda) const { - iterate_ast(c.left, lambda); - iterate_ast(c.right, lambda); + iterate_ast(c.compound, lambda); } }; diff --git a/dev/column_result.h b/dev/column_result.h index 1651776e2..255ce2d80 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -5,9 +5,11 @@ #include // std::reference_wrapper #include "functional/cxx_universal.h" +#include "functional/mpl.h" #include "tuple_helper/tuple_traits.h" #include "tuple_helper/tuple_fy.h" #include "tuple_helper/tuple_filter.h" +#include "tuple_helper/same_or_void.h" #include "type_traits.h" #include "member_traits/member_traits.h" #include "mapped_type_proxy.h" @@ -229,11 +231,11 @@ namespace sqlite_orm { template struct column_result_t> { - using left_result = column_result_of_t; - using right_result = column_result_of_t; - static_assert(std::is_same::value, + using column_result_fn = mpl::bind_front_fn; + using types_tuple = transform_tuple_t; + using type = std::tuple_element_t<0, types_tuple>; + static_assert(!std::is_same>::value, "Compound subselect queries must return same types"); - using type = left_result; }; template diff --git a/dev/constraints.h b/dev/constraints.h index 4d54c81ad..7f2c55ce5 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -291,12 +291,12 @@ namespace sqlite_orm { /** * Holds obect type of all referenced columns. */ - using target_type = typename same_or_void...>::type; + using target_type = same_or_void_t...>; /** * Holds obect type of all source columns. */ - using source_type = typename same_or_void...>::type; + using source_type = same_or_void_t...>; columns_type columns; references_type references; diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index 0a205668a..44c33dc7f 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -8,12 +8,14 @@ * such as filtering columns by constraints having various traits. * Hence it contains only a very small subset of a full MPL. * - * Two key concepts are critical to understanding: + * Three key concepts are critical to understanding: * 1. A 'metafunction' is a class template that represents a function invocable at compile-time. - * 2. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. + * E.g. `template struct x { using type = int; };` + * 2. A 'metafunction operation' is an alias template that represents a nested template expression, whose instantiation yields a type. + * E.g. `template using alias_op_t = typename x::type` + * 3. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. * More precisely, it's a class with a nested metafunction called "fn" * Correspondingly, a metafunction class invocation is defined as invocation of its nested "fn" metafunction. - * 3. A 'metafunction operation' is an alias template that represents a function whose instantiation already yields a type. * * Conventions: * - "Fn" is the name for a metafunction template template parameter. @@ -22,7 +24,7 @@ * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). */ -#include // std::false_type, std::true_type +#include // std::enable_if, std::is_same #include "cxx_universal.h" #include "cxx_type_traits_polyfill.h" @@ -54,6 +56,16 @@ namespace sqlite_orm { struct is_metafunction_class : polyfill::bool_constant> {}; #endif + /* + * Given suitable template arguments, detect whether a template is an alias template. + * + * @note It exploits the fact that the `is_specialization_of` trait is only true for class templates + * but always false for alias templates. + */ + template class Template, class... Args> + SQLITE_ORM_INLINE_VAR constexpr bool is_alias_template_v = + !polyfill::is_specialization_of_v, Template>; + /* * Invoke metafunction. */ @@ -82,6 +94,32 @@ namespace sqlite_orm { using invoke_op_t = typename wrap_op::type; #endif + template class F, class... Args> + struct invoke_meta; + + template class Op, class... Args> + struct invoke_meta>, Op, Args...> { + using type = Op; + }; + + template class Fn, class... Args> + struct invoke_meta>, Fn, Args...> { + using type = typename Fn::type; + }; + + /* + * Invoke metafunction or metafunction operation. + * + * @attention If using an alias template, be aware that it isn't recognizable whether an alias template is + * a. an alias of a class template (e.g. `template using aliased = x`) or + * b. an alias of a nested template expression yielding a result (e.g. `template alias_op_t = typename x::type`) + * Therefore, one cannot use `invoke_meta_t` with an alias of a class template (a.), or + * in other words, `invoke_meta_t` expects an alias of a nested template expression (b.). + * The alternative in that case is to use `invoke_fn_t`. + */ + template class F, class... Args> + using invoke_meta_t = typename invoke_meta::type; + /* * Invoke metafunction class by invoking its nested metafunction. */ diff --git a/dev/node_tuple.h b/dev/node_tuple.h index d75534ef3..a69d6c81e 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -143,14 +143,7 @@ namespace sqlite_orm { }; template - struct node_tuple> { - using node_type = T; - using left_type = typename node_type::left_type; - using right_type = typename node_type::right_type; - using left_tuple = node_tuple_t; - using right_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple> : node_tuple {}; template struct node_tuple, void> { diff --git a/dev/select_constraints.h b/dev/select_constraints.h index b945fc35d..02a6c15b0 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -10,6 +10,7 @@ #include "functional/cxx_type_traits_polyfill.h" #include "is_base_of_template.h" #include "tuple_helper/tuple_filter.h" +#include "tuple_helper/tuple_iteration.h" #include "optional_container.h" #include "ast/where.h" #include "ast/group_by.h" @@ -109,17 +110,16 @@ namespace sqlite_orm { /** * Base for UNION, UNION ALL, EXCEPT and INTERSECT */ - template + template struct compound_operator { - using left_type = L; - using right_type = R; + using expressions_tuple = std::tuple; - left_type left; - right_type right; + expressions_tuple compound; - compound_operator(left_type l, right_type r) : left(std::move(l)), right(std::move(r)) { - this->left.highest_level = true; - this->right.highest_level = true; + compound_operator(expressions_tuple compound) : compound{std::move(compound)} { + iterate_tuple(this->compound, [](auto& expression) { + expression.highest_level = true; + }); } }; @@ -148,15 +148,12 @@ namespace sqlite_orm { /** * UNION object type. */ - template - struct union_t : public compound_operator, union_base { - using left_type = typename compound_operator::left_type; - using right_type = typename compound_operator::right_type; + template + struct union_t : public compound_operator, union_base { + using typename compound_operator::expressions_tuple; - union_t(left_type l, right_type r, bool all_) : - compound_operator{std::move(l), std::move(r)}, union_base{all_} {} - - union_t(left_type l, right_type r) : union_t{std::move(l), std::move(r), false} {} + union_t(expressions_tuple compound, bool all) : + compound_operator{std::move(compound)}, union_base{all} {} }; struct except_string { @@ -168,11 +165,9 @@ namespace sqlite_orm { /** * EXCEPT object type. */ - template - struct except_t : compound_operator, except_string { - using super = compound_operator; - using left_type = typename super::left_type; - using right_type = typename super::right_type; + template + struct except_t : compound_operator, except_string { + using super = compound_operator; using super::super; }; @@ -185,11 +180,9 @@ namespace sqlite_orm { /** * INTERSECT object type. */ - template - struct intersect_t : compound_operator, intersect_string { - using super = compound_operator; - using left_type = typename super::left_type; - using right_type = typename super::right_type; + template + struct intersect_t : compound_operator, intersect_string { + using super = compound_operator; using super::super; }; @@ -347,37 +340,41 @@ namespace sqlite_orm { /** * Public function for UNION operator. - * lhs and rhs are subselect objects. + * Expressions are subselect objects. * Look through example in examples/union.cpp */ - template - internal::union_t union_(L lhs, R rhs) { - return {std::move(lhs), std::move(rhs)}; + template + internal::union_t union_(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + return {{std::forward(expressions)...}, false}; } /** - * Public function for EXCEPT operator. - * lhs and rhs are subselect objects. - * Look through example in examples/except.cpp + * Public function for UNION ALL operator. + * Expressions are subselect objects. + * Look through example in examples/union.cpp */ - template - internal::except_t except(L lhs, R rhs) { - return {std::move(lhs), std::move(rhs)}; - } - - template - internal::intersect_t intersect(L lhs, R rhs) { - return {std::move(lhs), std::move(rhs)}; + template + internal::union_t union_all(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + return {{std::forward(expressions)...}, true}; } /** - * Public function for UNION ALL operator. - * lhs and rhs are subselect objects. - * Look through example in examples/union.cpp + * Public function for EXCEPT operator. + * Expressions are subselect objects. + * Look through example in examples/except.cpp */ - template - internal::union_t union_all(L lhs, R rhs) { - return {std::move(lhs), std::move(rhs), true}; + template + internal::except_t except(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + return {{std::forward(expressions)...}}; + } + + template + internal::intersect_t intersect(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + return {{std::forward(expressions)...}}; } /** diff --git a/dev/serializing_util.h b/dev/serializing_util.h index 1ffb836ed..799953ef1 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -92,6 +92,7 @@ namespace sqlite_orm { actions_tuple, expressions_tuple, dynamic_expressions, + compound_expressions, serialized, identifier, identifiers, @@ -119,6 +120,7 @@ namespace sqlite_orm { constexpr streaming streaming_actions_tuple{}; constexpr streaming streaming_expressions_tuple{}; constexpr streaming streaming_dynamic_expressions{}; + constexpr streaming streaming_compound_expressions{}; constexpr streaming streaming_serialized{}; constexpr streaming streaming_identifier{}; constexpr streaming streaming_identifiers{}; @@ -172,6 +174,25 @@ namespace sqlite_orm { return ss; } + // serialize and stream expressions of a compound statement; + // separated by compound operator + template + std::ostream& + operator<<(std::ostream& ss, + std::tuple&, T, const std::string&, Ctx> tpl) { + const auto& args = get<1>(tpl); + const std::string& opString = get<2>(tpl); + auto& context = get<3>(tpl); + + iterate_tuple(args, [&ss, &opString, &context, first = true](auto& arg) mutable { + if(!std::exchange(first, false)) { + ss << ' ' << opString << ' '; + } + ss << serialize(arg, context); + }); + return ss; + } + // serialize and stream multi_order_by arguments; // comma-separated template diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 2877647b0..c927da917 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -614,9 +614,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; - ss << serialize(c.left, context) << " "; - ss << static_cast(c) << " "; - ss << serialize(c.right, context); + ss << streaming_compound_expressions(c.compound, static_cast(c), context); return ss.str(); } }; diff --git a/dev/tuple_helper/same_or_void.h b/dev/tuple_helper/same_or_void.h index 9de9b5d45..78788af73 100644 --- a/dev/tuple_helper/same_or_void.h +++ b/dev/tuple_helper/same_or_void.h @@ -21,8 +21,19 @@ namespace sqlite_orm { using type = A; }; + template + using same_or_void_t = typename same_or_void::type; + template struct same_or_void : same_or_void {}; + template + struct same_or_void_of; + + template class Pack, class... Types> + struct same_or_void_of> : same_or_void {}; + + template + using same_or_void_of_t = typename same_or_void_of::type; } } diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index a0154a927..60aa7e7ed 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -37,7 +37,7 @@ namespace sqlite_orm { #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) template - void iterate_tuple(const Tpl& tpl, std::index_sequence, L&& lambda) { + void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr(reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= int sink; @@ -48,10 +48,10 @@ namespace sqlite_orm { } #else template - void iterate_tuple(const Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} + void iterate_tuple(Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} template - void iterate_tuple(const Tpl& tpl, std::index_sequence, L&& lambda) { + void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { #ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED if constexpr(reversed) { #else @@ -66,9 +66,9 @@ namespace sqlite_orm { } #endif template - void iterate_tuple(const Tpl& tpl, L&& lambda) { + void iterate_tuple(Tpl&& tpl, L&& lambda) { iterate_tuple(tpl, - std::make_index_sequence::value>{}, + std::make_index_sequence>::value>{}, std::forward(lambda)); } diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index 9c7a73c43..7150a3627 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -1,26 +1,24 @@ #pragma once -#include // std::tuple - #include "../functional/mpl.h" namespace sqlite_orm { namespace internal { - template class Op> + 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...>; }; /* * Transform specified tuple. * - * `Op` is a metafunction operation. + * `Op` is a metafunction or metafunction operation. */ - template class Op> - using transform_tuple_t = typename tuple_transformer::type; + template class Op> + using transform_tuple_t = typename tuple_transformer::type; } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 37eb7dec4..c6df5b4bd 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -740,12 +740,14 @@ namespace sqlite_orm { * such as filtering columns by constraints having various traits. * Hence it contains only a very small subset of a full MPL. * - * Two key concepts are critical to understanding: + * Three key concepts are critical to understanding: * 1. A 'metafunction' is a class template that represents a function invocable at compile-time. - * 2. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. + * E.g. `template struct x { using type = int; };` + * 2. A 'metafunction operation' is an alias template that represents a nested template expression, whose instantiation yields a type. + * E.g. `template using alias_op_t = typename x::type` + * 3. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. * More precisely, it's a class with a nested metafunction called "fn" * Correspondingly, a metafunction class invocation is defined as invocation of its nested "fn" metafunction. - * 3. A 'metafunction operation' is an alias template that represents a function whose instantiation already yields a type. * * Conventions: * - "Fn" is the name for a metafunction template template parameter. @@ -754,7 +756,7 @@ namespace sqlite_orm { * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). */ -#include // std::false_type, std::true_type +#include // std::enable_if, std::is_same // #include "cxx_universal.h" @@ -787,6 +789,16 @@ namespace sqlite_orm { struct is_metafunction_class : polyfill::bool_constant> {}; #endif + /* + * Given suitable template arguments, detect whether a template is an alias template. + * + * @note It exploits the fact that the `is_specialization_of` trait is only true for class templates + * but always false for alias templates. + */ + template class Template, class... Args> + SQLITE_ORM_INLINE_VAR constexpr bool is_alias_template_v = + !polyfill::is_specialization_of_v, Template>; + /* * Invoke metafunction. */ @@ -815,6 +827,32 @@ namespace sqlite_orm { using invoke_op_t = typename wrap_op::type; #endif + template class F, class... Args> + struct invoke_meta; + + template class Op, class... Args> + struct invoke_meta>, Op, Args...> { + using type = Op; + }; + + template class Fn, class... Args> + struct invoke_meta>, Fn, Args...> { + using type = typename Fn::type; + }; + + /* + * Invoke metafunction or metafunction operation. + * + * @attention If using an alias template, be aware that it isn't recognizable whether an alias template is + * a. an alias of a class template (e.g. `template using aliased = x`) or + * b. an alias of a nested template expression yielding a result (e.g. `template alias_op_t = typename x::type`) + * Therefore, one cannot use `invoke_meta_t` with an alias of a class template (a.), or + * in other words, `invoke_meta_t` expects an alias of a nested template expression (b.). + * The alternative in that case is to use `invoke_fn_t`. + */ + template class F, class... Args> + using invoke_meta_t = typename invoke_meta::type; + /* * Invoke metafunction class by invoking its nested metafunction. */ @@ -1066,9 +1104,20 @@ namespace sqlite_orm { using type = A; }; + template + using same_or_void_t = typename same_or_void::type; + template struct same_or_void : same_or_void {}; + template + struct same_or_void_of; + + template class Pack, class... Types> + struct same_or_void_of> : same_or_void {}; + + template + using same_or_void_of_t = typename same_or_void_of::type; } } @@ -1577,12 +1626,12 @@ namespace sqlite_orm { /** * Holds obect type of all referenced columns. */ - using target_type = typename same_or_void...>::type; + using target_type = same_or_void_t...>; /** * Holds obect type of all source columns. */ - using source_type = typename same_or_void...>::type; + using source_type = same_or_void_t...>; columns_type columns; references_type references; @@ -6862,6 +6911,144 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_filter.h" +// #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::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 { + + // got it form here https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer + template + auto call(Function& f, FunctionPointer functionPointer, Tpl&& tpl, std::index_sequence) { + return (f.*functionPointer)(std::get(std::forward(tpl))...); + } + + template + auto call(Function& f, Tpl&& tpl, std::index_sequence) { + return f(std::get(std::forward(tpl))...); + } + + template + auto call(Function& f, FunctionPointer functionPointer, Tpl&& tpl) { + constexpr size_t size = std::tuple_size>::value; + return call(f, functionPointer, std::forward(tpl), std::make_index_sequence{}); + } + + // custom std::apply + template + auto call(Function& f, Tpl&& tpl) { + constexpr size_t size = std::tuple_size>::value; + return call(f, std::forward(tpl), std::make_index_sequence{}); + } + +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + template + void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { + if constexpr(reversed) { + // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= + int sink; + ((lambda(std::get(tpl)), sink) = ... = 0); + } else { + (lambda(std::get(tpl)), ...); + } + } +#else + template + void iterate_tuple(Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} + + template + void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr(reversed) { +#else + if(reversed) { +#endif + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + lambda(std::get(tpl)); + } else { + lambda(std::get(tpl)); + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + } + } +#endif + template + void iterate_tuple(Tpl&& tpl, L&& lambda) { + iterate_tuple(tpl, + std::make_index_sequence>::value>{}, + std::forward(lambda)); + } + +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void iterate_tuple(std::index_sequence, L&& lambda) { + (lambda((std::tuple_element_t*)nullptr), ...); + } +#else + template + void iterate_tuple(std::index_sequence<>, L&& /*lambda*/) {} + + template + void iterate_tuple(std::index_sequence, L&& lambda) { + lambda((std::tuple_element_t*)nullptr); + iterate_tuple(std::index_sequence{}, std::forward(lambda)); + } +#endif + template + void iterate_tuple(L&& lambda) { + 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 + lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} +#endif + template + decltype(auto) operator()(const Base& object) { + return L::operator()(object); + } + }; + + /* + * This method wraps the specified callable in another function object, + * which in turn implicitly casts its single argument to the specified template base class, + * then passes the converted argument to the lambda. + * + * Note: This method is useful for reducing combinatorial instantiation of template lambdas, + * as long as this library supports compilers that do not implement + * explicit template parameters in generic lambdas [SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED]. + * Unfortunately it doesn't work with user-defined conversion operators in order to extract + * parts of a class. In other words, the destination type must be a direct template base class. + */ + template class Base, class L> + lambda_as_template_base call_as_template_base(L lambda) { + return {std::move(lambda)}; + } + } +} + // #include "optional_container.h" // #include "ast/where.h" @@ -7067,17 +7254,16 @@ namespace sqlite_orm { /** * Base for UNION, UNION ALL, EXCEPT and INTERSECT */ - template + template struct compound_operator { - using left_type = L; - using right_type = R; + using expressions_tuple = std::tuple; - left_type left; - right_type right; + expressions_tuple compound; - compound_operator(left_type l, right_type r) : left(std::move(l)), right(std::move(r)) { - this->left.highest_level = true; - this->right.highest_level = true; + compound_operator(expressions_tuple compound) : compound{std::move(compound)} { + iterate_tuple(this->compound, [](auto& expression) { + expression.highest_level = true; + }); } }; @@ -7106,15 +7292,12 @@ namespace sqlite_orm { /** * UNION object type. */ - template - struct union_t : public compound_operator, union_base { - using left_type = typename compound_operator::left_type; - using right_type = typename compound_operator::right_type; + template + struct union_t : public compound_operator, union_base { + using typename compound_operator::expressions_tuple; - union_t(left_type l, right_type r, bool all_) : - compound_operator{std::move(l), std::move(r)}, union_base{all_} {} - - union_t(left_type l, right_type r) : union_t{std::move(l), std::move(r), false} {} + union_t(expressions_tuple compound, bool all) : + compound_operator{std::move(compound)}, union_base{all} {} }; struct except_string { @@ -7126,11 +7309,9 @@ namespace sqlite_orm { /** * EXCEPT object type. */ - template - struct except_t : compound_operator, except_string { - using super = compound_operator; - using left_type = typename super::left_type; - using right_type = typename super::right_type; + template + struct except_t : compound_operator, except_string { + using super = compound_operator; using super::super; }; @@ -7143,11 +7324,9 @@ namespace sqlite_orm { /** * INTERSECT object type. */ - template - struct intersect_t : compound_operator, intersect_string { - using super = compound_operator; - using left_type = typename super::left_type; - using right_type = typename super::right_type; + template + struct intersect_t : compound_operator, intersect_string { + using super = compound_operator; using super::super; }; @@ -7305,37 +7484,41 @@ namespace sqlite_orm { /** * Public function for UNION operator. - * lhs and rhs are subselect objects. + * Expressions are subselect objects. * Look through example in examples/union.cpp */ - template - internal::union_t union_(L lhs, R rhs) { - return {std::move(lhs), std::move(rhs)}; + template + internal::union_t union_(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + return {{std::forward(expressions)...}, false}; } /** - * Public function for EXCEPT operator. - * lhs and rhs are subselect objects. - * Look through example in examples/except.cpp + * Public function for UNION ALL operator. + * Expressions are subselect objects. + * Look through example in examples/union.cpp */ - template - internal::except_t except(L lhs, R rhs) { - return {std::move(lhs), std::move(rhs)}; - } - - template - internal::intersect_t intersect(L lhs, R rhs) { - return {std::move(lhs), std::move(rhs)}; + template + internal::union_t union_all(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + return {{std::forward(expressions)...}, true}; } /** - * Public function for UNION ALL operator. - * lhs and rhs are subselect objects. - * Look through example in examples/union.cpp + * Public function for EXCEPT operator. + * Expressions are subselect objects. + * Look through example in examples/except.cpp */ - template - internal::union_t union_all(L lhs, R rhs) { - return {std::move(lhs), std::move(rhs), true}; + template + internal::except_t except(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + return {{std::forward(expressions)...}}; + } + + template + internal::intersect_t intersect(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + return {{std::forward(expressions)...}}; } /** @@ -9595,142 +9778,6 @@ 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::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 { - - // got it form here https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer - template - auto call(Function& f, FunctionPointer functionPointer, Tpl&& tpl, std::index_sequence) { - return (f.*functionPointer)(std::get(std::forward(tpl))...); - } - - template - auto call(Function& f, Tpl&& tpl, std::index_sequence) { - return f(std::get(std::forward(tpl))...); - } - - template - auto call(Function& f, FunctionPointer functionPointer, Tpl&& tpl) { - constexpr size_t size = std::tuple_size>::value; - return call(f, functionPointer, std::forward(tpl), std::make_index_sequence{}); - } - - // custom std::apply - template - auto call(Function& f, Tpl&& tpl) { - constexpr size_t size = std::tuple_size>::value; - return call(f, std::forward(tpl), std::make_index_sequence{}); - } - -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) - template - void iterate_tuple(const Tpl& tpl, std::index_sequence, L&& lambda) { - if constexpr(reversed) { - // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= - int sink; - ((lambda(std::get(tpl)), sink) = ... = 0); - } else { - (lambda(std::get(tpl)), ...); - } - } -#else - template - void iterate_tuple(const Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} - - template - void iterate_tuple(const Tpl& tpl, std::index_sequence, L&& lambda) { -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED - if constexpr(reversed) { -#else - if(reversed) { -#endif - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - lambda(std::get(tpl)); - } else { - lambda(std::get(tpl)); - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - } - } -#endif - template - void iterate_tuple(const Tpl& tpl, L&& lambda) { - iterate_tuple(tpl, - std::make_index_sequence::value>{}, - std::forward(lambda)); - } - -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED - template - void iterate_tuple(std::index_sequence, L&& lambda) { - (lambda((std::tuple_element_t*)nullptr), ...); - } -#else - template - void iterate_tuple(std::index_sequence<>, L&& /*lambda*/) {} - - template - void iterate_tuple(std::index_sequence, L&& lambda) { - lambda((std::tuple_element_t*)nullptr); - iterate_tuple(std::index_sequence{}, std::forward(lambda)); - } -#endif - template - void iterate_tuple(L&& lambda) { - 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 - lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} -#endif - template - decltype(auto) operator()(const Base& object) { - return L::operator()(object); - } - }; - - /* - * This method wraps the specified callable in another function object, - * which in turn implicitly casts its single argument to the specified template base class, - * then passes the converted argument to the lambda. - * - * Note: This method is useful for reducing combinatorial instantiation of template lambdas, - * as long as this library supports compilers that do not implement - * explicit template parameters in generic lambdas [SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED]. - * Unfortunately it doesn't work with user-defined conversion operators in order to extract - * parts of a class. In other words, the destination type must be a direct template base class. - */ - template class Base, class L> - lambda_as_template_base call_as_template_base(L lambda) { - return {std::move(lambda)}; - } - } -} - // #include "../member_traits/member_traits.h" // #include "../typed_comparator.h" @@ -10441,6 +10488,8 @@ namespace sqlite_orm { // #include "functional/cxx_universal.h" +// #include "functional/mpl.h" + // #include "tuple_helper/tuple_traits.h" // #include "tuple_helper/tuple_fy.h" @@ -10467,6 +10516,8 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_filter.h" +// #include "tuple_helper/same_or_void.h" + // #include "type_traits.h" // #include "member_traits/member_traits.h" @@ -10518,28 +10569,26 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_transformer.h" -#include // std::tuple - // #include "../functional/mpl.h" namespace sqlite_orm { namespace internal { - template class Op> + 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...>; }; /* * Transform specified tuple. * - * `Op` is a metafunction operation. + * `Op` is a metafunction or metafunction operation. */ - template class Op> - using transform_tuple_t = typename tuple_transformer::type; + template class Op> + using transform_tuple_t = typename tuple_transformer::type; } } @@ -11054,11 +11103,11 @@ namespace sqlite_orm { template struct column_result_t> { - using left_result = column_result_of_t; - using right_result = column_result_of_t; - static_assert(std::is_same::value, + using column_result_fn = mpl::bind_front_fn; + using types_tuple = transform_tuple_t; + using type = std::tuple_element_t<0, types_tuple>; + static_assert(!std::is_same>::value, "Compound subselect queries must return same types"); - using type = left_result; }; template @@ -12778,8 +12827,7 @@ namespace sqlite_orm { template void operator()(const node_type& c, L& lambda) const { - iterate_ast(c.left, lambda); - iterate_ast(c.right, lambda); + iterate_ast(c.compound, lambda); } }; @@ -13439,6 +13487,7 @@ namespace sqlite_orm { actions_tuple, expressions_tuple, dynamic_expressions, + compound_expressions, serialized, identifier, identifiers, @@ -13466,6 +13515,7 @@ namespace sqlite_orm { constexpr streaming streaming_actions_tuple{}; constexpr streaming streaming_expressions_tuple{}; constexpr streaming streaming_dynamic_expressions{}; + constexpr streaming streaming_compound_expressions{}; constexpr streaming streaming_serialized{}; constexpr streaming streaming_identifier{}; constexpr streaming streaming_identifiers{}; @@ -13519,6 +13569,25 @@ namespace sqlite_orm { return ss; } + // serialize and stream expressions of a compound statement; + // separated by compound operator + template + std::ostream& + operator<<(std::ostream& ss, + std::tuple&, T, const std::string&, Ctx> tpl) { + const auto& args = get<1>(tpl); + const std::string& opString = get<2>(tpl); + auto& context = get<3>(tpl); + + iterate_tuple(args, [&ss, &opString, &context, first = true](auto& arg) mutable { + if(!std::exchange(first, false)) { + ss << ' ' << opString << ' '; + } + ss << serialize(arg, context); + }); + return ss; + } + // serialize and stream multi_order_by arguments; // comma-separated template @@ -16255,9 +16324,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; - ss << serialize(c.left, context) << " "; - ss << static_cast(c) << " "; - ss << serialize(c.right, context); + ss << streaming_compound_expressions(c.compound, static_cast(c), context); return ss.str(); } }; @@ -19431,14 +19498,7 @@ namespace sqlite_orm { }; template - struct node_tuple> { - using node_type = T; - using left_type = typename node_type::left_type; - using right_type = typename node_type::right_type; - using left_tuple = node_tuple_t; - using right_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple> : node_tuple {}; template struct node_tuple, void> { diff --git a/tests/statement_serializer_tests/statements/select.cpp b/tests/statement_serializer_tests/statements/select.cpp index bdd8f1be3..ac634367b 100644 --- a/tests/statement_serializer_tests/statements/select.cpp +++ b/tests/statement_serializer_tests/statements/select.cpp @@ -56,10 +56,27 @@ TEST_CASE("statement_serializer select_t") { expected = "SELECT ((1, 2, 3) = (4, 5, 6))"; } } - SECTION("compound operator") { - auto statement = select(union_(select(1), select(2))); - stringValue = serialize(statement, context); - expected = "SELECT 1 UNION SELECT 2"; + SECTION("compound operators") { + SECTION("union") { + auto statement = select(union_(select(1), select(2), select(3))); + stringValue = serialize(statement, context); + expected = "SELECT 1 UNION SELECT 2 UNION SELECT 3"; + } + SECTION("union all") { + auto statement = select(union_all(select(1), select(2), select(3))); + stringValue = serialize(statement, context); + expected = "SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3"; + } + SECTION("except") { + auto statement = select(except(select(1), select(2), select(3))); + stringValue = serialize(statement, context); + expected = "SELECT 1 EXCEPT SELECT 2 EXCEPT SELECT 3"; + } + SECTION("intersect") { + auto statement = select(intersect(select(1), select(2), select(3))); + stringValue = serialize(statement, context); + expected = "SELECT 1 INTERSECT SELECT 2 INTERSECT SELECT 3"; + } } SECTION("columns") { SECTION("literals") { diff --git a/tests/static_tests/compound_operators.cpp b/tests/static_tests/compound_operators.cpp index 164502e3b..b71f5c7f3 100644 --- a/tests/static_tests/compound_operators.cpp +++ b/tests/static_tests/compound_operators.cpp @@ -1,17 +1,22 @@ #include #include +#include // std::tuple_size #include "static_tests_common.h" using namespace sqlite_orm; +using internal::is_compound_operator_v; +using std::tuple_size; + +template +void runTest(Compound) { + STATIC_REQUIRE(is_compound_operator_v); + STATIC_REQUIRE(tuple_size::value == 3); +} TEST_CASE("Compound operators") { - auto unionValue = union_(select(&User::id), select(&Token::id)); - STATIC_REQUIRE(internal::is_base_of_template::value); - auto unionAllValue = union_all(select(&User::id), select(&Token::id)); - STATIC_REQUIRE(internal::is_base_of_template::value); - auto exceptValue = except(select(&User::id), select(&Token::id)); - STATIC_REQUIRE(internal::is_base_of_template::value); - auto intersectValue = intersect(select(&User::id), select(&Token::id)); - STATIC_REQUIRE(internal::is_base_of_template::value); + runTest(union_(select(&User::id), select(&User::id), select(&Token::id))); + runTest(union_all(select(&User::id), select(&User::id), select(&Token::id))); + runTest(except(select(&User::id), select(&User::id), select(&Token::id))); + runTest(intersect(select(&User::id), select(&User::id), select(&Token::id))); } diff --git a/tests/static_tests/functional/same_or_void.cpp b/tests/static_tests/functional/same_or_void.cpp index 1f58d4a1d..235861c3e 100644 --- a/tests/static_tests/functional/same_or_void.cpp +++ b/tests/static_tests/functional/same_or_void.cpp @@ -3,11 +3,13 @@ #include // std::is_same #include // std::string +#include // std::tuple using namespace sqlite_orm; TEST_CASE("same_or_void") { using internal::same_or_void; + using internal::same_or_void_of_t; // one argument STATIC_REQUIRE(std::is_same::type, int>::value); @@ -31,4 +33,8 @@ TEST_CASE("same_or_void") { STATIC_REQUIRE(std::is_same::type, int>::value); STATIC_REQUIRE(std::is_same::type, long>::value); STATIC_REQUIRE(std::is_same::type, void>::value); + + // type pack, e.g. tuple + STATIC_REQUIRE(std::is_same>, int>::value); + STATIC_REQUIRE(std::is_same>, void>::value); } From c324ad9fd2bfad57f5fd8d366a19fa8922651000 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 21 Oct 2023 23:11:50 +0200 Subject: [PATCH 04/10] Simplified partial node tuple specializations --- dev/ast_iterator.h | 6 +- dev/node_tuple.h | 175 +++-------- dev/operators.h | 8 + dev/type_traits.h | 6 + include/sqlite_orm/sqlite_orm.h | 272 +++++++----------- tests/prepared_statement_tests/update_all.cpp | 18 -- tests/static_tests/node_tuple.cpp | 36 ++- 7 files changed, 187 insertions(+), 334 deletions(-) diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index c09504d4a..047d468ce 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -160,9 +160,9 @@ namespace sqlite_orm { } }; - template - struct ast_iterator, void> { - using node_type = binary_operator; + template + struct ast_iterator> { + using node_type = T; template void operator()(const node_type& node, C& lambda) const { diff --git a/dev/node_tuple.h b/dev/node_tuple.h index a69d6c81e..deb200742 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -8,6 +8,7 @@ #include "functional/cxx_type_traits_polyfill.h" #include "tuple_helper/tuple_filter.h" +#include "type_traits.h" #include "conditions.h" #include "operators.h" #include "select_constraints.h" @@ -33,6 +34,16 @@ namespace sqlite_orm { template using node_tuple_t = typename node_tuple::type; + template + struct node_tuple_for { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple_for> { + using type = tuple_cat_t...>; + }; + template<> struct node_tuple { using type = std::tuple<>; @@ -45,30 +56,19 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> : node_tuple> {}; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using args_tuple = node_tuple_t>; - using expression_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; - template - struct node_tuple> : node_tuple {}; + template + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using x_node_tuple = node_tuple_t; - using y_node_tuple = node_tuple_t; - using z_node_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; @@ -104,105 +104,57 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple> { - using node_type = T; - using left_type = typename node_type::left_type; - using right_type = typename node_type::right_type; - using left_node_tuple = node_tuple_t; - using right_node_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple> : node_tuple_for, right_type_t> {}; - template - struct node_tuple, void> { - using node_type = binary_operator; - using left_type = typename node_type::left_type; - using right_type = typename node_type::right_type; - using left_node_tuple = node_tuple_t; - using right_node_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + template + struct node_tuple> : node_tuple_for, right_type_t> {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using left_tuple = node_tuple_t; - using right_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using left_tuple = node_tuple_t; - using right_tuple = tuple_cat_t...>; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple> : node_tuple {}; + struct node_tuple> : node_tuple_for {}; template - struct node_tuple, void> { - using columns_tuple = node_tuple_t; - using args_tuple = tuple_cat_t...>; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - struct node_tuple, Wargs...>, void> { - using set_tuple = tuple_cat_t...>; - using conditions_tuple = tuple_cat_t...>; - using type = tuple_cat_t; - }; + struct node_tuple, Wargs...>, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; @@ -214,27 +166,13 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using arg_tuple = node_tuple_t; - using pattern_tuple = node_tuple_t; - using escape_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using arg_tuple = node_tuple_t; - using pattern_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using expression_tuple = node_tuple_t; - using lower_tuple = node_tuple_t; - using upper_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; @@ -249,26 +187,16 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using left_tuple = node_tuple_t; - using right_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; @@ -291,19 +219,10 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using case_tuple = node_tuple_t; - using args_tuple = tuple_cat_t...>; - using else_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using left_tuple = node_tuple_t; - using right_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; @@ -312,13 +231,9 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using type = tuple_cat_t, node_tuple_t>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t, node_tuple_t>; - }; + struct node_tuple, void> : node_tuple_for {}; } } diff --git a/dev/operators.h b/dev/operators.h index 3b81a6ce0..22868aef6 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -3,6 +3,8 @@ #include // std::false_type, std::true_type #include // std::move +#include "functional/cxx_type_traits_polyfill.h" +#include "is_base_of_template.h" #include "tags.h" #include "serialize_result_type.h" @@ -21,6 +23,12 @@ namespace sqlite_orm { binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_of_template_v; + + template + using is_binary_operator = polyfill::bool_constant>; + struct conc_string { serialize_result_type serialize() const { return "||"; diff --git a/dev/type_traits.h b/dev/type_traits.h index 64bf1ccdb..ec253d544 100644 --- a/dev/type_traits.h +++ b/dev/type_traits.h @@ -58,6 +58,12 @@ namespace sqlite_orm { template using target_type_t = typename T::target_type; + template + using left_type_t = typename T::left_type; + + template + using right_type_t = typename T::right_type; + template using on_type_t = typename T::on_type; } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index c6df5b4bd..2f878e2b9 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -403,6 +403,12 @@ namespace sqlite_orm { template using target_type_t = typename T::target_type; + template + using left_type_t = typename T::left_type; + + template + using right_type_t = typename T::right_type; + template using on_type_t = typename T::on_type; } @@ -1930,6 +1936,47 @@ namespace sqlite_orm { #include // std::false_type, std::true_type #include // std::move +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_base_of_template.h" + +#include // std::true_type, std::false_type, std::declval + +namespace sqlite_orm { + + namespace internal { + + /* + * This is because of bug in MSVC, for more information, please visit + * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 + */ +#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Base> + struct is_base_of_template_impl { + template + static constexpr std::true_type test(const Base&); + + static constexpr std::false_type test(...); + }; + + template class C> + using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); +#else + template class C, typename... Ts> + std::true_type is_base_of_template_impl(const C&); + + template class C> + std::false_type is_base_of_template_impl(...); + + template class C> + using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); +#endif + + template class C> + SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; + } +} + // #include "tags.h" // #include "functional/cxx_functional_polyfill.h" @@ -2180,6 +2227,12 @@ namespace sqlite_orm { binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_of_template_v; + + template + using is_binary_operator = polyfill::bool_constant>; + struct conc_string { serialize_result_type serialize() const { return "||"; @@ -2806,43 +2859,6 @@ namespace sqlite_orm { // #include "is_base_of_template.h" -#include // std::true_type, std::false_type, std::declval - -namespace sqlite_orm { - - namespace internal { - - /* - * This is because of bug in MSVC, for more information, please visit - * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 - */ -#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION - template class Base> - struct is_base_of_template_impl { - template - static constexpr std::true_type test(const Base&); - - static constexpr std::false_type test(...); - }; - - template class C> - using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); -#else - template class C, typename... Ts> - std::true_type is_base_of_template_impl(const C&); - - template class C> - std::false_type is_base_of_template_impl(...); - - template class C> - using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); -#endif - - template class C> - SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; - } -} - // #include "type_traits.h" // #include "collate_argument.h" @@ -12746,9 +12762,9 @@ namespace sqlite_orm { } }; - template - struct ast_iterator, void> { - using node_type = binary_operator; + template + struct ast_iterator> { + using node_type = T; template void operator()(const node_type& node, C& lambda) const { @@ -19351,6 +19367,8 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_filter.h" +// #include "type_traits.h" + // #include "conditions.h" // #include "operators.h" @@ -19388,6 +19406,16 @@ namespace sqlite_orm { template using node_tuple_t = typename node_tuple::type; + template + struct node_tuple_for { + using type = tuple_cat_t...>; + }; + + template + struct node_tuple_for> { + using type = tuple_cat_t...>; + }; + template<> struct node_tuple { using type = std::tuple<>; @@ -19400,30 +19428,19 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> : node_tuple> {}; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using args_tuple = node_tuple_t>; - using expression_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; - template - struct node_tuple> : node_tuple {}; + template + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using x_node_tuple = node_tuple_t; - using y_node_tuple = node_tuple_t; - using z_node_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; @@ -19459,105 +19476,57 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple> { - using node_type = T; - using left_type = typename node_type::left_type; - using right_type = typename node_type::right_type; - using left_node_tuple = node_tuple_t; - using right_node_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple> : node_tuple_for, right_type_t> {}; - template - struct node_tuple, void> { - using node_type = binary_operator; - using left_type = typename node_type::left_type; - using right_type = typename node_type::right_type; - using left_node_tuple = node_tuple_t; - using right_node_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + template + struct node_tuple> : node_tuple_for, right_type_t> {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using left_tuple = node_tuple_t; - using right_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using left_tuple = node_tuple_t; - using right_tuple = tuple_cat_t...>; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple> : node_tuple {}; + struct node_tuple> : node_tuple_for {}; template - struct node_tuple, void> { - using columns_tuple = node_tuple_t; - using args_tuple = tuple_cat_t...>; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - struct node_tuple, Wargs...>, void> { - using set_tuple = tuple_cat_t...>; - using conditions_tuple = tuple_cat_t...>; - using type = tuple_cat_t; - }; + struct node_tuple, Wargs...>, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; @@ -19569,27 +19538,13 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using arg_tuple = node_tuple_t; - using pattern_tuple = node_tuple_t; - using escape_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using arg_tuple = node_tuple_t; - using pattern_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using expression_tuple = node_tuple_t; - using lower_tuple = node_tuple_t; - using upper_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; @@ -19604,26 +19559,16 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using left_tuple = node_tuple_t; - using right_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t...>; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; @@ -19646,19 +19591,10 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using case_tuple = node_tuple_t; - using args_tuple = tuple_cat_t...>; - using else_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using left_tuple = node_tuple_t; - using right_tuple = node_tuple_t; - using type = tuple_cat_t; - }; + struct node_tuple, void> : node_tuple_for {}; template struct node_tuple, void> : node_tuple {}; @@ -19667,14 +19603,10 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple {}; template - struct node_tuple, void> { - using type = tuple_cat_t, node_tuple_t>; - }; + struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> { - using type = tuple_cat_t, node_tuple_t>; - }; + struct node_tuple, void> : node_tuple_for {}; } } diff --git a/tests/prepared_statement_tests/update_all.cpp b/tests/prepared_statement_tests/update_all.cpp index cfa711777..8a9bb4d95 100644 --- a/tests/prepared_statement_tests/update_all.cpp +++ b/tests/prepared_statement_tests/update_all.cpp @@ -39,15 +39,6 @@ TEST_CASE("Prepared update all") { { // by val auto statement = storage.prepare(update_all(set(assign(&User::name, conc(&User::name, "_"))))); - using Statement = decltype(statement); - using Expression = Statement::expression_type; - using SetTuple = internal::node_tuple::set_tuple; - using SetBind = internal::bindable_filter_t; - STATIC_REQUIRE(std::tuple_size::value == 1); - { - using Arg0 = std::tuple_element_t<0, SetBind>; - STATIC_REQUIRE(std::is_same::value); - } REQUIRE(strcmp(get<0>(statement), "_") == 0); testSerializing(statement); SECTION("nothing") { @@ -105,15 +96,6 @@ TEST_CASE("Prepared update all") { { // by ref std::string str = "_"; auto statement = storage.prepare(update_all(set(assign(&User::name, conc(&User::name, std::ref(str)))))); - using Statement = decltype(statement); - using Expression = Statement::expression_type; - using SetTuple = internal::node_tuple::set_tuple; - using SetBind = internal::bindable_filter_t; - STATIC_REQUIRE(std::tuple_size::value == 1); - { - using Arg0 = std::tuple_element_t<0, SetBind>; - STATIC_REQUIRE(std::is_same::value); - } REQUIRE(get<0>(statement) == "_"); REQUIRE(&get<0>(statement) == &str); testSerializing(statement); diff --git a/tests/static_tests/node_tuple.cpp b/tests/static_tests/node_tuple.cpp index e5bd96819..08e1a9d5a 100644 --- a/tests/static_tests/node_tuple.cpp +++ b/tests/static_tests/node_tuple.cpp @@ -29,6 +29,20 @@ TEST_CASE("Node tuple") { std::string name; }; + SECTION("wrapper types") { + SECTION("reference wrapper") { + using Tuple = node_tuple_t>; + STATIC_REQUIRE(is_same>::value); + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + { + SECTION("as_optional") { + using Tuple = node_tuple_t>; + STATIC_REQUIRE(is_same>::value); + } + } +#endif + } SECTION("bindables") { SECTION("int") { using Tuple = node_tuple_t; @@ -538,19 +552,9 @@ TEST_CASE("Node tuple") { SECTION("like(&User::name, 'S%')") { auto lk = like(&User::name, "S%"); using Like = decltype(lk); - using NodeTuple = node_tuple; - using ArgTuple = NodeTuple::arg_tuple; - static_assert(is_same>::value, "arg_tuple"); - using PatternTuple = NodeTuple::pattern_tuple; - static_assert(is_same>::value, "pattern_tuple"); - using EscapeTuple = NodeTuple::escape_tuple; - static_assert(is_same>::value, "escape_tuple"); - using Tuple = NodeTuple::type; - static_assert(std::tuple_size::value == 2, "like(&User::name, \"S%\") size"); - using Tuple0 = std::tuple_element_t<0, Tuple>; - static_assert(is_same::value, "like(&User::name, \"S%\") type 0"); - static_assert(is_same>::value, - "like(&User::name, \"S%\")"); + using NodeTuple = node_tuple_t; + using Expected = tuple; + static_assert(is_same::value, "like(&User::name, \"S%\") type 0"); } SECTION("like(&User::name, std::string('pattern'), '%')") { auto lk = like(&User::name, std::string("pattern"), "%"); @@ -945,4 +949,10 @@ TEST_CASE("Node tuple") { using ExpectedTuple = tuple; STATIC_REQUIRE(std::is_same::value); } + SECTION("set assign") { + auto statement = set(assign(&User::name, conc(&User::name, "_"))); + using Tuple = node_tuple_t; + using ExpectedTuple = tuple; + STATIC_REQUIRE(std::is_same::value); + } } From a2f37cd03255ddb31b63e3595f0fedd01b6cdee5 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 24 Oct 2023 15:33:08 +0200 Subject: [PATCH 05/10] Generalize the counting of columns --- dev/column_names_getter.h | 2 +- dev/constraints.h | 60 ++-- dev/functional/index_sequence_util.h | 2 +- dev/implementations/storage_definitions.h | 2 +- dev/implementations/table_definitions.h | 2 +- dev/schema/column.h | 16 +- dev/schema/table.h | 55 ++- dev/statement_serializer.h | 2 +- dev/storage.h | 20 +- dev/storage_impl.h | 2 +- dev/tuple_helper/tuple_filter.h | 2 +- dev/tuple_helper/tuple_iteration.h | 17 +- dev/tuple_helper/tuple_transformer.h | 88 ++++- dev/type_traits.h | 3 + include/sqlite_orm/sqlite_orm.h | 321 +++++++++++------- tests/CMakeLists.txt | 1 - tests/schema/column_tests.cpp | 11 +- .../flatten_primary_keys_columns.cpp | 52 --- tests/static_tests/table_static_tests.cpp | 102 ++++-- 19 files changed, 429 insertions(+), 331 deletions(-) delete mode 100644 tests/static_tests/flatten_primary_keys_columns.cpp 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 } } From c05c6f89a84f206d8ed67c4142994a17677719e1 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 24 Oct 2023 16:34:33 +0200 Subject: [PATCH 06/10] Clang choked while accessing a variable that is actually constexpr --- dev/tuple_helper/tuple_transformer.h | 8 ++++---- include/sqlite_orm/sqlite_orm.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index 72e3248bc..2e3b723a5 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -62,11 +62,11 @@ namespace sqlite_orm { * 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 { + struct plus_fold_integrals { template - constexpr auto operator()(const Integrals&... integrals) const { + constexpr auto operator()(const Integrals&...) const { using integral_type = std::common_type_t; - return std::integral_constant{}; + return std::integral_constant{}; } }; @@ -83,7 +83,7 @@ namespace sqlite_orm { }; template class NestedProject, class Tpl, class IdxSeq> - using nested_tuple_size_for_t = decltype(recombine_tuple(plus_reduce_integrals{}, + using nested_tuple_size_for_t = decltype(recombine_tuple(plus_fold_integrals{}, std::declval(), IdxSeq{}, project_nested_tuple_size{}, diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index d7f16ed06..6e7c645e7 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -9784,11 +9784,11 @@ namespace sqlite_orm { * 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 { + struct plus_fold_integrals { template - constexpr auto operator()(const Integrals&... integrals) const { + constexpr auto operator()(const Integrals&...) const { using integral_type = std::common_type_t; - return std::integral_constant{}; + return std::integral_constant{}; } }; @@ -9805,7 +9805,7 @@ namespace sqlite_orm { }; template class NestedProject, class Tpl, class IdxSeq> - using nested_tuple_size_for_t = decltype(recombine_tuple(plus_reduce_integrals{}, + using nested_tuple_size_for_t = decltype(recombine_tuple(plus_fold_integrals{}, std::declval(), IdxSeq{}, project_nested_tuple_size{}, From e868539c9c3313d6fd678ba94a3f3fadf05ef931 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 24 Oct 2023 20:08:39 +0200 Subject: [PATCH 07/10] Update check now also works with auto-incrementable primary keys --- dev/constraints.h | 24 +++++++---------- dev/statement_serializer.h | 8 +++--- include/sqlite_orm/sqlite_orm.h | 32 ++++++++++------------- tests/static_tests/table_static_tests.cpp | 9 +++++++ 4 files changed, 37 insertions(+), 36 deletions(-) diff --git a/dev/constraints.h b/dev/constraints.h index e59bb42a9..211ac2751 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -44,12 +44,15 @@ namespace sqlite_orm { }; template - 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(T primary_key) : T{primary_key} {} +#endif }; /** @@ -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; @@ -433,16 +436,10 @@ namespace sqlite_orm { using is_foreign_key = polyfill::bool_constant>; template - struct is_primary_key : std::false_type {}; - - template - struct is_primary_key> : std::true_type {}; - - template - struct is_primary_key> : std::true_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = std::is_base_of::value; template - SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = is_primary_key::value; + using is_primary_key = polyfill::bool_constant>; template SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = @@ -477,7 +474,6 @@ namespace sqlite_orm { check_if_is_template, check_if_is_template, check_if_is_template, - check_if_is_template, check_if_is_type, check_if>, T>; diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 3dad4c474..ddaad6feb 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -907,13 +907,13 @@ namespace sqlite_orm { } }; - template - struct statement_serializer, void> { - using statement_type = primary_key_with_autoincrement; + template + struct statement_serializer, void> { + using statement_type = primary_key_with_autoincrement; template 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"; } }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 691d67cab..dc2ed46d0 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1351,12 +1351,15 @@ namespace sqlite_orm { }; template - 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(T primary_key) : T{primary_key} {} +#endif }; /** @@ -1372,7 +1375,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; @@ -1740,16 +1743,10 @@ namespace sqlite_orm { using is_foreign_key = polyfill::bool_constant>; template - struct is_primary_key : std::false_type {}; - - template - struct is_primary_key> : std::true_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = std::is_base_of::value; template - struct is_primary_key> : std::true_type {}; - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = is_primary_key::value; + using is_primary_key = polyfill::bool_constant>; template SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = @@ -1784,7 +1781,6 @@ namespace sqlite_orm { check_if_is_template, check_if_is_template, check_if_is_template, - check_if_is_template, check_if_is_type, check_if>, T>; @@ -16692,13 +16688,13 @@ namespace sqlite_orm { } }; - template - struct statement_serializer, void> { - using statement_type = primary_key_with_autoincrement; + template + struct statement_serializer, void> { + using statement_type = primary_key_with_autoincrement; template 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"; } }; diff --git a/tests/static_tests/table_static_tests.cpp b/tests/static_tests/table_static_tests.cpp index e96895de0..ff782f178 100644 --- a/tests/static_tests/table_static_tests.cpp +++ b/tests/static_tests/table_static_tests.cpp @@ -52,6 +52,15 @@ TEST_CASE("table static count_of()") { 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 + } + { // 1 column with 1 dedicated pk autoincrement + auto table = make_table("users", make_column("id", &User::id), primary_key(&User::id).autoincrement()); + 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 From 87cee49ad80b810db1950a11c993fafaba4f7df5 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 24 Oct 2023 20:23:09 +0200 Subject: [PATCH 08/10] Reused alias template type in primary key constructor again --- dev/constraints.h | 2 +- include/sqlite_orm/sqlite_orm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/constraints.h b/dev/constraints.h index 211ac2751..67c36f32e 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -51,7 +51,7 @@ namespace sqlite_orm { return *this; } #ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - primary_key_with_autoincrement(T primary_key) : T{primary_key} {} + primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} #endif }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index dc2ed46d0..eb3f56d88 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1358,7 +1358,7 @@ namespace sqlite_orm { return *this; } #ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - primary_key_with_autoincrement(T primary_key) : T{primary_key} {} + primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} #endif }; From 1fc26c2b435af6217417b25f0c04d8c181685cbe Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 24 Oct 2023 20:50:17 +0200 Subject: [PATCH 09/10] Added unit tests for invocation of "metafunction operation" --- tests/static_tests/functional/mpl.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/static_tests/functional/mpl.cpp b/tests/static_tests/functional/mpl.cpp index faceffaf4..322e34d8c 100644 --- a/tests/static_tests/functional/mpl.cpp +++ b/tests/static_tests/functional/mpl.cpp @@ -1,5 +1,8 @@ #include #include +#include +#include +#include // std::less using namespace sqlite_orm; @@ -11,7 +14,12 @@ TEST_CASE("mpl") { STATIC_REQUIRE_FALSE(mpl::is_metafunction_class_v); STATIC_REQUIRE(mpl::is_metafunction_class_v); - STATIC_REQUIRE(mpl::invoke_fn_t::value); + STATIC_REQUIRE_FALSE(mpl::is_alias_template_v); + STATIC_REQUIRE(mpl::is_alias_template_v); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, int>::value); STATIC_REQUIRE(mpl::invoke_t::value); STATIC_REQUIRE_FALSE(mpl::invoke_t, int, int>::value); STATIC_REQUIRE(mpl::invoke_t>, size_t, int>::value); From 7fa958261bcd3bcfb3af6a2b3e6927bb977c6300 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 24 Oct 2023 21:09:27 +0200 Subject: [PATCH 10/10] Turned `node_tuple_for` into an alias template --- dev/node_tuple.h | 28 +++++++++++++--------------- include/sqlite_orm/sqlite_orm.h | 28 +++++++++++++--------------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/dev/node_tuple.h b/dev/node_tuple.h index deb200742..632763372 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -34,26 +34,27 @@ namespace sqlite_orm { template using node_tuple_t = typename node_tuple::type; + /* + * Node tuple for several types. + */ template - struct node_tuple_for { - using type = tuple_cat_t...>; - }; - - template - struct node_tuple_for> { - using type = tuple_cat_t...>; - }; + using node_tuple_for = conc_tuple...>; template<> struct node_tuple { using type = std::tuple<>; }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple_for {}; + #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct node_tuple, void> : node_tuple {}; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> : node_tuple_for {}; @@ -62,7 +63,7 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> : node_tuple_for {}; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> : node_tuple_for {}; @@ -119,7 +120,7 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple> : node_tuple_for {}; + struct node_tuple> : node_tuple {}; template struct node_tuple, void> : node_tuple_for {}; @@ -136,9 +137,6 @@ namespace sqlite_orm { template struct node_tuple, void> : node_tuple_for {}; - template - struct node_tuple, void> : node_tuple_for {}; - template struct node_tuple, void> : node_tuple_for {}; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 45eafd785..c42f14a3f 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -19572,26 +19572,27 @@ namespace sqlite_orm { template using node_tuple_t = typename node_tuple::type; + /* + * Node tuple for several types. + */ template - struct node_tuple_for { - using type = tuple_cat_t...>; - }; - - template - struct node_tuple_for> { - using type = tuple_cat_t...>; - }; + using node_tuple_for = conc_tuple...>; template<> struct node_tuple { using type = std::tuple<>; }; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple_for {}; + #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct node_tuple, void> : node_tuple {}; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> : node_tuple_for {}; @@ -19600,7 +19601,7 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> : node_tuple_for {}; + struct node_tuple, void> : node_tuple {}; template struct node_tuple, void> : node_tuple_for {}; @@ -19657,7 +19658,7 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple> : node_tuple_for {}; + struct node_tuple> : node_tuple {}; template struct node_tuple, void> : node_tuple_for {}; @@ -19674,9 +19675,6 @@ namespace sqlite_orm { template struct node_tuple, void> : node_tuple_for {}; - template - struct node_tuple, void> : node_tuple_for {}; - template struct node_tuple, void> : node_tuple_for {};