diff --git a/dev/alias.h b/dev/alias.h index d5c9c689e..41fbdbd7f 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -58,7 +58,7 @@ namespace sqlite_orm { is_operator_argument_v::value>> = true; - struct basic_table; + struct table_identifier; /* * Encapsulates extracting the alias identifier of a non-alias. @@ -77,7 +77,7 @@ namespace sqlite_orm { return {}; } - template + template static const std::string& as_qualifier(const X& table) { return table.name; } @@ -114,7 +114,7 @@ namespace sqlite_orm { // for regular table aliases -> alias identifier template = true> - static std::string as_qualifier(const basic_table&) { + static std::string as_qualifier(const table_identifier&) { return alias_extractor::extract(); } }; diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index 9eac1ab02..927b5f44b 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -72,8 +72,8 @@ namespace sqlite_orm { ast_iterator iterator; // possibly invoke lambda with node itself - if constexpr (polyfill::is_invocable, const T&>::value) { - lambda(polyfill::bool_constant{}, t); + if constexpr (polyfill::is_invocable::value) { + lambda(std::true_type{}, t); } iterator(t, lambda); diff --git a/dev/column_names_getter.h b/dev/column_names_getter.h index 1200cc5dd..199f6a82a 100644 --- a/dev/column_names_getter.h +++ b/dev/column_names_getter.h @@ -51,7 +51,7 @@ namespace sqlite_orm { if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); } else if (!context.omit_table_name) { - const basic_table& table = pick_table>(context.db_objects); + const table_identifier& table = pick_table>(context.db_objects); collectedExpressions.push_back(quote_identifier(table.name) + ".*"); } else { collectedExpressions.emplace_back("*"); diff --git a/dev/constraints.h b/dev/constraints.h index e21fb7959..22e6cb7d6 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -631,6 +631,18 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { internal::table_content_t content() { return {}; } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * content='table' table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#external_content_tables + */ + template + auto content() { + return content>(); + } +#endif #endif /** diff --git a/dev/cte_storage.h b/dev/cte_storage.h index eb0d0f4d8..8f6095278 100644 --- a/dev/cte_storage.h +++ b/dev/cte_storage.h @@ -13,7 +13,7 @@ #include "table_type_of.h" #include "column_result.h" #include "select_constraints.h" -#include "schema/table.h" +#include "schema/table_base.h" #include "alias.h" #include "cte_types.h" #include "cte_column_names_collector.h" @@ -64,6 +64,21 @@ namespace sqlite_orm { FinalColRefs, Result>::type; + template + struct cte_table : table_identifier, table_definition { + using definition_type = table_definition; + using cte_mapper_type = Mapper; + using cte_moniker_type = typename cte_mapper_type::cte_moniker_type; + using object_type = cte_moniker_type; + using elements_type = typename definition_type::elements_type; + }; + + template + cte_table make_cte_table(std::string name, Cs... args) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } + // aliased column expressions, explicit or implicitly numbered template = true> auto make_cte_column(std::string name, const ColRef& /*finalColRef*/) { @@ -209,8 +224,8 @@ namespace sqlite_orm { template auto extract_colref_expressions(const DBOs& dbObjects, const asterisk_t& /*col*/) { using table_type = storage_pick_table_t; - using elements_t = typename table_type::elements_type; - using column_idxs = filter_tuple_sequence_t; + using elements_type = typename table_type::elements_type; + using column_idxs = filter_tuple_sequence_t; auto& table = pick_table(dbObjects); return get_table_columns_fields(table.elements, column_idxs{}); @@ -270,14 +285,14 @@ namespace sqlite_orm { std::vector columnNames, const ColRefs& finalColRefs, std::index_sequence) { - return make_table( + return make_cte_table( std::move(tableName), make_cte_column>(std::move(columnNames.at(CIs)), get(finalColRefs))...); } template - auto make_cte_table(const DBOs& dbObjects, const CTE& cte) { + auto make_cte_db_object(const DBOs& dbObjects, const CTE& cte) { using cte_type = CTE; auto subSelect = get_cte_driving_subselect(cte.subselect); @@ -315,7 +330,7 @@ namespace sqlite_orm { decltype(auto) make_recursive_cte_db_objects(const DBOs& dbObjects, const common_table_expressions& cte, std::index_sequence) { - auto tbl = make_cte_table(dbObjects, get(cte)); + auto tbl = make_cte_db_object(dbObjects, get(cte)); if constexpr (sizeof...(In) > 0) { return make_recursive_cte_db_objects( diff --git a/dev/cte_types.h b/dev/cte_types.h index 25efbe693..792924f47 100644 --- a/dev/cte_types.h +++ b/dev/cte_types.h @@ -27,7 +27,7 @@ namespace sqlite_orm { /** * This class captures various properties and aspects of a subselect's column expression, - * and is used as a proxy in table_t<>. + * and is used as a proxy in base_table<>. */ template // std::string -#endif -#endif - -#include "../schema/column.h" -#include "../schema/table.h" -#include "../column_pointer.h" - -SQLITE_ORM_EXPORT namespace sqlite_orm { -#ifdef SQLITE_ENABLE_DBSTAT_VTAB - struct dbstat { - std::string name; - std::string path; - int pageno = 0; - std::string pagetype; - int ncell = 0; - int payload = 0; - int unused = 0; - int mx_payload = 0; - int pgoffset = 0; - int pgsize = 0; - }; - - inline auto make_dbstat_table() { - return make_table("dbstat", - make_column("name", &dbstat::name), - make_column("path", &dbstat::path), - make_column("pageno", &dbstat::pageno), - make_column("pagetype", &dbstat::pagetype), - make_column("ncell", &dbstat::ncell), - make_column("payload", &dbstat::payload), - make_column("unused", &dbstat::unused), - make_column("mx_payload", &dbstat::mx_payload), - make_column("pgoffset", &dbstat::pgoffset), - make_column("pgsize", &dbstat::pgsize)); - } - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - inline constexpr orm_table_reference auto dbstat_table = c(); -#endif -#endif // SQLITE_ENABLE_DBSTAT_VTAB -} diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index d6ef14d55..221a3f11d 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -57,6 +57,10 @@ #define SQLITE_ORM_CLASSTYPE_TEMPLATE_ARGS_SUPPORTED #endif +#if __cpp_explicit_this_parameter >= 202110L +#define SQLITE_ORM_DEDUCING_THIS_SUPPORTED +#endif + #if __cpp_static_call_operator >= 202207L #define SQLITE_ORM_STATIC_CALL_OPERATOR_SUPPORTED #endif diff --git a/dev/implementations/column_definitions.h b/dev/implementations/column_definitions.h index 4ce4792bf..df93256a4 100644 --- a/dev/implementations/column_definitions.h +++ b/dev/implementations/column_definitions.h @@ -1,5 +1,5 @@ /** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> base_table -> column_t) * this file is also used to provide definitions of interface methods 'hitting the database'. */ #pragma once diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index 74a180141..04f9a314d 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -1,6 +1,5 @@ /** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * this file is also used to separate implementation details from the main header file, - * e.g. usage of the dbstat table. + * this file is also used to separate implementation details from the main header file. */ #pragma once @@ -14,7 +13,6 @@ #include "../type_traits.h" #include "../sqlite_schema_table.h" -#include "../eponymous_vtabs/dbstat.h" #include "../type_traits.h" #include "../util.h" #include "../serializing_util.h" @@ -23,24 +21,20 @@ namespace sqlite_orm { namespace internal { template - template> + template> sync_schema_result storage_t::sync_dbo([[maybe_unused]] const Table& table, [[maybe_unused]] sqlite3* db, [[maybe_unused]] bool preserve) { - if constexpr ( -#ifdef SQLITE_ENABLE_DBSTAT_VTAB - std::is_same, dbstat>::value || -#endif - std::is_same, sqlite_master>::value) { + if constexpr (std::is_same, sqlite_master>::value) { return sync_schema_result::already_in_sync; } else { - return this->sync_regular_table(table, db, preserve); + return this->sync_regular_base_table(table, db, preserve); } } template - template> - sync_schema_result storage_t::sync_regular_table(const Table& table, sqlite3* db, bool preserve) { + template> + sync_schema_result storage_t::sync_regular_base_table(const Table& table, sqlite3* db, bool preserve) { auto res = sync_schema_result::already_in_sync; bool attempt_to_preserve = true; diff --git a/dev/implementations/table_definitions.h b/dev/implementations/table_definitions.h index ae8dd0a59..57c7e1982 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -1,5 +1,5 @@ /** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> base_table -> column_t) * this file is also used to provide definitions of interface methods 'hitting the database'. */ #pragma once @@ -19,10 +19,10 @@ namespace sqlite_orm { namespace internal { - template - std::vector table_t::get_table_info() const { + template + std::vector base_table::get_table_info() const { std::vector res; - res.reserve(filter_tuple_sequence_t::size()); + res.reserve(col_index_sequence_of::size()); this->for_each_column([&res](auto& column) { using field_type = field_type_t>; std::string dft; diff --git a/dev/interface_definitions.h b/dev/interface_definitions.h index d8fbdbd62..8033e8cc5 100644 --- a/dev/interface_definitions.h +++ b/dev/interface_definitions.h @@ -1,5 +1,5 @@ /** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> base_table -> column_t) * this file is also used to provide definitions of interface methods 'hitting the database'. */ #pragma once diff --git a/dev/schema/table.h b/dev/schema/table.h index 07e048f92..c6b7bb6cc 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -2,147 +2,46 @@ #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string -#include // std::remove_const, std::is_member_pointer, std::true_type, std::false_type +#include // std::remove_const, std::true_type, std::false_type #include // std::vector -#include // std::tuple_element +#include // std::tuple_element, std::make_tuple, std::get #include // std::forward, std::move #endif #include "../functional/cxx_type_traits_polyfill.h" -#include "../functional/cxx_functional_polyfill.h" #include "../functional/mpl.h" -#include "../functional/index_sequence_util.h" -#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 "../tuple_helper/tuple_filter.h" #include "../member_traits/member_traits.h" #include "../field_of.h" #include "../type_traits.h" -#include "../alias_traits.h" #include "../constraints.h" #include "../table_info.h" -#include "index.h" +#include "table_base.h" #include "column.h" +#include "index.h" namespace sqlite_orm { namespace internal { template - using is_table_element_or_constraint = mpl::invoke_t, - check_if, - check_if, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template>, - T>; - -#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - /** - * A subselect mapper's CTE moniker, void otherwise. - */ - template - using moniker_of_or_void_t = polyfill::detected_or_t; + using is_base_table_element_or_constraint = mpl::invoke_t, + check_if, + check_if, + check_if_is_template, + check_if_is_template, + check_if_is_template>, + T>; /** - * If O is a subselect_mapper then returns its nested type name O::cte_moniker_type, - * otherwise O itself is a regular object type to be mapped. - */ - template - using mapped_object_type_for_t = polyfill::detected_or_t; -#endif - - struct basic_table { - - /** - * Table name. - */ - std::string name; - }; - - /** - * Table definition. + * Encapsulates base table elements, i.e. columns and constraints for a base table, + * and provides additional methods to those of a generic table definition in order to deal with foreign key and generated columns. */ - template - struct table_t : basic_table { -#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - // this typename is used in contexts where it is known that the 'table' holds a subselect_mapper - // instead of a regular object type - using cte_mapper_type = O; - using cte_moniker_type = moniker_of_or_void_t; - using object_type = mapped_object_type_for_t; -#else - using object_type = O; -#endif - using elements_type = std::tuple; - - static constexpr bool is_without_rowid_v = WithoutRowId; - - using is_without_rowid = polyfill::bool_constant; - - elements_type elements; - - table_t without_rowid() const { - return {this->name, this->elements}; - } - - /* - * Returns the number of elements of the specified type. - */ - 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. - * - * For a setter the corresponding getter has to be searched, - * so the method returns a pointer to the field as returned by the found getter. - * Otherwise the method invokes the member pointer and returns its result. - */ - template = true> - decltype(auto) object_field_value(const object_type& object, M memberPointer) const { - return polyfill::invoke(memberPointer, object); - } - - template = true> - const member_field_type_t* object_field_value(const object_type& object, M memberPointer) const { - using field_type = member_field_type_t; - const field_type* res = nullptr; - iterate_tuple(this->elements, - col_index_sequence_with_field_type{}, - call_as_template_base([&res, &memberPointer, &object](const auto& column) { - if (compare_fields(column.setter, memberPointer)) { - res = &polyfill::invoke(column.member_pointer, object); - } - })); - return res; - } + template + struct base_table_definition : insertable_table_definition { + using definition_base_type = insertable_table_definition; + using elements_type = elements_type_t; const basic_generated_always::storage_type* find_column_generated_storage_type([[maybe_unused]] const std::string& name) const { @@ -164,80 +63,6 @@ namespace sqlite_orm { return result; } - /** - * Call passed lambda with all defined primary keys. - */ - template - void for_each_primary_key(L&& lambda) const { - using pk_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->elements, pk_index_sequence{}, lambda); - } - - std::vector composite_key_columns_names() const { - std::vector res; - this->for_each_primary_key([this, &res](auto& primaryKey) { - res = this->composite_key_columns_names(primaryKey); - }); - return res; - } - - std::vector primary_key_column_names() const { - using pkcol_index_sequence = col_index_sequence_with; - - if constexpr (pkcol_index_sequence::size() > 0) { - return create_from_tuple>(this->elements, - pkcol_index_sequence{}, - &column_identifier::name); - } else { - return this->composite_key_columns_names(); - } - } - - template - void for_each_primary_key_column(L&& lambda) const { - iterate_tuple(this->elements, - col_index_sequence_with{}, - call_as_template_base([&lambda](const auto& column) { - lambda(column.member_pointer); - })); - this->for_each_primary_key([&lambda](auto& primaryKey) { - iterate_tuple(primaryKey.columns, lambda); - }); - } - - template - std::vector composite_key_columns_names(const primary_key_t& primaryKey) const { - return create_from_tuple>(primaryKey.columns, - [this, empty = std::string{}](auto& memberPointer) { - if (const std::string* columnName = - this->find_column_name(memberPointer)) { - return *columnName; - } else { - return empty; - } - }); - } - - /** - * Searches column name by class member pointer passed as the first argument. - * @return column name or empty string if nothing found. - */ - template = true> - const std::string* find_column_name(M memberPointer) const { - using field_type = member_field_type_t; - - const std::string* res = nullptr; - iterate_tuple(this->elements, - col_index_sequence_with_field_type{}, - [&res, memberPointer](auto& column) { - if (compare_fields(column.member_pointer, memberPointer) || - compare_fields(column.setter, memberPointer)) { - res = &column.name; - } - }); - return res; - } - /** * Call passed lambda with all defined foreign keys. * @param lambda Lambda called for each column. Function signature: `void(auto& column)` @@ -257,237 +82,82 @@ namespace sqlite_orm { fk_index_sequence>; iterate_tuple(this->elements, filtered_index_sequence{}, lambda); } + }; - /** - * Call passed lambda with all defined columns. - * @param lambda Lambda called for each column. Function signature: `void(auto& column)` - */ - template - void for_each_column(L&& lambda) const { - iterate_tuple(this->elements, col_index_sequence_of{}, lambda); - } + /** + * Represents a base table, i.e. a table that actually stores data (as opposed to views and other virtual tables). + */ + template + struct base_table : table_identifier, + base_table_definition, +#ifdef SQLITE_ORM_DEDUCING_THIS_SUPPORTED + mapped_object_mixin +#else + mapped_object_mixin> +#endif + { + using definition_type = base_table_definition; + using object_type = O; + using elements_type = typename definition_type::elements_type; + using is_without_rowid = WithoutRowId; - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template class OpTraitFn, class L> - void for_each_column_excluding(L&& lambda) const { - iterate_tuple(this->elements, col_index_sequence_excluding{}, lambda); + base_table without_rowid() const& { + return {this->name, this->elements}; } - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template = true> - void for_each_column_excluding(L&& lambda) const { - this->template for_each_column_excluding(lambda); + base_table without_rowid() && { + return {std::move(this->name), std::move(this->elements)}; } std::vector get_table_info() const; }; template - struct is_table : std::false_type {}; - - template - struct is_table> : std::true_type {}; - - template - struct virtual_table_t : basic_table { - using module_details_type = M; - using object_type = typename module_details_type::object_type; - using elements_type = typename module_details_type::columns_type; - - static constexpr bool is_without_rowid_v = false; - using is_without_rowid = polyfill::bool_constant; - - module_details_type module_details; - - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template class OpTraitFn, class L> - void for_each_column_excluding(L&& lambda) const { - this->module_details.template for_each_column_excluding(lambda); - } - - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template = true> - void for_each_column_excluding(L&& lambda) const { - this->module_details.template for_each_column_excluding(lambda); - } - - /** - * Call passed lambda with all defined columns. - * @param lambda Lambda called for each column. Function signature: `void(auto& column)` - */ - template - void for_each_column(L&& lambda) const { - this->module_details.for_each_column(lambda); - } - - template = true> - const std::string* find_column_name(MP memberPointer) const { - return this->module_details.find_column_name(memberPointer); - } - }; + inline constexpr bool is_base_table_v = polyfill::is_specialization_of_v; template - struct is_virtual_table : std::false_type {}; - - template - struct is_virtual_table> : std::true_type {}; - -#if SQLITE_VERSION_NUMBER >= 3009000 - template - struct using_fts5_t { - using object_type = T; - using columns_type = std::tuple; - - columns_type columns; - - using_fts5_t(columns_type columns) : columns(std::move(columns)) {} - - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template class OpTraitFn, class L> - void for_each_column_excluding(L&& lambda) const { - iterate_tuple(this->columns, col_index_sequence_excluding{}, lambda); - } - - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template = true> - void for_each_column_excluding(L&& lambda) const { - this->template for_each_column_excluding(lambda); - } - - /** - * Call passed lambda with all defined columns. - * @param lambda Lambda called for each column. Function signature: `void(auto& column)` - */ - template - void for_each_column(L&& lambda) const { - iterate_tuple(this->columns, col_index_sequence_of{}, lambda); - } - - template = true> - const std::string* find_column_name(M memberPointer) const { - using field_type = member_field_type_t; - - const std::string* res = nullptr; - iterate_tuple(this->columns, - col_index_sequence_with_field_type{}, - [&res, memberPointer](auto& column) { - if (compare_fields(column.member_pointer, memberPointer) || - compare_fields(column.setter, memberPointer)) { - res = &column.name; - } - }); - return res; - } - }; -#endif - - template - bool exists_in_composite_primary_key(const table_t& table, - const column_field& column) { - bool res = false; - table.for_each_primary_key([&column, &res](auto& primaryKey) { - using colrefs_tuple = decltype(primaryKey.columns); - using same_type_index_sequence = - filter_tuple_sequence_t>::template fn, - member_field_type_t>; - iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { - if (compare_fields(memberPointer, column.member_pointer) || - compare_fields(memberPointer, column.setter)) { - res = true; - } - }); - }); - return res; - } - - template - bool exists_in_composite_primary_key(const virtual_table_t& /*virtualTable*/, - const column_field& /*column*/) { - return false; - } + using is_base_table = polyfill::bool_constant>; } } SQLITE_ORM_EXPORT namespace sqlite_orm { -#if SQLITE_VERSION_NUMBER >= 3009000 - template>::object_type> - internal::using_fts5_t using_fts5(Cs... columns) { - static_assert(polyfill::conjunction_v...>, - "Incorrect table elements or constraints"); - - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); - } - - template - internal::using_fts5_t using_fts5(Cs... columns) { - static_assert(polyfill::conjunction_v...>, - "Incorrect table elements or constraints"); - - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); - } -#endif - /** - * Factory function for a table definition. + * Factory function for a base table. * * The mapped object type is determined implicitly from the first column definition. */ template>::object_type> - internal::table_t make_table(std::string name, Cs... args) { - static_assert(polyfill::conjunction_v...>, + internal::base_table make_table(std::string name, Cs... definition) { + static_assert(polyfill::conjunction_v...>, "Incorrect table elements or constraints"); SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), std::make_tuple(std::forward(args)...)}); + return {std::move(name), std::make_tuple(std::forward(definition)...)}); } /** - * Factory function for a table definition. + * Factory function for a base table. * * The mapped object type is explicitly specified. */ template - internal::table_t make_table(std::string name, Cs... args) { - static_assert(polyfill::conjunction_v...>, + internal::base_table make_table(std::string name, Cs... definition) { + static_assert(polyfill::conjunction_v...>, "Incorrect table elements or constraints"); SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), std::make_tuple(std::forward(args)...)}); + return {std::move(name), std::make_tuple(std::forward(definition)...)}); } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Factory function for a table definition. + * Factory function for a base table. * * The mapped object type is explicitly specified. */ template - auto make_table(std::string name, Cs... args) { - return make_table>(std::move(name), std::forward(args)...); + auto make_table(std::string name, Cs... definition) { + return make_table>(std::move(name), std::forward(definition)...); } #endif - - template - internal::virtual_table_t make_virtual_table(std::string name, M module_details) { - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(module_details)}); - } } diff --git a/dev/schema/table_base.h b/dev/schema/table_base.h new file mode 100644 index 000000000..e40455b55 --- /dev/null +++ b/dev/schema/table_base.h @@ -0,0 +1,246 @@ +#pragma once + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // std::is_member_pointer +#include // std::string +#include // std::tuple +#endif + +#include "../functional/cxx_functional_polyfill.h" +#include "../functional/mpl.h" +#include "../tuple_helper/tuple_filter.h" +#include "../tuple_helper/tuple_iteration.h" +#include "../tuple_helper/tuple_transformer.h" +#include "../member_traits/member_traits.h" +#include "../type_traits.h" +#include "../field_of.h" +#include "column.h" + +namespace sqlite_orm::internal { + + struct table_identifier { + + /** + * Table name. + */ + std::string name; + }; + + /** + * Encapsulates table elements, i.e. columns and constraints for any type of table. + */ + template + struct table_definition { + using elements_type = std::tuple; + + elements_type elements; + + /* + * Returns the number of elements of the specified type. + */ + 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 col_index_sequence = col_index_sequence_with; + return int(col_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()); + } + + /** + * Call passed lambda with all defined columns. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_column(L&& lambda) const { + iterate_tuple(this->elements, col_index_sequence_of{}, lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template class OpTraitFn, class L> + void for_each_column_excluding(L&& lambda) const { + iterate_tuple(this->elements, col_index_sequence_excluding{}, lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template = true> + void for_each_column_excluding(L&& lambda) const { + this->template for_each_column_excluding(lambda); + } + + /** + * Finds the column name by the given class member pointer. + * @return column name or nullptr if nothing found. + */ + template = true> + const std::string* find_column_name(M memberPointer) const { + using field_type = member_field_type_t; + + const std::string* res = nullptr; + iterate_tuple(this->elements, + col_index_sequence_with_field_type{}, + [&res, memberPointer](auto& column) { + if (compare_fields(column.member_pointer, memberPointer) || + compare_fields(column.setter, memberPointer)) { + res = &column.name; + } + }); + return res; + } + }; + + /** + * Encapsulates table elements, i.e. columns and constraints for a type of table that can have primary keys - base tables and usually virtual tables -, + * and provides additional methods to those of a generic table definition in order to deal with primary key columns. + */ + template + struct insertable_table_definition : table_definition { + using definition_base_type = table_definition; + using elements_type = elements_type_t; + + /** + * Call passed lambda with all defined primary keys. + */ + template + void for_each_primary_key(L&& lambda) const { + using pk_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, pk_index_sequence{}, lambda); + } + + std::vector composite_key_columns_names() const { + std::vector res; + this->for_each_primary_key([this, &res](auto& primaryKey) { + res = this->composite_key_columns_names(primaryKey); + }); + return res; + } + + std::vector primary_key_column_names() const { + using pkcol_index_sequence = col_index_sequence_with; + + if constexpr (pkcol_index_sequence::size() > 0) { + return create_from_tuple>(this->elements, + pkcol_index_sequence{}, + &column_identifier::name); + } else { + return this->composite_key_columns_names(); + } + } + + template + void for_each_primary_key_column(L&& lambda) const { + iterate_tuple(this->elements, + col_index_sequence_with{}, + call_as_template_base([&lambda](const auto& column) { + lambda(column.member_pointer); + })); + this->for_each_primary_key([&lambda](auto& primaryKey) { + iterate_tuple(primaryKey.columns, lambda); + }); + } + + template + std::vector composite_key_columns_names(const primary_key_t& primaryKey) const { + return create_from_tuple>(primaryKey.columns, + [this, empty = std::string{}](auto& memberPointer) { + if (const std::string* columnName = + this->find_column_name(memberPointer)) { + return *columnName; + } else { + return empty; + } + }); + } + }; + + template + bool exists_in_composite_primary_key(const insertable_table_definition& definition, + const column_field& column) { + bool res = false; + definition.for_each_primary_key([&column, &res](auto& primaryKey) { + using colrefs_tuple = decltype(primaryKey.columns); + using same_type_index_sequence = + filter_tuple_sequence_t>::template fn, + member_field_type_t>; + iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { + if (compare_fields(memberPointer, column.member_pointer) || + compare_fields(memberPointer, column.setter)) { + res = true; + } + }); + }); + return res; + } + + /** + * Mixin for a base table, providing methods used to access a mapped object's members. + * + * Implementation note: it is provided as a mixin to reduce the number of involved template parameters, + * which is possible in C++23 mode for 'getters'. + */ +#ifdef SQLITE_ORM_DEDUCING_THIS_SUPPORTED + template +#else + template +#endif + struct mapped_object_mixin { + using object_type = O; + + /** + * Function used to get field value from object by mapped member pointer/setter/getter. + * + * For a setter the corresponding getter has to be searched, + * so the method returns a pointer to the field as returned by the found getter. + * Otherwise the method invokes the member pointer and returns its result. + */ + template = true> + decltype(auto) object_field_value(const object_type& object, M memberPointer) const { + return polyfill::invoke(memberPointer, object); + } + + template = true> + const member_field_type_t* +#ifdef SQLITE_ORM_DEDUCING_THIS_SUPPORTED + object_field_value(this const table_definition& self, const object_type& object, M memberPointer) { + using elements_type = elements_type_t>; +#else + object_field_value(const object_type& object, M memberPointer) const { + using elements_type = elements_type_t; + auto& self = static_cast(*this); + +#endif + using field_type = member_field_type_t; + const field_type* res = nullptr; + iterate_tuple(self.elements, + col_index_sequence_with_field_type{}, + call_as_template_base([&res, &memberPointer, &object](const auto& column) { + if (compare_fields(column.setter, memberPointer)) { + res = &polyfill::invoke(column.member_pointer, object); + } + })); + return res; + } + }; +} diff --git a/dev/schema/triggers.h b/dev/schema/triggers.h index 2e6ecebd9..0ceb4738d 100644 --- a/dev/schema/triggers.h +++ b/dev/schema/triggers.h @@ -46,7 +46,7 @@ namespace sqlite_orm { } }; - struct base_trigger { + struct trigger_base { /** * Name of the trigger */ @@ -59,7 +59,7 @@ namespace sqlite_orm { * S is the list of trigger statments */ template - struct trigger_t : base_trigger { + struct trigger_t : trigger_base { using object_type = void; using elements_type = typename partial_trigger_t::statements_type; diff --git a/dev/schema/virtual_table.h b/dev/schema/virtual_table.h new file mode 100644 index 000000000..4f4582f85 --- /dev/null +++ b/dev/schema/virtual_table.h @@ -0,0 +1,151 @@ +#pragma once + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include // std::convertible_to +#endif +#include // std::string +#include // std::tuple_element, std::make_tuple +#include // std::forward, std::move +#endif + +#include "../functional/cxx_type_traits_polyfill.h" +#include "../functional/gsl.h" +#include "../functional/mpl.h" +#include "../type_traits.h" +#include "../constraints.h" +#include "table_base.h" +#include "column.h" + +namespace sqlite_orm::internal { +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept module_tag = requires { + typename T::module_type; + { T::name() } -> std::convertible_to; + }; +#endif + + /** + * Default base traits of a "normal" virtual table module. + * + * Particularly this means: + * - It is not eponymous. + * The definition of eponymous virtual tables is built-in, fixed and implicit, + * and they can only be created with optional table-values for their hidden columns. + * - It is not a WITHOUT ROWID table (i.e. it has an implicit `rowid` column). + * - Omits the column type in the SQL creation statement. + * + * Specific virtual table modules can specialize this struct to provide their own traits. + */ + template + struct virtual_table_module_traits { + using module_type = M; + using is_eponymous = std::false_type; + using is_without_rowid = std::false_type; + using omit_column_type = std::true_type; + }; + + /** + * Default traits of a "normal" virtual table. + * + * Particularly this means : + * - Its definition is a `insertable_table_definition`. + * + * Specific virtual table modules can specialize this struct to provide their own traits. + */ + template + struct virtual_table_traits : virtual_table_module_traits { + using definition_type = insertable_table_definition; + using elements_type = elements_type_t; + }; + + /** + * Encapsulates the intermediary (and temporary) (and deprecated) `using_module(...)` expression. + */ + template + struct virtual_table_description : virtual_table_traits::definition_type { +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + static_assert(module_tag, "Template parameter M must be a module tag"); +#endif + }; + + /** + * Encapsulates the intermediary (and temporary) `using_module(...)` expression. + * + * Implementation note: When making the virtual table this virtual table definition is unpacked into the virtual table type itself. + * If desired or necessary one day, derive `virtual_table` from it, similar to `base_table` deriving from `base_table_definition`. + */ + template + struct virtual_table_definition : virtual_table_traits::definition_type { +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + static_assert(module_tag, "Template parameter M must be a module tag"); +#endif + }; + + /** + * Represents an SQLite virtual table. + */ + template + struct virtual_table : table_identifier, virtual_table_traits::definition_type { + using traits_type = virtual_table_traits; + using module_traits_type = virtual_table_module_traits; + using module_type = M; + using object_type = O; + using elements_type = typename traits_type::elements_type; + using is_without_rowid = typename traits_type::is_without_rowid; + }; + + template + inline constexpr bool is_virtual_table_v = polyfill::is_specialization_of_v; + + template + using is_virtual_table = polyfill::bool_constant>; +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Factory function for a virtual table. + * + * [Deprecation notice] This factory function is deprecated and will be removed in v1.11. + */ + template + internal::virtual_table + make_virtual_table(std::string name, internal::virtual_table_description description) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(description)}); + } + + /** + * Factory function for a virtual table. + * + * The mapped object type is determined implicitly from the first column definition. + */ + template>::object_type> + internal::virtual_table make_virtual_table(std::string name, + internal::virtual_table_definition definition) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(definition)}); + } + + /** + * Factory function for a virtual table. + * + * The mapped object type is explicitly specified. + */ + template + internal::virtual_table make_virtual_table(std::string name, + internal::virtual_table_definition definition) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(definition)}); + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Factory function for a virtual table. + * + * The mapped object type is explicitly specified. + */ + template + auto make_virtual_table(std::string name, internal::virtual_table_definition definition) { + return make_virtual_table>(std::move(name), std::move(definition)); + } +#endif +} diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index c443e3d2a..aa7e6a725 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -57,6 +57,7 @@ #include "schema/column.h" #include "schema/index.h" #include "schema/table.h" +#include "schema/virtual_table.h" namespace sqlite_orm { @@ -149,9 +150,9 @@ namespace sqlite_orm { #endif }; - template - struct statement_serializer, void> { - using statement_type = table_t; + template + struct statement_serializer>> { + using statement_type = Table; template SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, @@ -165,13 +166,54 @@ namespace sqlite_orm { std::stringstream ss; ss << "CREATE TABLE " << streaming_identifier(tableName) << " (" << streaming_expressions_tuple(statement.elements, context) << ")"; - if constexpr (statement_type::is_without_rowid_v) { + if constexpr (statement_type::is_without_rowid::value) { ss << " WITHOUT ROWID"; } return ss.str(); } }; + // Eponymous virtual tables serialize only table values. Their definition is built-in, fixed and implicit + template = true> + std::string serialize_virtual_table_definition(const Definition&, const Ctx&) { + return {}; + } + + template = true> + std::string serialize_virtual_table_definition(const Elements& elements, const Ctx& context) { + using traits_type = ModTraits; + + auto subContext = context; + subContext.omit_column_type = traits_type::omit_column_type::value; + + std::stringstream ss; + ss << "(" << streaming_expressions_tuple(elements, subContext) << ")"; + return ss.str(); + } + + template + struct statement_serializer>> { + using statement_type = Table; + + template + SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, + const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { + std::stringstream ss; + ss << "CREATE VIRTUAL TABLE IF NOT EXISTS " << streaming_identifier(statement.name) << " USING " + << streaming_identifier(statement_type::module_type::name()) + << serialize_virtual_table_definition( + statement.elements, + context); + return ss.str(); + } + }; + template<> struct statement_serializer { using statement_type = current_time_t; @@ -1950,39 +1992,6 @@ namespace sqlite_orm { } }; -#if SQLITE_VERSION_NUMBER >= 3009000 - template - struct statement_serializer, void> { - using statement_type = using_fts5_t; - - template - SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, - const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { - std::stringstream ss; - ss << "USING FTS5("; - auto subContext = context; - subContext.omit_column_type = true; - ss << streaming_expressions_tuple(statement.columns, subContext) << ")"; - return ss.str(); - } - }; -#endif - - template - struct statement_serializer, void> { - using statement_type = virtual_table_t; - - template - SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, - const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { - std::stringstream ss; - ss << "CREATE VIRTUAL TABLE IF NOT EXISTS "; - ss << streaming_identifier(statement.name) << ' '; - ss << serialize(statement.module_details, context); - return ss.str(); - } - }; - template struct statement_serializer, void> { using statement_type = index_t; diff --git a/dev/storage.h b/dev/storage.h index aac24309e..17d3b08d7 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -54,12 +54,13 @@ #include "expression_object_type.h" #include "statement_serializer.h" #include "serializer_context.h" -#include "schema/triggers.h" #include "object_from_column_builder.h" #include "row_extractor.h" #include "schema/table.h" +#include "schema/virtual_table.h" #include "schema/column.h" #include "schema/index.h" +#include "schema/triggers.h" #include "cte_storage.h" #include "util.h" #include "serializing_util.h" @@ -1086,8 +1087,8 @@ namespace sqlite_orm { } protected: - template - sync_schema_result schema_status(const virtual_table_t&, sqlite3*, bool, bool*) { + template = true> + sync_schema_result schema_status(const Table&, sqlite3*, bool, bool*) { return sync_schema_result::already_in_sync; } @@ -1101,11 +1102,9 @@ namespace sqlite_orm { return sync_schema_result::already_in_sync; } - template - sync_schema_result schema_status(const table_t& table, - sqlite3* db, - bool preserve, - bool* attempt_to_preserve) { + template = true> + sync_schema_result + schema_status(const Table& table, sqlite3* db, bool preserve, bool* attempt_to_preserve) { if (attempt_to_preserve) { *attempt_to_preserve = true; } @@ -1188,12 +1187,17 @@ namespace sqlite_orm { return res; } - template - sync_schema_result sync_dbo(const virtual_table_t& virtualTable, sqlite3* db, bool) { - using context_t = serializer_context; + template = true> + sync_schema_result sync_dbo(const Table& virtualTable, sqlite3* db, bool) { + // eponymous virtual table instances with the same name as their module exist already + if constexpr (Table::module_traits_type::is_eponymous::value) { + if (virtualTable.name == Table::module_type::name()) { + return sync_schema_result::already_in_sync; + } + } const auto res = sync_schema_result::already_in_sync; - context_t context{this->db_objects}; + const serializer_context context{this->db_objects}; const auto sql = serialize(virtualTable, context); this->executor.perform_void_exec(db, sql.c_str()); return res; @@ -1201,10 +1205,8 @@ namespace sqlite_orm { template sync_schema_result sync_dbo(const index_t& index, sqlite3* db, bool) { - using context_t = serializer_context; - const auto res = sync_schema_result::already_in_sync; - context_t context{this->db_objects}; + const serializer_context context{this->db_objects}; const auto sql = serialize(index, context); this->executor.perform_void_exec(db, sql.c_str()); return res; @@ -1212,20 +1214,18 @@ namespace sqlite_orm { template sync_schema_result sync_dbo(const trigger_t& trigger, sqlite3* db, bool) { - using context_t = serializer_context; - const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly - context_t context{this->db_objects}; + const serializer_context context{this->db_objects}; const auto sql = serialize(trigger, context); this->executor.perform_void_exec(db, sql.c_str()); return res; } - template = true> + template = true> sync_schema_result sync_dbo(const Table& table, sqlite3* db, bool preserve); - template = true> - sync_schema_result sync_regular_table(const Table& table, sqlite3* db, bool preserve); + template = true> + sync_schema_result sync_regular_base_table(const Table& table, sqlite3* db, bool preserve); template void add_column(sqlite3* db, const std::string& tableName, const C& column) const { diff --git a/dev/storage_impl.h b/dev/storage_impl.h index e35c7a8d9..38c8f7f96 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -20,7 +20,7 @@ namespace sqlite_orm { namespace internal { template - using tables_index_sequence = filter_tuple_sequence_t; + using tables_index_sequence = filter_tuple_sequence_t; template = true> constexpr int foreign_keys_count() { @@ -64,7 +64,7 @@ namespace sqlite_orm { * Materialize column pointer: * 3. by moniker and alias_holder<>. * - * internal note: there's an overload for `find_column_name()` that avoids going through `table_t<>::find_column_name()` + * internal note: there's an overload for `find_column_name()` that avoids going through `cte_table<>::find_column_name()` */ template = true> constexpr decltype(auto) materialize_column_pointer(const DBOs&, @@ -111,9 +111,9 @@ namespace sqlite_orm { static_assert(colalias_index::value < std::tuple_size_v, "No such column mapped into the CTE."); - // note: we could "materialize" the alias to an `aliased_field<>::*` and use the regular `table_t<>::find_column_name()` mechanism; + // note: we could "materialize" the alias to an `aliased_field<>::*` and use the regular `cte_table<>::find_column_name()` mechanism; // however we have the column index already. - // lookup column in table_t<>'s elements + // lookup column in base_table<>'s elements constexpr size_t ColIdx = index_sequence_value_at(column_index_sequence{}); auto& table = pick_table(dboObjects); return &std::get(table.elements).name; diff --git a/dev/storage_lookup.h b/dev/storage_lookup.h index 6b9fa48bd..2c08bad20 100644 --- a/dev/storage_lookup.h +++ b/dev/storage_lookup.h @@ -2,7 +2,7 @@ #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void -#include +#include // std::tuple_size, std::get #include // std::index_sequence, std::make_index_sequence #endif @@ -18,10 +18,6 @@ namespace sqlite_orm { template using db_objects_tuple = std::tuple; - struct basic_table; - struct index_base; - struct base_trigger; - template struct is_storage : std::false_type {}; @@ -43,7 +39,7 @@ namespace sqlite_orm { /** * `std::true_type` if given object is mapped, `std::false_type` otherwise. * - * Note: unlike table_t<>, index_t<>::object_type and trigger_t<>::object_type is always void. + * Note: unlike base_table<>, index_t<>::object_type and trigger_t<>::object_type is always void. */ template struct object_type_matches : polyfill::conjunction>>, @@ -66,7 +62,7 @@ namespace sqlite_orm { struct enable_found_table : std::enable_if::value, DBO> {}; /** - * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * SFINAE friendly facility to pick a table definition (`base_table`) from a tuple of database objects. * * Lookup - mapped data type * Seq - index sequence matching the number of DBOs @@ -80,7 +76,7 @@ namespace sqlite_orm { : enable_found_table... {}; /** - * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * SFINAE friendly facility to pick a table definition (`base_table`) from a tuple of database objects. * * Lookup - 'table' type, mapped data type * DBOs - db_objects_tuple type, possibly const-qualified @@ -91,7 +87,7 @@ namespace sqlite_orm { std::remove_const_t>::type; /** - * Find a table definition (`table_t`) from a tuple of database objects; + * Find a table definition (`base_table`) from a tuple of database objects; * `std::nonesuch` if not found. * * DBOs - db_objects_tuple type @@ -101,7 +97,7 @@ namespace sqlite_orm { struct storage_find_table : polyfill::detected {}; /** - * Find a table definition (`table_t`) from a tuple of database objects; + * Find a table definition (`base_table`) from a tuple of database objects; * `std::nonesuch` if not found. * * DBOs - db_objects_tuple type, possibly const-qualified diff --git a/dev/table_name_collector.h b/dev/table_name_collector.h index 33452b939..c29356b6b 100644 --- a/dev/table_name_collector.h +++ b/dev/table_name_collector.h @@ -88,7 +88,7 @@ namespace sqlite_orm { } template - void operator()(polyfill::bool_constant, const highlight_t&) { + void operator()(std::true_type, const highlight_t&) { this->table_names.emplace(lookup_table_name(this->db_objects), ""); } }; diff --git a/dev/udf_existence_checker.h b/dev/udf_existence_checker.h index bef774fbf..4f7667c68 100644 --- a/dev/udf_existence_checker.h +++ b/dev/udf_existence_checker.h @@ -27,7 +27,7 @@ namespace sqlite_orm::internal { // examine `function_call` node expressions template - void operator()(polyfill::bool_constant, const function_call& udfCall) const { + void operator()(std::true_type, const function_call& udfCall) const { auto&& name = udfCall.name(); SQLITE_ORM_CPP_UNLIKELY { if (!_contains(_scalarFunctions, name) && !_contains(_aggregateFunctions, name)) @@ -36,7 +36,7 @@ namespace sqlite_orm::internal { } // examine `named_collate` node expressions - void operator()(polyfill::bool_constant, const named_collate_base& collateCall) const { + void operator()(std::true_type, const named_collate_base& collateCall) const { if (_collatingFunctions.find(collateCall.name) == _collatingFunctions.end()) SQLITE_ORM_CPP_UNLIKELY { #if SQLITE_VERSION_NUMBER >= 3008008 throw std::system_error{sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ), std::string(collateCall.name)}; diff --git a/dev/vtabs/dbstat.h b/dev/vtabs/dbstat.h new file mode 100644 index 000000000..742ab0cc0 --- /dev/null +++ b/dev/vtabs/dbstat.h @@ -0,0 +1,92 @@ +#pragma once + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#ifdef SQLITE_ENABLE_DBSTAT_VTAB +#include // std::false_type, std::true_type +#include // std::make_tuple +#include // std::move +#endif +#endif + +#include "../functional/gsl.h" +#include "../schema/virtual_table.h" +#include "../schema/column.h" +#include "../column_pointer.h" + +#ifdef SQLITE_ENABLE_DBSTAT_VTAB +namespace sqlite_orm::internal { + struct dbstat_module_tag { + // simplify conceptual/meta programming + using module_type = dbstat_module_tag; + + static constexpr orm_gsl::czstring name() { + return "dbstat"; + } + }; + + template<> + struct virtual_table_module_traits { + using module_type = dbstat_module_tag; + using is_eponymous = std::true_type; + using is_without_rowid = std::false_type; + using omit_column_type = std::true_type; + }; + + template + struct virtual_table_traits : virtual_table_module_traits { + using definition_type = table_definition; + using elements_type = typename definition_type::elements_type; + }; + + template + inline virtual_table_definition using_dbstat(Cs... columns) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {{std::make_tuple(std::move(columns)...)}}); + } +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + struct dbstat { + std::string name; + std::string path; + int pageno = 0; + std::string pagetype; + int ncell = 0; + int payload = 0; + int unused = 0; + int mx_payload = 0; + int pgoffset = 0; + int pgsize = 0; + }; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + inline constexpr orm_table_reference auto dbstat_table = c(); +#endif + + /** + * Factory function for a DBSTAT virtual table definition. + * If no schema is specified then the main schema is used. + * + * Though the DBSTAT virtual table is an eponymous table SQLite allows to create a virtual table instance with a different name. + * This is mostly useful with binding input arguments, e.g. a different schema than "main", which is yet unimplemented. + */ + inline auto using_dbstat() { + return internal::using_dbstat(make_column("name", &dbstat::name), + make_column("path", &dbstat::path), + make_column("pageno", &dbstat::pageno), + make_column("pagetype", &dbstat::pagetype), + make_column("ncell", &dbstat::ncell), + make_column("payload", &dbstat::payload), + make_column("unused", &dbstat::unused), + make_column("mx_payload", &dbstat::mx_payload), + make_column("pgoffset", &dbstat::pgoffset), + make_column("pgsize", &dbstat::pgsize)); + } + + /** + * Factory function for the DBSTAT default eponymous virtual table. + */ + inline auto make_dbstat_table() { + return make_virtual_table(internal::dbstat_module_tag::name(), using_dbstat()); + } +} +#endif // SQLITE_ENABLE_DBSTAT_VTAB diff --git a/dev/vtabs/fts5.h b/dev/vtabs/fts5.h new file mode 100644 index 000000000..59bf21d6c --- /dev/null +++ b/dev/vtabs/fts5.h @@ -0,0 +1,66 @@ +#pragma once + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#if SQLITE_VERSION_NUMBER >= 3009000 +#include // std::make_tuple +#include // std::forward +#endif +#endif + +#include "../functional/cxx_type_traits_polyfill.h" +#include "../functional/gsl.h" +#include "../functional/mpl.h" +#include "../schema/virtual_table.h" +#include "../schema/column.h" +#include "../constraints.h" + +#if SQLITE_VERSION_NUMBER >= 3009000 +namespace sqlite_orm::internal { + template + using is_fts5_table_element_or_constraint = mpl::invoke_t, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template>, + T>; + struct fts5_module_tag { + // simplify conceptual/meta programming + using module_type = fts5_module_tag; + + static constexpr orm_gsl::czstring name() { + return "fts5"; + } + }; +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Factory function for a FTS5 virtual table definition. + * + * The mapped object type will be determined implicitly from the first column definition when calling `make_virtual_table()`. + */ + template + internal::virtual_table_definition using_fts5(Cs... definition) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(definition)...)}); + } + + /** + * Factory function for a FTS5 virtual table definition. + * + * The mapped object type is explicitly specified. + * + * [Deprecation notice] This factory function is deprecated and will be removed in v1.11. + */ + template + [[deprecated("Specify the explicit object type when calling `make_virtual_table()`.")]] + internal::virtual_table_description using_fts5(Cs... definition) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(definition)...)}); + } +} +#endif diff --git a/dev/vtabs/vtabs.h b/dev/vtabs/vtabs.h new file mode 100644 index 000000000..fb7c8884b --- /dev/null +++ b/dev/vtabs/vtabs.h @@ -0,0 +1,4 @@ +#pragma once + +#include "dbstat.h" +#include "fts5.h" diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 2ed7e0fb0..3eb06ad4a 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -111,6 +111,10 @@ using std::nullptr_t; #define SQLITE_ORM_CLASSTYPE_TEMPLATE_ARGS_SUPPORTED #endif +#if __cpp_explicit_this_parameter >= 202110L +#define SQLITE_ORM_DEDUCING_THIS_SUPPORTED +#endif + #if __cpp_static_call_operator >= 202207L #define SQLITE_ORM_STATIC_CALL_OPERATOR_SUPPORTED #endif @@ -2531,7 +2535,7 @@ namespace sqlite_orm { is_operator_argument_v::value>> = true; - struct basic_table; + struct table_identifier; /* * Encapsulates extracting the alias identifier of a non-alias. @@ -2550,7 +2554,7 @@ namespace sqlite_orm { return {}; } - template + template static const std::string& as_qualifier(const X& table) { return table.name; } @@ -2587,7 +2591,7 @@ namespace sqlite_orm { // for regular table aliases -> alias identifier template = true> - static std::string as_qualifier(const basic_table&) { + static std::string as_qualifier(const table_identifier&) { return alias_extractor::extract(); } }; @@ -3976,6 +3980,18 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { internal::table_content_t content() { return {}; } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * content='table' table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#external_content_tables + */ + template + auto content() { + return content>(); + } +#endif #endif /** @@ -10790,7 +10806,7 @@ namespace sqlite_orm { /** * This class captures various properties and aspects of a subselect's column expression, - * and is used as a proxy in table_t<>. + * and is used as a proxy in base_table<>. */ template // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void -#include +#include // std::tuple_size, std::get #include // std::index_sequence, std::make_index_sequence #endif @@ -10857,10 +10873,6 @@ namespace sqlite_orm { template using db_objects_tuple = std::tuple; - struct basic_table; - struct index_base; - struct base_trigger; - template struct is_storage : std::false_type {}; @@ -10882,7 +10894,7 @@ namespace sqlite_orm { /** * `std::true_type` if given object is mapped, `std::false_type` otherwise. * - * Note: unlike table_t<>, index_t<>::object_type and trigger_t<>::object_type is always void. + * Note: unlike base_table<>, index_t<>::object_type and trigger_t<>::object_type is always void. */ template struct object_type_matches : polyfill::conjunction>>, @@ -10905,7 +10917,7 @@ namespace sqlite_orm { struct enable_found_table : std::enable_if::value, DBO> {}; /** - * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * SFINAE friendly facility to pick a table definition (`base_table`) from a tuple of database objects. * * Lookup - mapped data type * Seq - index sequence matching the number of DBOs @@ -10919,7 +10931,7 @@ namespace sqlite_orm { : enable_found_table... {}; /** - * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * SFINAE friendly facility to pick a table definition (`base_table`) from a tuple of database objects. * * Lookup - 'table' type, mapped data type * DBOs - db_objects_tuple type, possibly const-qualified @@ -10930,7 +10942,7 @@ namespace sqlite_orm { std::remove_const_t>::type; /** - * Find a table definition (`table_t`) from a tuple of database objects; + * Find a table definition (`base_table`) from a tuple of database objects; * `std::nonesuch` if not found. * * DBOs - db_objects_tuple type @@ -10940,7 +10952,7 @@ namespace sqlite_orm { struct storage_find_table : polyfill::detected {}; /** - * Find a table definition (`table_t`) from a tuple of database objects; + * Find a table definition (`base_table`) from a tuple of database objects; * `std::nonesuch` if not found. * * DBOs - db_objects_tuple type, possibly const-qualified @@ -12245,23 +12257,43 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string -#include // std::remove_const, std::is_member_pointer, std::true_type, std::false_type +#include // std::remove_const, std::true_type, std::false_type #include // std::vector -#include // std::tuple_element +#include // std::tuple_element, std::make_tuple, std::get #include // std::forward, std::move #endif // #include "../functional/cxx_type_traits_polyfill.h" -// #include "../functional/cxx_functional_polyfill.h" - // #include "../functional/mpl.h" -// #include "../functional/index_sequence_util.h" +// #include "../tuple_helper/tuple_iteration.h" // #include "../tuple_helper/tuple_filter.h" -// #include "../tuple_helper/tuple_traits.h" +// #include "../member_traits/member_traits.h" + +// #include "../field_of.h" + +// #include "../type_traits.h" + +// #include "../constraints.h" + +// #include "../table_info.h" + +// #include "table_base.h" + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // std::is_member_pointer +#include // std::string +#include // std::tuple +#endif + +// #include "../functional/cxx_functional_polyfill.h" + +// #include "../functional/mpl.h" + +// #include "../tuple_helper/tuple_filter.h" // #include "../tuple_helper/tuple_iteration.h" @@ -12269,15 +12301,242 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../member_traits/member_traits.h" +// #include "../type_traits.h" + // #include "../field_of.h" -// #include "../type_traits.h" +// #include "column.h" -// #include "../alias_traits.h" +namespace sqlite_orm::internal { -// #include "../constraints.h" + struct table_identifier { -// #include "../table_info.h" + /** + * Table name. + */ + std::string name; + }; + + /** + * Encapsulates table elements, i.e. columns and constraints for any type of table. + */ + template + struct table_definition { + using elements_type = std::tuple; + + elements_type elements; + + /* + * Returns the number of elements of the specified type. + */ + 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 col_index_sequence = col_index_sequence_with; + return int(col_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()); + } + + /** + * Call passed lambda with all defined columns. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_column(L&& lambda) const { + iterate_tuple(this->elements, col_index_sequence_of{}, lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template class OpTraitFn, class L> + void for_each_column_excluding(L&& lambda) const { + iterate_tuple(this->elements, col_index_sequence_excluding{}, lambda); + } + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template = true> + void for_each_column_excluding(L&& lambda) const { + this->template for_each_column_excluding(lambda); + } + + /** + * Finds the column name by the given class member pointer. + * @return column name or nullptr if nothing found. + */ + template = true> + const std::string* find_column_name(M memberPointer) const { + using field_type = member_field_type_t; + + const std::string* res = nullptr; + iterate_tuple(this->elements, + col_index_sequence_with_field_type{}, + [&res, memberPointer](auto& column) { + if (compare_fields(column.member_pointer, memberPointer) || + compare_fields(column.setter, memberPointer)) { + res = &column.name; + } + }); + return res; + } + }; + + /** + * Encapsulates table elements, i.e. columns and constraints for a type of table that can have primary keys - base tables and usually virtual tables -, + * and provides additional methods to those of a generic table definition in order to deal with primary key columns. + */ + template + struct insertable_table_definition : table_definition { + using definition_base_type = table_definition; + using elements_type = elements_type_t; + + /** + * Call passed lambda with all defined primary keys. + */ + template + void for_each_primary_key(L&& lambda) const { + using pk_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, pk_index_sequence{}, lambda); + } + + std::vector composite_key_columns_names() const { + std::vector res; + this->for_each_primary_key([this, &res](auto& primaryKey) { + res = this->composite_key_columns_names(primaryKey); + }); + return res; + } + + std::vector primary_key_column_names() const { + using pkcol_index_sequence = col_index_sequence_with; + + if constexpr (pkcol_index_sequence::size() > 0) { + return create_from_tuple>(this->elements, + pkcol_index_sequence{}, + &column_identifier::name); + } else { + return this->composite_key_columns_names(); + } + } + + template + void for_each_primary_key_column(L&& lambda) const { + iterate_tuple(this->elements, + col_index_sequence_with{}, + call_as_template_base([&lambda](const auto& column) { + lambda(column.member_pointer); + })); + this->for_each_primary_key([&lambda](auto& primaryKey) { + iterate_tuple(primaryKey.columns, lambda); + }); + } + + template + std::vector composite_key_columns_names(const primary_key_t& primaryKey) const { + return create_from_tuple>(primaryKey.columns, + [this, empty = std::string{}](auto& memberPointer) { + if (const std::string* columnName = + this->find_column_name(memberPointer)) { + return *columnName; + } else { + return empty; + } + }); + } + }; + + template + bool exists_in_composite_primary_key(const insertable_table_definition& definition, + const column_field& column) { + bool res = false; + definition.for_each_primary_key([&column, &res](auto& primaryKey) { + using colrefs_tuple = decltype(primaryKey.columns); + using same_type_index_sequence = + filter_tuple_sequence_t>::template fn, + member_field_type_t>; + iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { + if (compare_fields(memberPointer, column.member_pointer) || + compare_fields(memberPointer, column.setter)) { + res = true; + } + }); + }); + return res; + } + + /** + * Mixin for a base table, providing methods used to access a mapped object's members. + * + * Implementation note: it is provided as a mixin to reduce the number of involved template parameters, + * which is possible in C++23 mode for 'getters'. + */ +#ifdef SQLITE_ORM_DEDUCING_THIS_SUPPORTED + template +#else + template +#endif + struct mapped_object_mixin { + using object_type = O; + + /** + * Function used to get field value from object by mapped member pointer/setter/getter. + * + * For a setter the corresponding getter has to be searched, + * so the method returns a pointer to the field as returned by the found getter. + * Otherwise the method invokes the member pointer and returns its result. + */ + template = true> + decltype(auto) object_field_value(const object_type& object, M memberPointer) const { + return polyfill::invoke(memberPointer, object); + } + + template = true> + const member_field_type_t* +#ifdef SQLITE_ORM_DEDUCING_THIS_SUPPORTED + object_field_value(this const table_definition& self, const object_type& object, M memberPointer) { + using elements_type = elements_type_t>; +#else + object_field_value(const object_type& object, M memberPointer) const { + using elements_type = elements_type_t; + auto& self = static_cast(*this); + +#endif + using field_type = member_field_type_t; + const field_type* res = nullptr; + iterate_tuple(self.elements, + col_index_sequence_with_field_type{}, + call_as_template_base([&res, &memberPointer, &object](const auto& column) { + if (compare_fields(column.setter, memberPointer)) { + res = &polyfill::invoke(column.member_pointer, object); + } + })); + return res; + } + }; +} + +// #include "column.h" // #include "index.h" @@ -12413,126 +12672,27 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } -// #include "column.h" - namespace sqlite_orm { namespace internal { template - using is_table_element_or_constraint = mpl::invoke_t, - check_if, - check_if, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template>, - T>; - -#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - /** - * A subselect mapper's CTE moniker, void otherwise. - */ - template - using moniker_of_or_void_t = polyfill::detected_or_t; + using is_base_table_element_or_constraint = mpl::invoke_t, + check_if, + check_if, + check_if_is_template, + check_if_is_template, + check_if_is_template>, + T>; /** - * If O is a subselect_mapper then returns its nested type name O::cte_moniker_type, - * otherwise O itself is a regular object type to be mapped. - */ - template - using mapped_object_type_for_t = polyfill::detected_or_t; -#endif - - struct basic_table { - - /** - * Table name. - */ - std::string name; - }; - - /** - * Table definition. + * Encapsulates base table elements, i.e. columns and constraints for a base table, + * and provides additional methods to those of a generic table definition in order to deal with foreign key and generated columns. */ - template - struct table_t : basic_table { -#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - // this typename is used in contexts where it is known that the 'table' holds a subselect_mapper - // instead of a regular object type - using cte_mapper_type = O; - using cte_moniker_type = moniker_of_or_void_t; - using object_type = mapped_object_type_for_t; -#else - using object_type = O; -#endif - using elements_type = std::tuple; - - static constexpr bool is_without_rowid_v = WithoutRowId; - - using is_without_rowid = polyfill::bool_constant; - - elements_type elements; - - table_t without_rowid() const { - return {this->name, this->elements}; - } - - /* - * Returns the number of elements of the specified type. - */ - 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. - * - * For a setter the corresponding getter has to be searched, - * so the method returns a pointer to the field as returned by the found getter. - * Otherwise the method invokes the member pointer and returns its result. - */ - template = true> - decltype(auto) object_field_value(const object_type& object, M memberPointer) const { - return polyfill::invoke(memberPointer, object); - } - - template = true> - const member_field_type_t* object_field_value(const object_type& object, M memberPointer) const { - using field_type = member_field_type_t; - const field_type* res = nullptr; - iterate_tuple(this->elements, - col_index_sequence_with_field_type{}, - call_as_template_base([&res, &memberPointer, &object](const auto& column) { - if (compare_fields(column.setter, memberPointer)) { - res = &polyfill::invoke(column.member_pointer, object); - } - })); - return res; - } + template + struct base_table_definition : insertable_table_definition { + using definition_base_type = insertable_table_definition; + using elements_type = elements_type_t; const basic_generated_always::storage_type* find_column_generated_storage_type([[maybe_unused]] const std::string& name) const { @@ -12554,80 +12714,6 @@ namespace sqlite_orm { return result; } - /** - * Call passed lambda with all defined primary keys. - */ - template - void for_each_primary_key(L&& lambda) const { - using pk_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->elements, pk_index_sequence{}, lambda); - } - - std::vector composite_key_columns_names() const { - std::vector res; - this->for_each_primary_key([this, &res](auto& primaryKey) { - res = this->composite_key_columns_names(primaryKey); - }); - return res; - } - - std::vector primary_key_column_names() const { - using pkcol_index_sequence = col_index_sequence_with; - - if constexpr (pkcol_index_sequence::size() > 0) { - return create_from_tuple>(this->elements, - pkcol_index_sequence{}, - &column_identifier::name); - } else { - return this->composite_key_columns_names(); - } - } - - template - void for_each_primary_key_column(L&& lambda) const { - iterate_tuple(this->elements, - col_index_sequence_with{}, - call_as_template_base([&lambda](const auto& column) { - lambda(column.member_pointer); - })); - this->for_each_primary_key([&lambda](auto& primaryKey) { - iterate_tuple(primaryKey.columns, lambda); - }); - } - - template - std::vector composite_key_columns_names(const primary_key_t& primaryKey) const { - return create_from_tuple>(primaryKey.columns, - [this, empty = std::string{}](auto& memberPointer) { - if (const std::string* columnName = - this->find_column_name(memberPointer)) { - return *columnName; - } else { - return empty; - } - }); - } - - /** - * Searches column name by class member pointer passed as the first argument. - * @return column name or empty string if nothing found. - */ - template = true> - const std::string* find_column_name(M memberPointer) const { - using field_type = member_field_type_t; - - const std::string* res = nullptr; - iterate_tuple(this->elements, - col_index_sequence_with_field_type{}, - [&res, memberPointer](auto& column) { - if (compare_fields(column.member_pointer, memberPointer) || - compare_fields(column.setter, memberPointer)) { - res = &column.name; - } - }); - return res; - } - /** * Call passed lambda with all defined foreign keys. * @param lambda Lambda called for each column. Function signature: `void(auto& column)` @@ -12647,239 +12733,84 @@ namespace sqlite_orm { fk_index_sequence>; iterate_tuple(this->elements, filtered_index_sequence{}, lambda); } + }; - /** - * Call passed lambda with all defined columns. - * @param lambda Lambda called for each column. Function signature: `void(auto& column)` - */ - template - void for_each_column(L&& lambda) const { - iterate_tuple(this->elements, col_index_sequence_of{}, lambda); - } + /** + * Represents a base table, i.e. a table that actually stores data (as opposed to views and other virtual tables). + */ + template + struct base_table : table_identifier, + base_table_definition, +#ifdef SQLITE_ORM_DEDUCING_THIS_SUPPORTED + mapped_object_mixin +#else + mapped_object_mixin> +#endif + { + using definition_type = base_table_definition; + using object_type = O; + using elements_type = typename definition_type::elements_type; + using is_without_rowid = WithoutRowId; - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template class OpTraitFn, class L> - void for_each_column_excluding(L&& lambda) const { - iterate_tuple(this->elements, col_index_sequence_excluding{}, lambda); + base_table without_rowid() const& { + return {this->name, this->elements}; } - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template = true> - void for_each_column_excluding(L&& lambda) const { - this->template for_each_column_excluding(lambda); + base_table without_rowid() && { + return {std::move(this->name), std::move(this->elements)}; } std::vector get_table_info() const; }; template - struct is_table : std::false_type {}; - - template - struct is_table> : std::true_type {}; + inline constexpr bool is_base_table_v = polyfill::is_specialization_of_v; - template - struct virtual_table_t : basic_table { - using module_details_type = M; - using object_type = typename module_details_type::object_type; - using elements_type = typename module_details_type::columns_type; - - static constexpr bool is_without_rowid_v = false; - using is_without_rowid = polyfill::bool_constant; + template + using is_base_table = polyfill::bool_constant>; + } +} - module_details_type module_details; +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Factory function for a base table. + * + * The mapped object type is determined implicitly from the first column definition. + */ + template>::object_type> + internal::base_table make_table(std::string name, Cs... definition) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template class OpTraitFn, class L> - void for_each_column_excluding(L&& lambda) const { - this->module_details.template for_each_column_excluding(lambda); - } - - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template = true> - void for_each_column_excluding(L&& lambda) const { - this->module_details.template for_each_column_excluding(lambda); - } - - /** - * Call passed lambda with all defined columns. - * @param lambda Lambda called for each column. Function signature: `void(auto& column)` - */ - template - void for_each_column(L&& lambda) const { - this->module_details.for_each_column(lambda); - } - - template = true> - const std::string* find_column_name(MP memberPointer) const { - return this->module_details.find_column_name(memberPointer); - } - }; - - template - struct is_virtual_table : std::false_type {}; - - template - struct is_virtual_table> : std::true_type {}; - -#if SQLITE_VERSION_NUMBER >= 3009000 - template - struct using_fts5_t { - using object_type = T; - using columns_type = std::tuple; - - columns_type columns; - - using_fts5_t(columns_type columns) : columns(std::move(columns)) {} - - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template class OpTraitFn, class L> - void for_each_column_excluding(L&& lambda) const { - iterate_tuple(this->columns, col_index_sequence_excluding{}, lambda); - } - - /** - * Call passed lambda with columns not having the specified constraint trait `OpTrait`. - * @param lambda Lambda called for each column. - */ - template = true> - void for_each_column_excluding(L&& lambda) const { - this->template for_each_column_excluding(lambda); - } - - /** - * Call passed lambda with all defined columns. - * @param lambda Lambda called for each column. Function signature: `void(auto& column)` - */ - template - void for_each_column(L&& lambda) const { - iterate_tuple(this->columns, col_index_sequence_of{}, lambda); - } - - template = true> - const std::string* find_column_name(M memberPointer) const { - using field_type = member_field_type_t; - - const std::string* res = nullptr; - iterate_tuple(this->columns, - col_index_sequence_with_field_type{}, - [&res, memberPointer](auto& column) { - if (compare_fields(column.member_pointer, memberPointer) || - compare_fields(column.setter, memberPointer)) { - res = &column.name; - } - }); - return res; - } - }; -#endif - - template - bool exists_in_composite_primary_key(const table_t& table, - const column_field& column) { - bool res = false; - table.for_each_primary_key([&column, &res](auto& primaryKey) { - using colrefs_tuple = decltype(primaryKey.columns); - using same_type_index_sequence = - filter_tuple_sequence_t>::template fn, - member_field_type_t>; - iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { - if (compare_fields(memberPointer, column.member_pointer) || - compare_fields(memberPointer, column.setter)) { - res = true; - } - }); - }); - return res; - } - - template - bool exists_in_composite_primary_key(const virtual_table_t& /*virtualTable*/, - const column_field& /*column*/) { - return false; - } - } -} - -SQLITE_ORM_EXPORT namespace sqlite_orm { -#if SQLITE_VERSION_NUMBER >= 3009000 - template>::object_type> - internal::using_fts5_t using_fts5(Cs... columns) { - static_assert(polyfill::conjunction_v...>, - "Incorrect table elements or constraints"); - - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); - } - - template - internal::using_fts5_t using_fts5(Cs... columns) { - static_assert(polyfill::conjunction_v...>, - "Incorrect table elements or constraints"); - - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); - } -#endif + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(definition)...)}); + } /** - * Factory function for a table definition. - * - * The mapped object type is determined implicitly from the first column definition. - */ - template>::object_type> - internal::table_t make_table(std::string name, Cs... args) { - static_assert(polyfill::conjunction_v...>, - "Incorrect table elements or constraints"); - - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), std::make_tuple(std::forward(args)...)}); - } - - /** - * Factory function for a table definition. + * Factory function for a base table. * * The mapped object type is explicitly specified. */ template - internal::table_t make_table(std::string name, Cs... args) { - static_assert(polyfill::conjunction_v...>, + internal::base_table make_table(std::string name, Cs... definition) { + static_assert(polyfill::conjunction_v...>, "Incorrect table elements or constraints"); SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), std::make_tuple(std::forward(args)...)}); + return {std::move(name), std::make_tuple(std::forward(definition)...)}); } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Factory function for a table definition. + * Factory function for a base table. * * The mapped object type is explicitly specified. */ template - auto make_table(std::string name, Cs... args) { - return make_table>(std::move(name), std::forward(args)...); + auto make_table(std::string name, Cs... definition) { + return make_table>(std::move(name), std::forward(definition)...); } #endif - - template - internal::virtual_table_t make_virtual_table(std::string name, M module_details) { - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(module_details)}); - } } // #include "storage_lookup.h" @@ -12889,7 +12820,7 @@ namespace sqlite_orm { namespace internal { template - using tables_index_sequence = filter_tuple_sequence_t; + using tables_index_sequence = filter_tuple_sequence_t; template = true> constexpr int foreign_keys_count() { @@ -12933,7 +12864,7 @@ namespace sqlite_orm { * Materialize column pointer: * 3. by moniker and alias_holder<>. * - * internal note: there's an overload for `find_column_name()` that avoids going through `table_t<>::find_column_name()` + * internal note: there's an overload for `find_column_name()` that avoids going through `cte_table<>::find_column_name()` */ template = true> constexpr decltype(auto) materialize_column_pointer(const DBOs&, @@ -12980,9 +12911,9 @@ namespace sqlite_orm { static_assert(colalias_index::value < std::tuple_size_v, "No such column mapped into the CTE."); - // note: we could "materialize" the alias to an `aliased_field<>::*` and use the regular `table_t<>::find_column_name()` mechanism; + // note: we could "materialize" the alias to an `aliased_field<>::*` and use the regular `cte_table<>::find_column_name()` mechanism; // however we have the column index already. - // lookup column in table_t<>'s elements + // lookup column in base_table<>'s elements constexpr size_t ColIdx = index_sequence_value_at(column_index_sequence{}); auto& table = pick_table(dboObjects); return &std::get(table.elements).name; @@ -14680,7 +14611,7 @@ namespace sqlite_orm { } template - void operator()(polyfill::bool_constant, const highlight_t&) { + void operator()(std::true_type, const highlight_t&) { this->table_names.emplace(lookup_table_name(this->db_objects), ""); } }; @@ -15784,8 +15715,8 @@ namespace sqlite_orm { ast_iterator iterator; // possibly invoke lambda with node itself - if constexpr (polyfill::is_invocable, const T&>::value) { - lambda(polyfill::bool_constant{}, t); + if constexpr (polyfill::is_invocable::value) { + lambda(std::true_type{}, t); } iterator(t, lambda); @@ -19627,7 +19558,7 @@ namespace sqlite_orm { if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); } else if (!context.omit_table_name) { - const basic_table& table = pick_table>(context.db_objects); + const table_identifier& table = pick_table>(context.db_objects); collectedExpressions.push_back(quote_identifier(table.name) + ".*"); } else { collectedExpressions.emplace_back("*"); @@ -20084,7 +20015,7 @@ namespace sqlite_orm { } }; - struct base_trigger { + struct trigger_base { /** * Name of the trigger */ @@ -20097,7 +20028,7 @@ namespace sqlite_orm { * S is the list of trigger statments */ template - struct trigger_t : base_trigger { + struct trigger_t : trigger_base { using object_type = void; using elements_type = typename partial_trigger_t::statements_type; @@ -20326,6 +20257,164 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "schema/table.h" +// #include "schema/virtual_table.h" + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include // std::convertible_to +#endif +#include // std::string +#include // std::tuple_element, std::make_tuple +#include // std::forward, std::move +#endif + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../functional/gsl.h" + +// #include "../functional/mpl.h" + +// #include "../type_traits.h" + +// #include "../constraints.h" + +// #include "table_base.h" + +// #include "column.h" + +namespace sqlite_orm::internal { +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept module_tag = requires { + typename T::module_type; + { T::name() } -> std::convertible_to; + }; +#endif + + /** + * Default base traits of a "normal" virtual table module. + * + * Particularly this means: + * - It is not eponymous. + * The definition of eponymous virtual tables is built-in, fixed and implicit, + * and they can only be created with optional table-values for their hidden columns. + * - It is not a WITHOUT ROWID table (i.e. it has an implicit `rowid` column). + * - Omits the column type in the SQL creation statement. + * + * Specific virtual table modules can specialize this struct to provide their own traits. + */ + template + struct virtual_table_module_traits { + using module_type = M; + using is_eponymous = std::false_type; + using is_without_rowid = std::false_type; + using omit_column_type = std::true_type; + }; + + /** + * Default traits of a "normal" virtual table. + * + * Particularly this means : + * - Its definition is a `insertable_table_definition`. + * + * Specific virtual table modules can specialize this struct to provide their own traits. + */ + template + struct virtual_table_traits : virtual_table_module_traits { + using definition_type = insertable_table_definition; + using elements_type = elements_type_t; + }; + + /** + * Encapsulates the intermediary (and temporary) (and deprecated) `using_module(...)` expression. + */ + template + struct virtual_table_description : virtual_table_traits::definition_type { +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + static_assert(module_tag, "Template parameter M must be a module tag"); +#endif + }; + + /** + * Encapsulates the intermediary (and temporary) `using_module(...)` expression. + * + * Implementation note: When making the virtual table this virtual table definition is unpacked into the virtual table type itself. + * If desired or necessary one day, derive `virtual_table` from it, similar to `base_table` deriving from `base_table_definition`. + */ + template + struct virtual_table_definition : virtual_table_traits::definition_type { +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + static_assert(module_tag, "Template parameter M must be a module tag"); +#endif + }; + + /** + * Represents an SQLite virtual table. + */ + template + struct virtual_table : table_identifier, virtual_table_traits::definition_type { + using traits_type = virtual_table_traits; + using module_traits_type = virtual_table_module_traits; + using module_type = M; + using object_type = O; + using elements_type = typename traits_type::elements_type; + using is_without_rowid = typename traits_type::is_without_rowid; + }; + + template + inline constexpr bool is_virtual_table_v = polyfill::is_specialization_of_v; + + template + using is_virtual_table = polyfill::bool_constant>; +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Factory function for a virtual table. + * + * [Deprecation notice] This factory function is deprecated and will be removed in v1.11. + */ + template + internal::virtual_table + make_virtual_table(std::string name, internal::virtual_table_description description) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(description)}); + } + + /** + * Factory function for a virtual table. + * + * The mapped object type is determined implicitly from the first column definition. + */ + template>::object_type> + internal::virtual_table make_virtual_table(std::string name, + internal::virtual_table_definition definition) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(definition)}); + } + + /** + * Factory function for a virtual table. + * + * The mapped object type is explicitly specified. + */ + template + internal::virtual_table make_virtual_table(std::string name, + internal::virtual_table_definition definition) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(definition)}); + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Factory function for a virtual table. + * + * The mapped object type is explicitly specified. + */ + template + auto make_virtual_table(std::string name, internal::virtual_table_definition definition) { + return make_virtual_table>(std::move(name), std::move(definition)); + } +#endif +} + namespace sqlite_orm { namespace internal { @@ -20417,9 +20506,9 @@ namespace sqlite_orm { #endif }; - template - struct statement_serializer, void> { - using statement_type = table_t; + template + struct statement_serializer>> { + using statement_type = Table; template SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, @@ -20433,13 +20522,54 @@ namespace sqlite_orm { std::stringstream ss; ss << "CREATE TABLE " << streaming_identifier(tableName) << " (" << streaming_expressions_tuple(statement.elements, context) << ")"; - if constexpr (statement_type::is_without_rowid_v) { + if constexpr (statement_type::is_without_rowid::value) { ss << " WITHOUT ROWID"; } return ss.str(); } }; + // Eponymous virtual tables serialize only table values. Their definition is built-in, fixed and implicit + template = true> + std::string serialize_virtual_table_definition(const Definition&, const Ctx&) { + return {}; + } + + template = true> + std::string serialize_virtual_table_definition(const Elements& elements, const Ctx& context) { + using traits_type = ModTraits; + + auto subContext = context; + subContext.omit_column_type = traits_type::omit_column_type::value; + + std::stringstream ss; + ss << "(" << streaming_expressions_tuple(elements, subContext) << ")"; + return ss.str(); + } + + template + struct statement_serializer>> { + using statement_type = Table; + + template + SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, + const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { + std::stringstream ss; + ss << "CREATE VIRTUAL TABLE IF NOT EXISTS " << streaming_identifier(statement.name) << " USING " + << streaming_identifier(statement_type::module_type::name()) + << serialize_virtual_table_definition( + statement.elements, + context); + return ss.str(); + } + }; + template<> struct statement_serializer { using statement_type = current_time_t; @@ -22218,39 +22348,6 @@ namespace sqlite_orm { } }; -#if SQLITE_VERSION_NUMBER >= 3009000 - template - struct statement_serializer, void> { - using statement_type = using_fts5_t; - - template - SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, - const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { - std::stringstream ss; - ss << "USING FTS5("; - auto subContext = context; - subContext.omit_column_type = true; - ss << streaming_expressions_tuple(statement.columns, subContext) << ")"; - return ss.str(); - } - }; -#endif - - template - struct statement_serializer, void> { - using statement_type = virtual_table_t; - - template - SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, - const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { - std::stringstream ss; - ss << "CREATE VIRTUAL TABLE IF NOT EXISTS "; - ss << streaming_identifier(statement.name) << ' '; - ss << serialize(statement.module_details, context); - return ss.str(); - } - }; - template struct statement_serializer, void> { using statement_type = index_t; @@ -22740,18 +22837,20 @@ namespace sqlite_orm { // #include "serializer_context.h" -// #include "schema/triggers.h" - // #include "object_from_column_builder.h" // #include "row_extractor.h" // #include "schema/table.h" +// #include "schema/virtual_table.h" + // #include "schema/column.h" // #include "schema/index.h" +// #include "schema/triggers.h" + // #include "cte_storage.h" #ifndef SQLITE_ORM_IMPORT_STD_MODULE @@ -22771,7 +22870,7 @@ namespace sqlite_orm { // #include "select_constraints.h" -// #include "schema/table.h" +// #include "schema/table_base.h" // #include "alias.h" @@ -22941,6 +23040,21 @@ namespace sqlite_orm { FinalColRefs, Result>::type; + template + struct cte_table : table_identifier, table_definition { + using definition_type = table_definition; + using cte_mapper_type = Mapper; + using cte_moniker_type = typename cte_mapper_type::cte_moniker_type; + using object_type = cte_moniker_type; + using elements_type = typename definition_type::elements_type; + }; + + template + cte_table make_cte_table(std::string name, Cs... args) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } + // aliased column expressions, explicit or implicitly numbered template = true> auto make_cte_column(std::string name, const ColRef& /*finalColRef*/) { @@ -23086,8 +23200,8 @@ namespace sqlite_orm { template auto extract_colref_expressions(const DBOs& dbObjects, const asterisk_t& /*col*/) { using table_type = storage_pick_table_t; - using elements_t = typename table_type::elements_type; - using column_idxs = filter_tuple_sequence_t; + using elements_type = typename table_type::elements_type; + using column_idxs = filter_tuple_sequence_t; auto& table = pick_table(dbObjects); return get_table_columns_fields(table.elements, column_idxs{}); @@ -23147,14 +23261,14 @@ namespace sqlite_orm { std::vector columnNames, const ColRefs& finalColRefs, std::index_sequence) { - return make_table( + return make_cte_table( std::move(tableName), make_cte_column>(std::move(columnNames.at(CIs)), get(finalColRefs))...); } template - auto make_cte_table(const DBOs& dbObjects, const CTE& cte) { + auto make_cte_db_object(const DBOs& dbObjects, const CTE& cte) { using cte_type = CTE; auto subSelect = get_cte_driving_subselect(cte.subselect); @@ -23192,7 +23306,7 @@ namespace sqlite_orm { decltype(auto) make_recursive_cte_db_objects(const DBOs& dbObjects, const common_table_expressions& cte, std::index_sequence) { - auto tbl = make_cte_table(dbObjects, get(cte)); + auto tbl = make_cte_db_object(dbObjects, get(cte)); if constexpr (sizeof...(In) > 0) { return make_recursive_cte_db_objects( @@ -23253,7 +23367,7 @@ namespace sqlite_orm::internal { // examine `function_call` node expressions template - void operator()(polyfill::bool_constant, const function_call& udfCall) const { + void operator()(std::true_type, const function_call& udfCall) const { auto&& name = udfCall.name(); SQLITE_ORM_CPP_UNLIKELY { if (!_contains(_scalarFunctions, name) && !_contains(_aggregateFunctions, name)) @@ -23262,7 +23376,7 @@ namespace sqlite_orm::internal { } // examine `named_collate` node expressions - void operator()(polyfill::bool_constant, const named_collate_base& collateCall) const { + void operator()(std::true_type, const named_collate_base& collateCall) const { if (_collatingFunctions.find(collateCall.name) == _collatingFunctions.end()) SQLITE_ORM_CPP_UNLIKELY { #if SQLITE_VERSION_NUMBER >= 3008008 throw std::system_error{sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ), std::string(collateCall.name)}; @@ -24311,8 +24425,8 @@ namespace sqlite_orm { } protected: - template - sync_schema_result schema_status(const virtual_table_t&, sqlite3*, bool, bool*) { + template = true> + sync_schema_result schema_status(const Table&, sqlite3*, bool, bool*) { return sync_schema_result::already_in_sync; } @@ -24326,11 +24440,9 @@ namespace sqlite_orm { return sync_schema_result::already_in_sync; } - template - sync_schema_result schema_status(const table_t& table, - sqlite3* db, - bool preserve, - bool* attempt_to_preserve) { + template = true> + sync_schema_result + schema_status(const Table& table, sqlite3* db, bool preserve, bool* attempt_to_preserve) { if (attempt_to_preserve) { *attempt_to_preserve = true; } @@ -24413,12 +24525,17 @@ namespace sqlite_orm { return res; } - template - sync_schema_result sync_dbo(const virtual_table_t& virtualTable, sqlite3* db, bool) { - using context_t = serializer_context; + template = true> + sync_schema_result sync_dbo(const Table& virtualTable, sqlite3* db, bool) { + // eponymous virtual table instances with the same name as their module exist already + if constexpr (Table::module_traits_type::is_eponymous::value) { + if (virtualTable.name == Table::module_type::name()) { + return sync_schema_result::already_in_sync; + } + } const auto res = sync_schema_result::already_in_sync; - context_t context{this->db_objects}; + const serializer_context context{this->db_objects}; const auto sql = serialize(virtualTable, context); this->executor.perform_void_exec(db, sql.c_str()); return res; @@ -24426,10 +24543,8 @@ namespace sqlite_orm { template sync_schema_result sync_dbo(const index_t& index, sqlite3* db, bool) { - using context_t = serializer_context; - const auto res = sync_schema_result::already_in_sync; - context_t context{this->db_objects}; + const serializer_context context{this->db_objects}; const auto sql = serialize(index, context); this->executor.perform_void_exec(db, sql.c_str()); return res; @@ -24437,20 +24552,18 @@ namespace sqlite_orm { template sync_schema_result sync_dbo(const trigger_t& trigger, sqlite3* db, bool) { - using context_t = serializer_context; - const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly - context_t context{this->db_objects}; + const serializer_context context{this->db_objects}; const auto sql = serialize(trigger, context); this->executor.perform_void_exec(db, sql.c_str()); return res; } - template = true> + template = true> sync_schema_result sync_dbo(const Table& table, sqlite3* db, bool preserve); - template = true> - sync_schema_result sync_regular_table(const Table& table, sqlite3* db, bool preserve); + template = true> + sync_schema_result sync_regular_base_table(const Table& table, sqlite3* db, bool preserve); template void add_column(sqlite3* db, const std::string& tableName, const C& column) const { @@ -25036,14 +25149,14 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } /** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> base_table -> column_t) * this file is also used to provide definitions of interface methods 'hitting the database'. */ #pragma once // #include "implementations/column_definitions.h" /** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> base_table -> column_t) * this file is also used to provide definitions of interface methods 'hitting the database'. */ @@ -25108,7 +25221,7 @@ namespace sqlite_orm { // #include "implementations/table_definitions.h" /** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> base_table -> column_t) * this file is also used to provide definitions of interface methods 'hitting the database'. */ @@ -25131,10 +25244,10 @@ namespace sqlite_orm { namespace sqlite_orm { namespace internal { - template - std::vector table_t::get_table_info() const { + template + std::vector base_table::get_table_info() const { std::vector res; - res.reserve(filter_tuple_sequence_t::size()); + res.reserve(col_index_sequence_of::size()); this->for_each_column([&res](auto& column) { using field_type = field_type_t>; std::string dft; @@ -25183,8 +25296,7 @@ namespace sqlite_orm { // #include "implementations/storage_definitions.h" /** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * this file is also used to separate implementation details from the main header file, - * e.g. usage of the dbstat table. + * this file is also used to separate implementation details from the main header file. */ #ifndef SQLITE_ORM_IMPORT_STD_MODULE @@ -25248,55 +25360,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #endif } -// #include "../eponymous_vtabs/dbstat.h" - -#ifndef SQLITE_ORM_IMPORT_STD_MODULE -#ifdef SQLITE_ENABLE_DBSTAT_VTAB -#include // std::string -#endif -#endif - -// #include "../schema/column.h" - -// #include "../schema/table.h" - -// #include "../column_pointer.h" - -SQLITE_ORM_EXPORT namespace sqlite_orm { -#ifdef SQLITE_ENABLE_DBSTAT_VTAB - struct dbstat { - std::string name; - std::string path; - int pageno = 0; - std::string pagetype; - int ncell = 0; - int payload = 0; - int unused = 0; - int mx_payload = 0; - int pgoffset = 0; - int pgsize = 0; - }; - - inline auto make_dbstat_table() { - return make_table("dbstat", - make_column("name", &dbstat::name), - make_column("path", &dbstat::path), - make_column("pageno", &dbstat::pageno), - make_column("pagetype", &dbstat::pagetype), - make_column("ncell", &dbstat::ncell), - make_column("payload", &dbstat::payload), - make_column("unused", &dbstat::unused), - make_column("mx_payload", &dbstat::mx_payload), - make_column("pgoffset", &dbstat::pgoffset), - make_column("pgsize", &dbstat::pgsize)); - } - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - inline constexpr orm_table_reference auto dbstat_table = c(); -#endif -#endif // SQLITE_ENABLE_DBSTAT_VTAB -} - // #include "../type_traits.h" // #include "../util.h" @@ -25308,24 +25371,20 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { namespace sqlite_orm { namespace internal { template - template> + template> sync_schema_result storage_t::sync_dbo([[maybe_unused]] const Table& table, [[maybe_unused]] sqlite3* db, [[maybe_unused]] bool preserve) { - if constexpr ( -#ifdef SQLITE_ENABLE_DBSTAT_VTAB - std::is_same, dbstat>::value || -#endif - std::is_same, sqlite_master>::value) { + if constexpr (std::is_same, sqlite_master>::value) { return sync_schema_result::already_in_sync; } else { - return this->sync_regular_table(table, db, preserve); + return this->sync_regular_base_table(table, db, preserve); } } template - template> - sync_schema_result storage_t::sync_regular_table(const Table& table, sqlite3* db, bool preserve) { + template> + sync_schema_result storage_t::sync_regular_base_table(const Table& table, sqlite3* db, bool preserve) { auto res = sync_schema_result::already_in_sync; bool attempt_to_preserve = true; @@ -25908,6 +25967,176 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } #pragma once +// #include "dbstat.h" + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#ifdef SQLITE_ENABLE_DBSTAT_VTAB +#include // std::false_type, std::true_type +#include // std::make_tuple +#include // std::move +#endif +#endif + +// #include "../functional/gsl.h" + +// #include "../schema/virtual_table.h" + +// #include "../schema/column.h" + +// #include "../column_pointer.h" + +#ifdef SQLITE_ENABLE_DBSTAT_VTAB +namespace sqlite_orm::internal { + struct dbstat_module_tag { + // simplify conceptual/meta programming + using module_type = dbstat_module_tag; + + static constexpr orm_gsl::czstring name() { + return "dbstat"; + } + }; + + template<> + struct virtual_table_module_traits { + using module_type = dbstat_module_tag; + using is_eponymous = std::true_type; + using is_without_rowid = std::false_type; + using omit_column_type = std::true_type; + }; + + template + struct virtual_table_traits : virtual_table_module_traits { + using definition_type = table_definition; + using elements_type = typename definition_type::elements_type; + }; + + template + inline virtual_table_definition using_dbstat(Cs... columns) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {{std::make_tuple(std::move(columns)...)}}); + } +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + struct dbstat { + std::string name; + std::string path; + int pageno = 0; + std::string pagetype; + int ncell = 0; + int payload = 0; + int unused = 0; + int mx_payload = 0; + int pgoffset = 0; + int pgsize = 0; + }; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + inline constexpr orm_table_reference auto dbstat_table = c(); +#endif + + /** + * Factory function for a DBSTAT virtual table definition. + * If no schema is specified then the main schema is used. + * + * Though the DBSTAT virtual table is an eponymous table SQLite allows to create a virtual table instance with a different name. + * This is mostly useful with binding input arguments, e.g. a different schema than "main", which is yet unimplemented. + */ + inline auto using_dbstat() { + return internal::using_dbstat(make_column("name", &dbstat::name), + make_column("path", &dbstat::path), + make_column("pageno", &dbstat::pageno), + make_column("pagetype", &dbstat::pagetype), + make_column("ncell", &dbstat::ncell), + make_column("payload", &dbstat::payload), + make_column("unused", &dbstat::unused), + make_column("mx_payload", &dbstat::mx_payload), + make_column("pgoffset", &dbstat::pgoffset), + make_column("pgsize", &dbstat::pgsize)); + } + + /** + * Factory function for the DBSTAT default eponymous virtual table. + */ + inline auto make_dbstat_table() { + return make_virtual_table(internal::dbstat_module_tag::name(), using_dbstat()); + } +} +#endif // SQLITE_ENABLE_DBSTAT_VTAB + +// #include "fts5.h" + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#if SQLITE_VERSION_NUMBER >= 3009000 +#include // std::make_tuple +#include // std::forward +#endif +#endif + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../functional/gsl.h" + +// #include "../functional/mpl.h" + +// #include "../schema/virtual_table.h" + +// #include "../schema/column.h" + +// #include "../constraints.h" + +#if SQLITE_VERSION_NUMBER >= 3009000 +namespace sqlite_orm::internal { + template + using is_fts5_table_element_or_constraint = mpl::invoke_t, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template>, + T>; + struct fts5_module_tag { + // simplify conceptual/meta programming + using module_type = fts5_module_tag; + + static constexpr orm_gsl::czstring name() { + return "fts5"; + } + }; +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Factory function for a FTS5 virtual table definition. + * + * The mapped object type will be determined implicitly from the first column definition when calling `make_virtual_table()`. + */ + template + internal::virtual_table_definition using_fts5(Cs... definition) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(definition)...)}); + } + + /** + * Factory function for a FTS5 virtual table definition. + * + * The mapped object type is explicitly specified. + * + * [Deprecation notice] This factory function is deprecated and will be removed in v1.11. + */ + template + [[deprecated("Specify the explicit object type when calling `make_virtual_table()`.")]] + internal::virtual_table_description using_fts5(Cs... definition) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(definition)...)}); + } +} +#endif + +#pragma once + #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3020000 #include // std::move diff --git a/not_single_header_include/sqlite_orm/sqlite_orm.h b/not_single_header_include/sqlite_orm/sqlite_orm.h index b217801ed..8a36c4b2d 100644 --- a/not_single_header_include/sqlite_orm/sqlite_orm.h +++ b/not_single_header_include/sqlite_orm/sqlite_orm.h @@ -8,5 +8,6 @@ #include "../../dev/storage.h" #include "../../dev/interface_definitions.h" #include "../../dev/get_prepared_statement.h" +#include "../../dev/vtabs/vtabs.h" #include "../../dev/carray.h" #include "../../dev/functional/finish_macros.h" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5ea3022ed..294c42a2b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -64,7 +64,10 @@ if(MSVC) # C4456: declaration of 'symbol' hides previous local declaration /wd4456 # C4458: declaration of 'symbol' hides class member - /wd4458) + /wd4458 + # C4458: 'symbol': deprecated + # disabled due to unit tests still testing deprecated features + /wd4996) if (CMAKE_CXX_FLAGS MATCHES "/D_UNICODE") # explicitly set the entry point of the executable file, # otherwise for some reason the linker will not pick up `wmain`, which is provided by the static Catch2 library diff --git a/tests/builtin_tables.cpp b/tests/builtin_tables.cpp index e82d8b7a3..15f1f2c55 100644 --- a/tests/builtin_tables.cpp +++ b/tests/builtin_tables.cpp @@ -31,29 +31,4 @@ TEST_CASE("builtin tables") { REQUIRE_THAT(schemaRows2, Equals(masterRows)); #endif } - -#ifdef SQLITE_ENABLE_DBSTAT_VTAB - SECTION("dbstat") { - struct User { - int id = 0; - std::string name; - }; - auto storage = make_storage( - "", - make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), - make_dbstat_table()); - storage.sync_schema(); - - storage.remove_all(); - - storage.replace(User{1, "Dua Lipa"}); - - auto dbstatRows = storage.get_all(); - std::ignore = dbstatRows; - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - dbstatRows = storage.get_all(); -#endif - } -#endif // SQLITE_ENABLE_DBSTAT_VTAB } diff --git a/tests/schema/virtual_table.cpp b/tests/schema/virtual_table.cpp index 3ff7c2759..1f6af400d 100644 --- a/tests/schema/virtual_table.cpp +++ b/tests/schema/virtual_table.cpp @@ -4,7 +4,7 @@ #if SQLITE_VERSION_NUMBER >= 3009000 using namespace sqlite_orm; -TEST_CASE("virtual table") { +TEST_CASE("fts5 virtual table schema") { using Catch::Matchers::UnorderedEquals; struct Post { @@ -121,6 +121,55 @@ TEST_CASE("issue1410") { for (const auto& row: rows) { std::ignore = row; - } // has to be compiled + } // must compile +} +#endif + +#ifdef SQLITE_ENABLE_DBSTAT_VTAB +TEST_CASE("dbstat virtual table schema") { + constexpr auto compareColumnName = [](const std::string* foundValue, const std::string& expectedValue) { + if (!foundValue) { + return false; + } + return *foundValue == expectedValue; + }; + + SECTION("epynomous") { + SECTION("definition") { + auto virtualTable = make_dbstat_table(); + REQUIRE(compareColumnName(virtualTable.find_column_name(&dbstat::name), "name")); + REQUIRE(compareColumnName(virtualTable.find_column_name(&dbstat::pgsize), "pgsize")); + } + SECTION("storage") { + auto storage = make_storage("", make_dbstat_table()); + storage.sync_schema(); + // eponymous virtual tables must not get created + REQUIRE_FALSE(storage.table_exists("dbstat")); + + auto dbstatRows = storage.get_all(); + REQUIRE(dbstatRows.size() == 0); + } + } + + SECTION("virtual table instance") { + struct mystat : sqlite_orm::dbstat {}; + + SECTION("definition") { + auto virtualTable = make_virtual_table("mystat", using_dbstat()); + REQUIRE(compareColumnName(virtualTable.find_column_name(&mystat::name), "name")); + REQUIRE(compareColumnName(virtualTable.find_column_name(&mystat::pgsize), "pgsize")); + } + SECTION("storage") { + auto storage = + make_storage("", make_sqlite_schema_table(), make_virtual_table("mystat", using_dbstat())); + storage.sync_schema(); + storage.sync_schema_simulate(); + REQUIRE(storage.table_exists("mystat")); + REQUIRE_FALSE(storage.table_exists("dbstat")); + + auto mystatRows = storage.get_all(); + REQUIRE(mystatRows.size() == 1); + } + } } #endif diff --git a/tests/statement_serializer_tests/column_names.cpp b/tests/statement_serializer_tests/column_names.cpp index 31bbb9dc8..65381044f 100644 --- a/tests/statement_serializer_tests/column_names.cpp +++ b/tests/statement_serializer_tests/column_names.cpp @@ -168,7 +168,8 @@ TEST_CASE("statement_serializer column names") { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES SECTION("cte") { auto dbObjects2 = - internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, 1_ctealias().as(select(1)))); + internal::db_objects_cat(dbObjects, + internal::make_cte_db_object(dbObjects, 1_ctealias().as(select(1)))); using context_t = internal::serializer_context; context_t context{dbObjects2}; context.omit_table_name = false; diff --git a/tests/statement_serializer_tests/schema/table.cpp b/tests/statement_serializer_tests/schema/table.cpp index f775985a8..7b1be310e 100644 --- a/tests/statement_serializer_tests/schema/table.cpp +++ b/tests/statement_serializer_tests/schema/table.cpp @@ -3,7 +3,7 @@ using namespace sqlite_orm; -TEST_CASE("statement_serializer table_t") { +TEST_CASE("statement_serializer base_table") { struct User { int id = 0; std::string name; diff --git a/tests/statement_serializer_tests/schema/using_dbstat.cpp b/tests/statement_serializer_tests/schema/using_dbstat.cpp new file mode 100644 index 000000000..f368892b0 --- /dev/null +++ b/tests/statement_serializer_tests/schema/using_dbstat.cpp @@ -0,0 +1,33 @@ +#include +#include + +using namespace sqlite_orm; + +#ifdef SQLITE_ENABLE_DBSTAT_VTAB +TEST_CASE("statement_serializer dbstat") { + struct mystat : sqlite_orm::dbstat {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr orm_table_reference auto mystat_table = c(); +#endif + + std::string value; + std::string expected; + using db_objects_t = internal::db_objects_tuple<>; + const db_objects_t dbObjects{}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + SECTION("default") { + auto expression = make_virtual_table("mystat", using_dbstat()); + value = serialize(expression, context); + expected = R"(CREATE VIRTUAL TABLE IF NOT EXISTS "mystat" USING "dbstat")"; + } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("table reference") { + auto expression = make_virtual_table("mystat", using_dbstat()); + value = serialize(expression, context); + expected = R"(CREATE VIRTUAL TABLE IF NOT EXISTS "mystat" USING "dbstat")"; + } +#endif + REQUIRE(value == expected); +} +#endif diff --git a/tests/statement_serializer_tests/schema/using_fts5.cpp b/tests/statement_serializer_tests/schema/using_fts5.cpp index e08033072..6d6a72ce9 100644 --- a/tests/statement_serializer_tests/schema/using_fts5.cpp +++ b/tests/statement_serializer_tests/schema/using_fts5.cpp @@ -4,7 +4,7 @@ #if SQLITE_VERSION_NUMBER >= 3009000 using namespace sqlite_orm; -TEST_CASE("statement_serializer using_fts5") { +TEST_CASE("statement_serializer fts5") { struct Post { std::string title; std::string body; @@ -13,6 +13,11 @@ TEST_CASE("statement_serializer using_fts5") { int id = 0; std::string name; }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr orm_table_reference auto post = c(); + constexpr orm_table_reference auto user = c(); +#endif + std::string value; std::string expected; auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)); @@ -21,46 +26,83 @@ TEST_CASE("statement_serializer using_fts5") { using context_t = internal::serializer_context; context_t context{dbObjects}; SECTION("simple") { - auto node = using_fts5(make_column("title", &Post::title), make_column("body", &Post::body)); - value = serialize(node, context); - expected = R"(USING FTS5("title", "body"))"; + auto expression = + make_virtual_table("posts", + using_fts5(make_column("title", &Post::title), make_column("body", &Post::body))); + value = serialize(expression, context); + expected = R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING "fts5"("title", "body"))"; + } + SECTION("explicit object, deprecated") { + auto expression = + make_virtual_table("posts", + using_fts5(make_column("title", &Post::title), make_column("body", &Post::body))); + value = serialize(expression, context); + expected = R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING "fts5"("title", "body"))"; + } + SECTION("explicit object") { + auto expression = + make_virtual_table("posts", + using_fts5(make_column("title", &Post::title), make_column("body", &Post::body))); + value = serialize(expression, context); + expected = R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING "fts5"("title", "body"))"; } SECTION("unindexed") { - auto node = using_fts5(make_column("title", &Post::title), make_column("body", &Post::body, unindexed())); - value = serialize(node, context); - expected = R"(USING FTS5("title", "body" UNINDEXED))"; + auto expression = make_virtual_table( + "posts", + using_fts5(make_column("title", &Post::title), make_column("body", &Post::body, unindexed()))); + value = serialize(expression, context); + expected = R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING "fts5"("title", "body" UNINDEXED))"; } SECTION("prefix=2") { - auto node = using_fts5(make_column("title", &Post::title), make_column("body", &Post::body), prefix(2)); - value = serialize(node, context); - expected = R"(USING FTS5("title", "body", prefix=2))"; + auto expression = make_virtual_table( + "posts", + using_fts5(make_column("title", &Post::title), make_column("body", &Post::body), prefix(2))); + value = serialize(expression, context); + expected = R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING "fts5"("title", "body", prefix=2))"; } SECTION("tokenize") { SECTION("porter ascii") { - auto node = using_fts5(make_column("title", &Post::title), - make_column("body", &Post::body), - tokenize("porter ascii")); - value = serialize(node, context); - expected = R"(USING FTS5("title", "body", tokenize = 'porter ascii'))"; + auto expression = make_virtual_table("posts", + using_fts5(make_column("title", &Post::title), + make_column("body", &Post::body), + tokenize("porter ascii"))); + value = serialize(expression, context); + expected = + R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING "fts5"("title", "body", tokenize = 'porter ascii'))"; } SECTION("unicode61 remove_diacritics 1") { - auto node = using_fts5(make_column("title", &Post::title), - make_column("body", &Post::body), - tokenize("unicode61 remove_diacritics 1")); - value = serialize(node, context); - expected = R"(USING FTS5("title", "body", tokenize = 'unicode61 remove_diacritics 1'))"; + auto expression = make_virtual_table("posts", + using_fts5(make_column("title", &Post::title), + make_column("body", &Post::body), + tokenize("unicode61 remove_diacritics 1"))); + value = serialize(expression, context); + expected = + R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING "fts5"("title", "body", tokenize = 'unicode61 remove_diacritics 1'))"; } } SECTION("content") { - auto node = using_fts5(make_column("title", &Post::title), make_column("body", &Post::body), content("")); - value = serialize(node, context); - expected = R"(USING FTS5("title", "body", content=''))"; + auto expression = make_virtual_table( + "posts", + using_fts5(make_column("title", &Post::title), make_column("body", &Post::body), content(""))); + value = serialize(expression, context); + expected = R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING "fts5"("title", "body", content=''))"; } SECTION("table_content") { - auto node = using_fts5(make_column("title", &Post::title), make_column("body", &Post::body), content()); - value = serialize(node, context); - expected = R"(USING FTS5("title", "body", content="users"))"; + auto expression = make_virtual_table( + "posts", + using_fts5(make_column("title", &Post::title), make_column("body", &Post::body), content())); + value = serialize(expression, context); + expected = R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING "fts5"("title", "body", content="users"))"; + } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("table reference") { + auto expression = make_virtual_table( + "posts", + using_fts5(make_column("title", &Post::title), make_column("body", &Post::body), content())); + value = serialize(expression, context); + expected = R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING "fts5"("title", "body", content="users"))"; } +#endif REQUIRE(value == expected); } #endif diff --git a/tests/statement_serializer_tests/schema/virtual_table.cpp b/tests/statement_serializer_tests/schema/virtual_table.cpp deleted file mode 100644 index d330649e5..000000000 --- a/tests/statement_serializer_tests/schema/virtual_table.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include - -#if SQLITE_VERSION_NUMBER >= 3009000 -using namespace sqlite_orm; - -TEST_CASE("statement_serializer FTS5") { - struct Post { - std::string title; - std::string body; - }; - internal::db_objects_tuple<> storage; - internal::serializer_context> context{storage}; - auto node = - make_virtual_table("posts", using_fts5(make_column("title", &Post::title), make_column("body", &Post::body))); - auto value = serialize(node, context); - REQUIRE(value == R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING FTS5("title", "body"))"); -} -#endif diff --git a/tests/statement_serializer_tests/select_constraints.cpp b/tests/statement_serializer_tests/select_constraints.cpp index b0d05c119..0ece294f8 100644 --- a/tests/statement_serializer_tests/select_constraints.cpp +++ b/tests/statement_serializer_tests/select_constraints.cpp @@ -96,7 +96,7 @@ TEST_CASE("statement_serializer select constraints") { SECTION("from CTE") { using cte_1 = decltype(1_ctealias); auto dbObjects2 = - internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cte().as(select(1)))); + internal::db_objects_cat(dbObjects, internal::make_cte_db_object(dbObjects, cte().as(select(1)))); using context_t = internal::serializer_context; context_t context{dbObjects2}; SECTION("without alias 1") { diff --git a/tests/statement_serializer_tests/statements/insert_replace.cpp b/tests/statement_serializer_tests/statements/insert_replace.cpp index 3a5b16f14..5ab7735f6 100644 --- a/tests/statement_serializer_tests/statements/insert_replace.cpp +++ b/tests/statement_serializer_tests/statements/insert_replace.cpp @@ -76,7 +76,7 @@ TEST_CASE("statement_serializer insert/replace") { constexpr orm_cte_moniker auto data = "data"_cte; constexpr auto cteExpression = cte().as(select(asterisk())); auto dbObjects2 = - internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + internal::db_objects_cat(dbObjects, internal::make_cte_db_object(dbObjects, cteExpression)); using context_t = internal::serializer_context; context_t context2{dbObjects2}; @@ -369,7 +369,7 @@ TEST_CASE("statement_serializer insert/replace") { constexpr orm_cte_moniker auto data = "data"_cte; constexpr auto cteExpression = cte().as(select(asterisk())); auto dbObjects2 = - internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + internal::db_objects_cat(dbObjects, internal::make_cte_db_object(dbObjects, cteExpression)); using context_t = internal::serializer_context; context_t context2{dbObjects2}; diff --git a/tests/statement_serializer_tests/statements/remove_all.cpp b/tests/statement_serializer_tests/statements/remove_all.cpp index 44c62a9d5..cee0685e8 100644 --- a/tests/statement_serializer_tests/statements/remove_all.cpp +++ b/tests/statement_serializer_tests/statements/remove_all.cpp @@ -38,7 +38,7 @@ TEST_CASE("statement_serializer remove_all") { SECTION("With clause") { constexpr orm_cte_moniker auto data = "data"_cte; constexpr auto cteExpression = cte().as(select(1)); - auto dbObjects2 = internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + auto dbObjects2 = internal::db_objects_cat(dbObjects, internal::make_cte_db_object(dbObjects, cteExpression)); using context_t = internal::serializer_context; context_t context2{dbObjects2}; diff --git a/tests/statement_serializer_tests/statements/update_all.cpp b/tests/statement_serializer_tests/statements/update_all.cpp index 160e1c944..b21e1d1d5 100644 --- a/tests/statement_serializer_tests/statements/update_all.cpp +++ b/tests/statement_serializer_tests/statements/update_all.cpp @@ -65,7 +65,7 @@ TEST_CASE("statement_serializer update_all") { SECTION("With clause") { constexpr orm_cte_moniker auto data = "data"_cte; constexpr auto cteExpression = cte().as(select(&Customer::phone, where(c(&Customer::id) == 1))); - auto dbObjects2 = internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + auto dbObjects2 = internal::db_objects_cat(dbObjects, internal::make_cte_db_object(dbObjects, cteExpression)); using context_t = internal::serializer_context; context_t context2{dbObjects2}; diff --git a/tests/static_tests/column_expression_type.cpp b/tests/static_tests/column_expression_type.cpp index 47c233089..607841335 100644 --- a/tests/static_tests/column_expression_type.cpp +++ b/tests/static_tests/column_expression_type.cpp @@ -56,9 +56,9 @@ TEST_CASE("column_expression_of_t") { #endif #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) using cte_1 = decltype(1_ctealias); - auto dbObjects2 = - internal::db_objects_cat(dbObjects, - internal::make_cte_table(dbObjects, cte().as(select(columns(&Org::id, 1))))); + auto dbObjects2 = internal::db_objects_cat( + dbObjects, + internal::make_cte_db_object(dbObjects, cte().as(select(columns(&Org::id, 1))))); using db_objects2_t = decltype(dbObjects2); runTest>(column(&Org::id)); runTest>>>(column(1_colalias)); diff --git a/tests/static_tests/column_pointer.cpp b/tests/static_tests/column_pointer.cpp index 4d4df3ef4..f98ea0669 100644 --- a/tests/static_tests/column_pointer.cpp +++ b/tests/static_tests/column_pointer.cpp @@ -147,7 +147,7 @@ TEST_CASE("column pointers") { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES SECTION("table reference expressions") { - runTest>(make_table("derived_user")); + runTest>(make_table("derived_user")); runTest>(from()); runTest>(asterisk()); runTest>(object()); diff --git a/tests/static_tests/column_result_t.cpp b/tests/static_tests/column_result_t.cpp index 035f80385..6f6b2fa5d 100644 --- a/tests/static_tests/column_result_t.cpp +++ b/tests/static_tests/column_result_t.cpp @@ -148,7 +148,7 @@ TEST_CASE("column_result_of_t") { // note: even though used with the CTE, &User::id doesn't need to be mapped into the CTE to make column results work; // this is because the result type is taken from the member pointer just because we can't look it up in the storage definition auto dbObjects2 = - internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cte().as(select(1)))); + internal::db_objects_cat(dbObjects, internal::make_cte_db_object(dbObjects, cte().as(select(1)))); using db_objects2_t = decltype(dbObjects2); runTest(column(&User::id)); runTest(column(1_colalias)); diff --git a/tests/static_tests/cte.cpp b/tests/static_tests/cte.cpp index b069ce4fb..4566d2c52 100644 --- a/tests/static_tests/cte.cpp +++ b/tests/static_tests/cte.cpp @@ -89,7 +89,7 @@ TEST_CASE("CTE storage") { auto idx1 = make_unique_index("idx1_org", &Org::id); auto idx2 = make_index("idx2_org", &Org::id); auto dbObjects = tuple{idx1, idx2, table}; - auto cteTable = internal::make_cte_table(dbObjects, 1_ctealias().as(select(1))); + auto cteTable = internal::make_cte_db_object(dbObjects, 1_ctealias().as(select(1))); auto dbObjects2 = internal::db_objects_cat(dbObjects, cteTable); // note: deliberately make indexes resulting in the same index_t<> type, such that we know `db_objects_cat()` is working properly diff --git a/tests/static_tests/virtual_tables.cpp b/tests/static_tests/virtual_tables.cpp new file mode 100644 index 000000000..00789014c --- /dev/null +++ b/tests/static_tests/virtual_tables.cpp @@ -0,0 +1,26 @@ +#include +#include + +using namespace sqlite_orm; +using internal::col_index_sequence_of, internal::col_index_sequence_with_field_type; +using internal::is_base_template_of_v; +using internal::table_definition; + +#ifdef SQLITE_ENABLE_DBSTAT_VTAB +TEST_CASE("dbstat static tests") { + SECTION("table definition") { + auto table = make_dbstat_table(); + using table_type = decltype(table); + using elements_type = decltype(table.elements); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(is_base_template_of_v); + STATIC_REQUIRE(col_index_sequence_of::size() == 10); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 3); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 7); +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + STATIC_REQUIRE(orm_table_reference); +#endif + } +} +#endif diff --git a/tests/table_name_collector.cpp b/tests/table_name_collector.cpp index 36f5a79d6..687265828 100644 --- a/tests/table_name_collector.cpp +++ b/tests/table_name_collector.cpp @@ -57,7 +57,7 @@ TEST_CASE("table name collector") { #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) SECTION("from CTE") { auto dbObjects2 = - internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, 1_ctealias().as(select(1)))); + internal::db_objects_cat(dbObjects, internal::make_cte_db_object(dbObjects, 1_ctealias().as(select(1)))); using context_t = internal::serializer_context; context_t context{dbObjects2}; auto collector = internal::make_table_name_collector(context.db_objects); diff --git a/third_party/amalgamate/config.json b/third_party/amalgamate/config.json index ccf329ef8..e370d0a72 100755 --- a/third_party/amalgamate/config.json +++ b/third_party/amalgamate/config.json @@ -8,6 +8,7 @@ "dev/storage.h", "dev/interface_definitions.h", "dev/get_prepared_statement.h", + "dev/vtabs/vtabs.h", "dev/carray.h", "dev/functional/finish_macros.h" ],