From 7e2f5783ddd4e59bd69de939e78d998655fb04dd Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 16 Jul 2024 22:15:43 +0200 Subject: [PATCH 001/170] First attempt to offer sqlite_orm as a named C++ module --- dev/alias.h | 2 +- dev/alias_traits.h | 2 +- dev/arg_values.h | 2 +- dev/arithmetic_tag.h | 2 +- dev/ast/excluded.h | 2 +- dev/ast/exists.h | 2 +- dev/ast/group_by.h | 2 +- dev/ast/into.h | 2 +- dev/ast/match.h | 2 +- dev/ast/rank.h | 2 +- dev/ast/set.h | 2 +- dev/ast/special_keywords.h | 2 +- dev/ast/upsert_clause.h | 2 +- dev/ast/where.h | 2 +- dev/ast_iterator.h | 2 +- dev/backup.h | 2 +- dev/carray.h | 2 +- dev/collate_argument.h | 2 +- dev/column_pointer.h | 2 +- dev/conditions.h | 4 ++-- dev/constraints.h | 2 +- dev/core_functions.h | 4 ++-- dev/cte_moniker.h | 2 +- dev/cte_storage.h | 4 ++-- dev/cte_types.h | 2 +- dev/eponymous_vtabs/dbstat.h | 2 +- dev/error_code.h | 4 ++-- dev/expression.h | 2 +- dev/field_printer.h | 2 +- dev/function.h | 2 +- dev/functional/config.h | 6 ++++++ dev/get_prepared_statement.h | 2 +- dev/indexed_column.h | 2 +- dev/is_std_ptr.h | 2 +- dev/journal_mode.h | 2 +- dev/limit_accessor.h | 2 +- dev/literal.h | 2 +- dev/member_traits/member_traits.h | 2 +- dev/object_from_column_builder.h | 2 +- dev/operators.h | 2 +- dev/pointer_value.h | 4 ++-- dev/pragma.h | 2 +- dev/prepared_statement.h | 2 +- dev/row_extractor.h | 2 +- dev/rowid.h | 2 +- dev/schema/column.h | 2 +- dev/schema/index.h | 2 +- dev/schema/table.h | 2 +- dev/schema/triggers.h | 2 +- dev/select_constraints.h | 2 +- dev/sqlite_schema_table.h | 2 +- dev/statement_binder.h | 2 +- dev/statement_finalizer.h | 2 +- dev/storage.h | 2 +- dev/storage_base.h | 2 +- dev/sync_schema_result.h | 2 +- dev/table_info.h | 2 +- dev/table_reference.h | 2 +- dev/type_is_nullable.h | 2 +- dev/type_printer.h | 2 +- dev/type_traits.h | 2 +- dev/util.h | 2 +- dev/values.h | 2 +- dev/xdestroy_handling.h | 6 +++--- examples/iteration.cpp | 6 ++++-- modules/sqlite_orm.ixx | 13 +++++++++++++ 66 files changed, 93 insertions(+), 72 deletions(-) create mode 100644 modules/sqlite_orm.ixx diff --git a/dev/alias.h b/dev/alias.h index 822b41be5..e964a6d68 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -17,7 +17,7 @@ #include "tags.h" #include "column_pointer.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES diff --git a/dev/alias_traits.h b/dev/alias_traits.h index 5d64de530..95ce37c1c 100644 --- a/dev/alias_traits.h +++ b/dev/alias_traits.h @@ -10,7 +10,7 @@ #include "type_traits.h" #include "table_reference.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** @short Base class for a custom table alias, column alias or expression alias. */ diff --git a/dev/arg_values.h b/dev/arg_values.h index 515a1b12f..34f31f665 100644 --- a/dev/arg_values.h +++ b/dev/arg_values.h @@ -4,7 +4,7 @@ #include "row_extractor.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** @short Wrapper around a dynamically typed value object. */ diff --git a/dev/arithmetic_tag.h b/dev/arithmetic_tag.h index 26570fff6..c7dd076fc 100644 --- a/dev/arithmetic_tag.h +++ b/dev/arithmetic_tag.h @@ -3,7 +3,7 @@ #include "functional/mpl/conditional.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * Helper classes used by statement_binder and row_extractor. diff --git a/dev/ast/excluded.h b/dev/ast/excluded.h index faea0a340..4bf5874cb 100644 --- a/dev/ast/excluded.h +++ b/dev/ast/excluded.h @@ -2,7 +2,7 @@ #include // std::move -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { template diff --git a/dev/ast/exists.h b/dev/ast/exists.h index b6d2a1cfd..0cbfb0ea3 100644 --- a/dev/ast/exists.h +++ b/dev/ast/exists.h @@ -4,7 +4,7 @@ #include "../tags.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { template diff --git a/dev/ast/group_by.h b/dev/ast/group_by.h index 966dc1550..29e9cb43f 100644 --- a/dev/ast/group_by.h +++ b/dev/ast/group_by.h @@ -6,7 +6,7 @@ #include "../functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { template diff --git a/dev/ast/into.h b/dev/ast/into.h index d2bba6a2e..70a390df2 100644 --- a/dev/ast/into.h +++ b/dev/ast/into.h @@ -2,7 +2,7 @@ #include "../functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { template diff --git a/dev/ast/match.h b/dev/ast/match.h index 5500e2820..b60bffa94 100644 --- a/dev/ast/match.h +++ b/dev/ast/match.h @@ -1,6 +1,6 @@ #pragma once -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { template diff --git a/dev/ast/rank.h b/dev/ast/rank.h index e71ec8723..9d0888ebf 100644 --- a/dev/ast/rank.h +++ b/dev/ast/rank.h @@ -1,6 +1,6 @@ #pragma once -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { struct rank_t {}; } diff --git a/dev/ast/set.h b/dev/ast/set.h index e5e66f2ea..c562f1093 100644 --- a/dev/ast/set.h +++ b/dev/ast/set.h @@ -9,7 +9,7 @@ #include "../tuple_helper/tuple_traits.h" #include "../table_name_collector.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/ast/special_keywords.h b/dev/ast/special_keywords.h index f553d123b..518c8f7dd 100644 --- a/dev/ast/special_keywords.h +++ b/dev/ast/special_keywords.h @@ -1,6 +1,6 @@ #pragma once -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { struct current_time_t {}; struct current_date_t {}; diff --git a/dev/ast/upsert_clause.h b/dev/ast/upsert_clause.h index 287abaace..a23e16d31 100644 --- a/dev/ast/upsert_clause.h +++ b/dev/ast/upsert_clause.h @@ -7,7 +7,7 @@ #include "../functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { #if SQLITE_VERSION_NUMBER >= 3024000 template diff --git a/dev/ast/where.h b/dev/ast/where.h index 29ca50021..03c518a48 100644 --- a/dev/ast/where.h +++ b/dev/ast/where.h @@ -7,7 +7,7 @@ #include "../functional/cxx_type_traits_polyfill.h" #include "../serialize_result_type.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { struct where_string { diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index 7e166566e..38e3a3f71 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -22,7 +22,7 @@ #include "ast/set.h" #include "ast/match.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/backup.h b/dev/backup.h index 73b62250e..7b89ec900 100644 --- a/dev/backup.h +++ b/dev/backup.h @@ -9,7 +9,7 @@ #include "error_code.h" #include "connection_holder.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/carray.h b/dev/carray.h index f0c7ab73a..d4984463f 100644 --- a/dev/carray.h +++ b/dev/carray.h @@ -19,7 +19,7 @@ #if SQLITE_VERSION_NUMBER >= 3020000 #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES inline constexpr orm_pointer_type auto carray_pointer_tag = "carray"_pointer_type; diff --git a/dev/collate_argument.h b/dev/collate_argument.h index 720886622..6d83b663f 100644 --- a/dev/collate_argument.h +++ b/dev/collate_argument.h @@ -1,6 +1,6 @@ #pragma once -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/column_pointer.h b/dev/column_pointer.h index c2b6b75bd..89764b960 100644 --- a/dev/column_pointer.h +++ b/dev/column_pointer.h @@ -10,7 +10,7 @@ #include "alias_traits.h" #include "tags.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { /** * This class is used to store explicit mapped type T and its column descriptor (member pointer/getter/setter). diff --git a/dev/conditions.h b/dev/conditions.h index e1a1fccdc..2ad332065 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -25,7 +25,7 @@ #include "type_printer.h" #include "literal.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { @@ -844,7 +844,7 @@ namespace sqlite_orm { // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace // to facilitate ADL (Argument Dependent Lookup) - namespace internal { + _EXPORT_SQLITE_ORM namespace internal { template< class T, std::enable_if_t, is_operator_argument>::value, diff --git a/dev/constraints.h b/dev/constraints.h index eed1db455..a66611caf 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -18,7 +18,7 @@ #include "table_type_of.h" #include "type_printer.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/core_functions.h b/dev/core_functions.h index 4f4c8bd87..01fff897f 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -17,7 +17,7 @@ #include "table_reference.h" #include "ast/into.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { using int64 = sqlite_int64; using uint64 = sqlite_uint64; @@ -2040,7 +2040,7 @@ namespace sqlite_orm { // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace // to facilitate ADL (Argument Dependent Lookup) - namespace internal { + _EXPORT_SQLITE_ORM namespace internal { template, diff --git a/dev/cte_moniker.h b/dev/cte_moniker.h index 79e623ef9..f6d6c24f5 100644 --- a/dev/cte_moniker.h +++ b/dev/cte_moniker.h @@ -15,7 +15,7 @@ #include "alias.h" #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { /** diff --git a/dev/cte_storage.h b/dev/cte_storage.h index 61dca9102..f933d94c9 100644 --- a/dev/cte_storage.h +++ b/dev/cte_storage.h @@ -62,7 +62,7 @@ namespace sqlite_orm { // aliased column expressions, explicit or implicitly numbered template = true> - static auto make_cte_column(std::string name, const ColRef& /*finalColRef*/) { + auto make_cte_column(std::string name, const ColRef& /*finalColRef*/) { using object_type = aliased_field, F>; return sqlite_orm::make_column<>(std::move(name), &object_type::field); @@ -70,7 +70,7 @@ namespace sqlite_orm { // F O::* template = true> - static auto make_cte_column(std::string name, const ColRef& finalColRef) { + auto make_cte_column(std::string name, const ColRef& finalColRef) { using object_type = table_type_of_t; using column_type = column_t; diff --git a/dev/cte_types.h b/dev/cte_types.h index 7594da147..20ee296c3 100644 --- a/dev/cte_types.h +++ b/dev/cte_types.h @@ -10,7 +10,7 @@ #include "tuple_helper/tuple_fy.h" #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/eponymous_vtabs/dbstat.h b/dev/eponymous_vtabs/dbstat.h index 1b674ed9b..4a077faf2 100644 --- a/dev/eponymous_vtabs/dbstat.h +++ b/dev/eponymous_vtabs/dbstat.h @@ -7,7 +7,7 @@ #include "../schema/column.h" #include "../schema/table.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB struct dbstat { std::string name; diff --git a/dev/error_code.h b/dev/error_code.h index 7410c2687..263d37190 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -7,7 +7,7 @@ #include // std::ostringstream #include -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** @short Enables classifying sqlite error codes. @@ -50,7 +50,7 @@ namespace std { struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; } -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { class orm_error_category : public std::error_category { public: diff --git a/dev/expression.h b/dev/expression.h index 657bf56e2..1ef926824 100644 --- a/dev/expression.h +++ b/dev/expression.h @@ -9,7 +9,7 @@ #include "functional/cxx_type_traits_polyfill.h" #include "tags.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/field_printer.h b/dev/field_printer.h index 6bd990348..e6ba95bb3 100644 --- a/dev/field_printer.h +++ b/dev/field_printer.h @@ -15,7 +15,7 @@ #include "is_std_ptr.h" #include "type_traits.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * Is used to print members mapped to objects in storage_t::dump member function. diff --git a/dev/function.h b/dev/function.h index b26335ffc..2673de8b6 100644 --- a/dev/function.h +++ b/dev/function.h @@ -15,7 +15,7 @@ #include "type_traits.h" #include "tags.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { struct arg_values; diff --git a/dev/functional/config.h b/dev/functional/config.h index aa2ee2cba..d1e8b6cc2 100644 --- a/dev/functional/config.h +++ b/dev/functional/config.h @@ -2,6 +2,12 @@ #include "cxx_universal.h" +#ifdef _BUILD_SQLITE_ORM_MODULE +#define _EXPORT_SQLITE_ORM export +#else +#define _EXPORT_SQLITE_ORM +#endif + #if SQLITE_ORM_HAS_INCLUDE() #include #endif diff --git a/dev/get_prepared_statement.h b/dev/get_prepared_statement.h index 10848fa43..08b545151 100644 --- a/dev/get_prepared_statement.h +++ b/dev/get_prepared_statement.h @@ -12,7 +12,7 @@ #include "node_tuple.h" #include "expression_object_type.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { template auto& get(internal::prepared_statement_t>& statement) { diff --git a/dev/indexed_column.h b/dev/indexed_column.h index e8bd3f1d4..6638429a4 100644 --- a/dev/indexed_column.h +++ b/dev/indexed_column.h @@ -6,7 +6,7 @@ #include "functional/cxx_universal.h" #include "ast/where.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/is_std_ptr.h b/dev/is_std_ptr.h index dc48b1dc6..b884ec99a 100644 --- a/dev/is_std_ptr.h +++ b/dev/is_std_ptr.h @@ -2,7 +2,7 @@ #include #include -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * Specialization for optional type (std::shared_ptr / std::unique_ptr). diff --git a/dev/journal_mode.h b/dev/journal_mode.h index b72defda1..d2dcd07c1 100644 --- a/dev/journal_mode.h +++ b/dev/journal_mode.h @@ -13,7 +13,7 @@ #undef DELETE #endif -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * Caps case because of: diff --git a/dev/limit_accessor.h b/dev/limit_accessor.h index e87adbf82..e8404e823 100644 --- a/dev/limit_accessor.h +++ b/dev/limit_accessor.h @@ -7,7 +7,7 @@ #include "connection_holder.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/literal.h b/dev/literal.h index 65cb1b265..b58c3c79b 100644 --- a/dev/literal.h +++ b/dev/literal.h @@ -1,6 +1,6 @@ #pragma once -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { /* diff --git a/dev/member_traits/member_traits.h b/dev/member_traits/member_traits.h index 652d58ade..cb2042628 100644 --- a/dev/member_traits/member_traits.h +++ b/dev/member_traits/member_traits.h @@ -5,7 +5,7 @@ #include "../functional/cxx_universal.h" #include "../functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { // SFINAE friendly trait to get a member object pointer's field type template diff --git a/dev/object_from_column_builder.h b/dev/object_from_column_builder.h index 86aae7f40..4777cb7ae 100644 --- a/dev/object_from_column_builder.h +++ b/dev/object_from_column_builder.h @@ -11,7 +11,7 @@ #include "schema/column.h" #include "storage_lookup.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/operators.h b/dev/operators.h index d6cd2b225..0cb0d3676 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -8,7 +8,7 @@ #include "tags.h" #include "serialize_result_type.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/pointer_value.h b/dev/pointer_value.h index 9b21294da..1bd819999 100644 --- a/dev/pointer_value.h +++ b/dev/pointer_value.h @@ -13,7 +13,7 @@ #include "xdestroy_handling.h" #if SQLITE_VERSION_NUMBER >= 3020000 -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES namespace internal { template @@ -184,7 +184,7 @@ namespace sqlite_orm { #endif } -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * Wrap a pointer, its type and its deleter function for binding it to a statement. * diff --git a/dev/pragma.h b/dev/pragma.h index c30008cbc..47efe0325 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -14,7 +14,7 @@ #include "util.h" #include "serializing_util.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { struct storage_base; diff --git a/dev/prepared_statement.h b/dev/prepared_statement.h index 9dbe7d0d1..079dc8f93 100644 --- a/dev/prepared_statement.h +++ b/dev/prepared_statement.h @@ -20,7 +20,7 @@ #include "ast/upsert_clause.h" #include "ast/set.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/row_extractor.h b/dev/row_extractor.h index 42becde10..783d3a889 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -30,7 +30,7 @@ #include "is_std_ptr.h" #include "type_traits.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * Helper for casting values originating from SQL to C++ typed values, usually from rows of a result set. diff --git a/dev/rowid.h b/dev/rowid.h index a99fe6d51..a8994fe79 100644 --- a/dev/rowid.h +++ b/dev/rowid.h @@ -2,7 +2,7 @@ #include // std::string -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/schema/column.h b/dev/schema/column.h index 45c95a367..42b2c28e8 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -15,7 +15,7 @@ #include "../type_is_nullable.h" #include "../constraints.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/schema/index.h b/dev/schema/index.h index a846e51d9..1814faaf7 100644 --- a/dev/schema/index.h +++ b/dev/schema/index.h @@ -9,7 +9,7 @@ #include "../indexed_column.h" #include "../table_type_of.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/schema/table.h b/dev/schema/table.h index 055e70be5..fb957631a 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -24,7 +24,7 @@ #include "../table_info.h" #include "column.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/schema/triggers.h b/dev/schema/triggers.h index a090c4015..b924219a8 100644 --- a/dev/schema/triggers.h +++ b/dev/schema/triggers.h @@ -13,7 +13,7 @@ // the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) // It could be an interesting feature to bring to sqlite_orm that other libraries don't have ? -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { enum class trigger_timing { trigger_before, trigger_after, trigger_instead_of }; enum class trigger_type { trigger_delete, trigger_insert, trigger_update }; diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 4b7226b1e..07bb4a6c8 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -22,7 +22,7 @@ #include "alias_traits.h" #include "cte_moniker.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED diff --git a/dev/sqlite_schema_table.h b/dev/sqlite_schema_table.h index 9dfda257c..da1818826 100644 --- a/dev/sqlite_schema_table.h +++ b/dev/sqlite_schema_table.h @@ -7,7 +7,7 @@ #include "column_pointer.h" #include "alias.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * SQLite's "schema table" that stores the schema for a database. * diff --git a/dev/statement_binder.h b/dev/statement_binder.h index 0473f7943..0d5c32d56 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -26,7 +26,7 @@ #include "xdestroy_handling.h" #include "pointer_value.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * Helper class used for binding fields to sqlite3 statements. diff --git a/dev/statement_finalizer.h b/dev/statement_finalizer.h index d94950080..a9d2d98e9 100644 --- a/dev/statement_finalizer.h +++ b/dev/statement_finalizer.h @@ -4,7 +4,7 @@ #include // std::unique_ptr #include // std::integral_constant -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * Guard class which finalizes `sqlite3_stmt` in dtor diff --git a/dev/storage.h b/dev/storage.h index a7bf160df..1c65adce2 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -58,7 +58,7 @@ #include "util.h" #include "serializing_util.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { /* diff --git a/dev/storage_base.h b/dev/storage_base.h index ddad55592..4360fa246 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -31,7 +31,7 @@ #include "udf_proxy.h" #include "serializing_util.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/sync_schema_result.h b/dev/sync_schema_result.h index e4df5cc96..e8d162edd 100644 --- a/dev/sync_schema_result.h +++ b/dev/sync_schema_result.h @@ -2,7 +2,7 @@ #include -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { enum class sync_schema_result { diff --git a/dev/table_info.h b/dev/table_info.h index 087a3c6f2..0fc5fa614 100644 --- a/dev/table_info.h +++ b/dev/table_info.h @@ -4,7 +4,7 @@ #include "functional/cxx_universal.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { struct table_info { int cid = 0; diff --git a/dev/table_reference.h b/dev/table_reference.h index 8b20c9162..412f2838b 100644 --- a/dev/table_reference.h +++ b/dev/table_reference.h @@ -7,7 +7,7 @@ #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { /* * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. diff --git a/dev/type_is_nullable.h b/dev/type_is_nullable.h index 9f161c110..b959a360d 100644 --- a/dev/type_is_nullable.h +++ b/dev/type_is_nullable.h @@ -6,7 +6,7 @@ #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * This is class that tells `sqlite_orm` that type is nullable. Nullable types diff --git a/dev/type_printer.h b/dev/type_printer.h index 79df36b03..b5cfe08b0 100644 --- a/dev/type_printer.h +++ b/dev/type_printer.h @@ -9,7 +9,7 @@ #include "type_traits.h" #include "is_std_ptr.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) diff --git a/dev/type_traits.h b/dev/type_traits.h index e55c22d8e..26fc97815 100644 --- a/dev/type_traits.h +++ b/dev/type_traits.h @@ -10,7 +10,7 @@ #include "functional/cxx_core_features.h" #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { // C++ generic traits used throughout the library namespace internal { template diff --git a/dev/util.h b/dev/util.h index f162eeda9..3ab47d7e0 100644 --- a/dev/util.h +++ b/dev/util.h @@ -6,7 +6,7 @@ #include "error_code.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * Escape the provided character in the given string by doubling it. diff --git a/dev/values.h b/dev/values.h index 68ce9639d..2643d47b2 100644 --- a/dev/values.h +++ b/dev/values.h @@ -7,7 +7,7 @@ #include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/xdestroy_handling.h b/dev/xdestroy_handling.h index 0b4b40afd..f9d8e63ca 100644 --- a/dev/xdestroy_handling.h +++ b/dev/xdestroy_handling.h @@ -8,14 +8,14 @@ #include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { using xdestroy_fn_t = void (*)(void*); using null_xdestroy_t = std::integral_constant; SQLITE_ORM_INLINE_VAR constexpr null_xdestroy_t null_xdestroy_f{}; } -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { #ifdef SQLITE_ORM_CONCEPTS_SUPPORTED /** @@ -173,7 +173,7 @@ namespace sqlite_orm { } } -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED /** diff --git a/examples/iteration.cpp b/examples/iteration.cpp index 1ca54b8d3..a7c8bcc43 100644 --- a/examples/iteration.cpp +++ b/examples/iteration.cpp @@ -1,7 +1,9 @@ - -#include +#include +#include +#include #include #include +#include using std::cout; using std::endl; diff --git a/modules/sqlite_orm.ixx b/modules/sqlite_orm.ixx new file mode 100644 index 000000000..f40e3bc1a --- /dev/null +++ b/modules/sqlite_orm.ixx @@ -0,0 +1,13 @@ +// In a module-file, the optional `module;` must appear first; see [cpp.pre]. +module; + +#define _BUILD_SQLITE_ORM_MODULE + +export module sqlite_orm; + +#pragma warning(push) +#pragma warning(disable : 5244) // '#include ' in the purview of module 'sqlite_orm' appears erroneous. + +#include + +#pragma warning(pop) From 2adf82374816efc85c72efef771a874fc66d60bc Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 17 Jul 2024 21:09:38 +0200 Subject: [PATCH 002/170] Added includes of dependent headers * Added missing includes of dependent internal headers * Cleaned up amalgamation files to only include the public facing high-level headers --- dev/alias.h | 2 +- dev/arithmetic_tag.h | 1 + dev/ast/match.h | 1 + dev/carray.h | 16 +- dev/collate_argument.h | 1 - dev/conditions.h | 5 +- dev/constraints.h | 2 +- dev/core_functions.h | 41 +- dev/error_code.h | 11 +- dev/expression.h | 3 +- dev/field_printer.h | 2 +- dev/functional/cxx_functional_polyfill.h | 1 + dev/get_prepared_statement.h | 22 +- dev/implementations/storage_definitions.h | 5 +- dev/implementations/table_definitions.h | 1 + dev/indexed_column.h | 1 - dev/is_std_ptr.h | 1 + dev/operators.h | 1 - dev/pointer_value.h | 34 +- dev/pragma.h | 1 + dev/prepared_statement.h | 4 +- dev/row_extractor.h | 14 +- dev/schema/column.h | 5 +- dev/schema/table.h | 1 + dev/select_constraints.h | 8 +- dev/serializing_util.h | 2 +- dev/statement_binder.h | 6 +- dev/statement_serializer.h | 10 +- dev/storage.h | 1 + dev/storage_base.h | 2 + dev/storage_impl.h | 2 + dev/table_type_of.h | 1 + dev/tuple_helper/same_or_void.h | 1 + dev/tuple_helper/tuple_filter.h | 2 +- dev/udf_proxy.h | 2 +- dev/values.h | 1 - examples/iteration.cpp | 6 +- include/sqlite_orm/sqlite_orm.h | 25386 ++++++++-------- .../sqlite_orm/sqlite_orm.h | 24 +- third_party/amalgamate/config.json | 24 +- 40 files changed, 12819 insertions(+), 12835 deletions(-) diff --git a/dev/alias.h b/dev/alias.h index e964a6d68..784d3dde6 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -1,6 +1,6 @@ #pragma once -#include // std::enable_if, std::is_same, std::conditional +#include // std::enable_if, std::is_same #include // std::make_index_sequence, std::move #include // std::string #include // std::stringstream diff --git a/dev/arithmetic_tag.h b/dev/arithmetic_tag.h index c7dd076fc..5c0d37a2d 100644 --- a/dev/arithmetic_tag.h +++ b/dev/arithmetic_tag.h @@ -1,4 +1,5 @@ #pragma once + #include // std::is_integral #include "functional/mpl/conditional.h" diff --git a/dev/ast/match.h b/dev/ast/match.h index b60bffa94..f83dd974b 100644 --- a/dev/ast/match.h +++ b/dev/ast/match.h @@ -1,4 +1,5 @@ #pragma once +#include _EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { diff --git a/dev/carray.h b/dev/carray.h index d4984463f..28144df6b 100644 --- a/dev/carray.h +++ b/dev/carray.h @@ -41,12 +41,12 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - carray_pointer_binding bind_carray_pointer(P* p, D d) noexcept { + carray_pointer_binding bind_carray_pointer(P * p, D d) noexcept { return bind_pointer(p, std::move(d)); } template - static_carray_pointer_binding

bind_carray_pointer_statically(P* p) noexcept { + static_carray_pointer_binding

bind_carray_pointer_statically(P * p) noexcept { return bind_pointer_statically(p); } @@ -58,14 +58,14 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { */ template [[deprecated("Use the better named function `bind_carray_pointer(...)`")]] carray_pointer_binding - bindable_carray_pointer(P* p, D d) noexcept { + bindable_carray_pointer(P * p, D d) noexcept { return bind_pointer(p, std::move(d)); } template [[deprecated( "Use the better named function `bind_carray_pointer_statically(...)` ")]] static_carray_pointer_binding

- statically_bindable_carray_pointer(P* p) noexcept { + statically_bindable_carray_pointer(P * p) noexcept { return bind_pointer_statically(p); } #else @@ -89,7 +89,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - carray_pointer_binding bind_carray_pointer(P* p, D d) noexcept { + carray_pointer_binding bind_carray_pointer(P * p, D d) noexcept { return bind_pointer(p, std::move(d)); } @@ -100,20 +100,20 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. */ template - static_carray_pointer_binding

bind_carray_pointer_statically(P* p) noexcept { + static_carray_pointer_binding

bind_carray_pointer_statically(P * p) noexcept { return bind_pointer_statically(p); } template [[deprecated("Use the better named function `bind_carray_pointer(...)`")]] carray_pointer_binding - bindable_carray_pointer(P* p, D d) noexcept { + bindable_carray_pointer(P * p, D d) noexcept { return bind_carray_pointer(p, std::move(d)); } template [[deprecated( "Use the better named function `bind_carray_pointer_statically(...)` ")]] static_carray_pointer_binding

- statically_bindable_carray_pointer(P* p) noexcept { + statically_bindable_carray_pointer(P * p) noexcept { return bind_carray_pointer_statically(p); } #endif diff --git a/dev/collate_argument.h b/dev/collate_argument.h index 6d83b663f..f717717f9 100644 --- a/dev/collate_argument.h +++ b/dev/collate_argument.h @@ -10,5 +10,4 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { rtrim, }; } - } diff --git a/dev/conditions.h b/dev/conditions.h index 2ad332065..e4b7b67fd 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -6,6 +6,7 @@ #include // std::tuple #include // std::move, std::forward #include // std::stringstream +#include // std::flush #include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" @@ -1234,8 +1235,8 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * } */ template - internal::dynamic_order_by_t> - dynamic_order_by(const S& storage) { + internal::dynamic_order_by_t> dynamic_order_by( + const S& storage) { internal::serializer_context_builder builder(storage); return builder(); } diff --git a/dev/constraints.h b/dev/constraints.h index a66611caf..ee1971623 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -1,10 +1,10 @@ #pragma once +#include // std::is_base_of, std::false_type, std::true_type #include // std::system_error #include // std::ostream #include // std::string #include // std::tuple -#include // std::is_base_of, std::false_type, std::true_type #include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" diff --git a/dev/core_functions.h b/dev/core_functions.h index 01fff897f..c051cc362 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -1670,12 +1670,12 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { */ template auto coalesce(Args... args) - -> internal::built_in_function_t::value, - std::common_type...>, - polyfill::type_identity>::type, - internal::coalesce_string, - Args...> { + ->internal::built_in_function_t::value, + std::common_type...>, + polyfill::type_identity>::type, + internal::coalesce_string, + Args...> { return {std::make_tuple(std::forward(args)...)}; } @@ -1683,14 +1683,15 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull */ template - auto ifnull(X x, Y y) -> internal::built_in_function_t< - typename mpl::conditional_t< // choose R or common type - std::is_void::value, - std::common_type, internal::field_type_or_type_t>, - polyfill::type_identity>::type, - internal::ifnull_string, - X, - Y> { + auto ifnull(X x, Y y) + ->internal::built_in_function_t< + typename mpl::conditional_t< // choose R or common type + std::is_void::value, + std::common_type, internal::field_type_or_type_t>, + polyfill::type_identity>::type, + internal::ifnull_string, + X, + Y> { return {std::make_tuple(std::move(x), std::move(y))}; } @@ -1885,8 +1886,10 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * The return type is the type of the first argument. */ template - internal::built_in_function_t, internal::max_string, X, Y, Rest...> - max(X x, Y y, Rest... rest) { + internal::built_in_function_t, internal::max_string, X, Y, Rest...> max( + X x, + Y y, + Rest... rest) { return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; } @@ -1895,8 +1898,10 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * The return type is the type of the first argument. */ template - internal::built_in_function_t, internal::min_string, X, Y, Rest...> - min(X x, Y y, Rest... rest) { + internal::built_in_function_t, internal::min_string, X, Y, Rest...> min( + X x, + Y y, + Rest... rest) { return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; } diff --git a/dev/error_code.h b/dev/error_code.h index 263d37190..41ad99f13 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -39,7 +39,6 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { value_is_null, no_tables_specified, }; - } namespace std { @@ -134,7 +133,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - std::string get_error_message(sqlite3* db, T&&... args) { + std::string get_error_message(sqlite3 * db, T && ... args) { std::ostringstream stream; using unpack = int[]; (void)unpack{0, (stream << args, 0)...}; @@ -143,7 +142,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - [[noreturn]] void throw_error(sqlite3* db, T&&... args) { + [[noreturn]] void throw_error(sqlite3 * db, T && ... args) { throw std::system_error{sqlite_errc(sqlite3_errcode(db)), get_error_message(db, std::forward(args)...)}; } @@ -151,7 +150,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { return {sqlite_errc(ev)}; } - inline std::system_error sqlite_to_system_error(sqlite3* db) { + inline std::system_error sqlite_to_system_error(sqlite3 * db) { return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(db)}; } @@ -159,11 +158,11 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { throw sqlite_to_system_error(ev); } - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3 * db) { throw sqlite_to_system_error(db); } - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt * stmt) { throw sqlite_to_system_error(sqlite3_db_handle(stmt)); } } diff --git a/dev/expression.h b/dev/expression.h index 1ef926824..496c643fe 100644 --- a/dev/expression.h +++ b/dev/expression.h @@ -5,9 +5,10 @@ #include // std::move, std::forward, std::declval #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" +#include "functional/cxx_universal.h" // ::nullptr_t #include "functional/cxx_type_traits_polyfill.h" #include "tags.h" +#include "operators.h" _EXPORT_SQLITE_ORM namespace sqlite_orm { diff --git a/dev/field_printer.h b/dev/field_printer.h index e6ba95bb3..71d6aba15 100644 --- a/dev/field_printer.h +++ b/dev/field_printer.h @@ -10,7 +10,7 @@ #endif #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" +#include "functional/cxx_universal.h" // ::nullptr_t #include "functional/cxx_type_traits_polyfill.h" #include "is_std_ptr.h" #include "type_traits.h" diff --git a/dev/functional/cxx_functional_polyfill.h b/dev/functional/cxx_functional_polyfill.h index 723923633..474ab2f38 100644 --- a/dev/functional/cxx_functional_polyfill.h +++ b/dev/functional/cxx_functional_polyfill.h @@ -1,4 +1,5 @@ #pragma once + #include #if __cpp_lib_invoke < 201411L #include // std::enable_if, std::is_member_object_pointer, std::is_member_function_pointer diff --git a/dev/get_prepared_statement.h b/dev/get_prepared_statement.h index 08b545151..fa8d26f66 100644 --- a/dev/get_prepared_statement.h +++ b/dev/get_prepared_statement.h @@ -15,7 +15,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { template - auto& get(internal::prepared_statement_t>& statement) { + auto& get(internal::prepared_statement_t> & statement) { return std::get(statement.expression.range); } @@ -25,7 +25,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t>& statement) { + auto& get(internal::prepared_statement_t> & statement) { return std::get(statement.expression.range); } @@ -35,7 +35,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t>& statement) { + auto& get(internal::prepared_statement_t> & statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -45,7 +45,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t>& statement) { + auto& get(internal::prepared_statement_t> & statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -56,7 +56,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - auto& get(internal::prepared_statement_t>& statement) { + auto& get(internal::prepared_statement_t> & statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -67,7 +67,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - auto& get(internal::prepared_statement_t>& statement) { + auto& get(internal::prepared_statement_t> & statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -77,7 +77,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t>& statement) { + auto& get(internal::prepared_statement_t> & statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); return internal::get_ref(statement.expression.object); } @@ -89,7 +89,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t>& statement) { + auto& get(internal::prepared_statement_t> & statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.expression.obj); } @@ -101,7 +101,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t>& statement) { + auto& get(internal::prepared_statement_t> & statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); return internal::get_ref(statement.expression.object); } @@ -113,7 +113,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t>& statement) { + auto& get(internal::prepared_statement_t> & statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.expression.object); } @@ -150,7 +150,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t& statement) { + auto& get(internal::prepared_statement_t & statement) { using statement_type = std::remove_reference_t; using expression_type = internal::expression_type_t; using node_tuple = internal::node_tuple_t; diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index 30d498b13..80d777a21 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -3,11 +3,14 @@ * e.g. usage of the dbstat table. */ #pragma once + #include // std::is_same -#include +#include // std::stringstream +#include // std::flush #include // std::reference_wrapper, std::cref #include // std::find_if, std::ranges::find +#include "../type_traits.h" #include "../sqlite_schema_table.h" #include "../eponymous_vtabs/dbstat.h" #include "../type_traits.h" diff --git a/dev/implementations/table_definitions.h b/dev/implementations/table_definitions.h index a869d2272..eb983961a 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -3,6 +3,7 @@ * this file is also used to provide definitions of interface methods 'hitting the database'. */ #pragma once + #include // std::decay_t #include // std::move #include // std::find_if, std::ranges::find diff --git a/dev/indexed_column.h b/dev/indexed_column.h index 6638429a4..84e8785e1 100644 --- a/dev/indexed_column.h +++ b/dev/indexed_column.h @@ -66,5 +66,4 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { internal::indexed_column_t indexed_column(C column_or_expression) { return {std::move(column_or_expression)}; } - } diff --git a/dev/is_std_ptr.h b/dev/is_std_ptr.h index b884ec99a..dca7b8140 100644 --- a/dev/is_std_ptr.h +++ b/dev/is_std_ptr.h @@ -1,4 +1,5 @@ #pragma once + #include #include diff --git a/dev/operators.h b/dev/operators.h index 0cb0d3676..90af21d76 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -272,5 +272,4 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { internal::assign_t assign(L l, R r) { return {std::move(l), std::move(r)}; } - } diff --git a/dev/pointer_value.h b/dev/pointer_value.h index 1bd819999..a6cd86c47 100644 --- a/dev/pointer_value.h +++ b/dev/pointer_value.h @@ -193,7 +193,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - auto bind_pointer(P* p, D d) noexcept -> pointer_binding { + auto bind_pointer(P * p, D d) noexcept -> pointer_binding { return {p, std::move(d)}; } @@ -203,25 +203,27 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto bind_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept -> B { + auto bind_pointer(typename B::qualified_type * p, typename B::deleter_type d = {}) noexcept -> B { return B{p, std::move(d)}; } template - [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding - bindable_pointer(P* p, D d) noexcept { + [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding bindable_pointer( + P * p, + D d) noexcept { return bind_pointer(p, std::move(d)); } template - [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding - bindable_pointer(std::unique_ptr p) noexcept { + [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding bindable_pointer( + std::unique_ptr p) noexcept { return bind_pointer(p.release(), p.get_deleter()); } template - [[deprecated("Use the better named function `bind_pointer(...)`")]] B - bindable_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept { + [[deprecated("Use the better named function `bind_pointer(...)`")]] B bindable_pointer( + typename B::qualified_type * p, + typename B::deleter_type d = {}) noexcept { return bind_pointer(p, std::move(d)); } @@ -234,7 +236,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - auto bind_pointer(P* p, D d) noexcept -> pointer_binding { + auto bind_pointer(P * p, D d) noexcept -> pointer_binding { return {p, std::move(d)}; } @@ -251,26 +253,26 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. */ template - auto bind_pointer_statically(P* p) noexcept -> static_pointer_binding { + auto bind_pointer_statically(P * p) noexcept -> static_pointer_binding { return bind_pointer(p, null_xdestroy_f); } template - B bind_pointer_statically(typename B::qualified_type* p, + B bind_pointer_statically(typename B::qualified_type * p, typename B::deleter_type* /*exposition*/ = nullptr) noexcept { return bind_pointer(p); } template [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] static_pointer_binding - statically_bindable_pointer(P* p) noexcept { + statically_bindable_pointer(P * p) noexcept { return bind_pointer(p, null_xdestroy_f); } template - [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] B - statically_bindable_pointer(typename B::qualified_type* p, - typename B::deleter_type* /*exposition*/ = nullptr) noexcept { + [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] B statically_bindable_pointer( + typename B::qualified_type * p, + typename B::deleter_type* /*exposition*/ = nullptr) noexcept { return bind_pointer(p); } @@ -282,7 +284,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. */ template - auto bind_pointer_statically(P* p) noexcept -> static_pointer_binding { + auto bind_pointer_statically(P * p) noexcept -> static_pointer_binding { return bind_pointer(p, null_xdestroy_f); } #endif diff --git a/dev/pragma.h b/dev/pragma.h index 47efe0325..29ef435e0 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -6,6 +6,7 @@ #include // std::shared_ptr #include // std::vector #include +#include // std::flush #include "error_code.h" #include "row_extractor.h" diff --git a/dev/prepared_statement.h b/dev/prepared_statement.h index 079dc8f93..910f06614 100644 --- a/dev/prepared_statement.h +++ b/dev/prepared_statement.h @@ -739,7 +739,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { template>, class... Args> - auto get_all(Args&&... conditions) { + auto get_all(Args && ... conditions) { return get_all, R>(std::forward(conditions)...); } #endif @@ -809,7 +809,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { template>, class... Args> - auto get_all_optional(Args&&... conditions) { + auto get_all_optional(Args && ... conditions) { return get_all_optional, R>(std::forward(conditions)...); } #endif diff --git a/dev/row_extractor.h b/dev/row_extractor.h index 783d3a889..3f5a74a2a 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -2,7 +2,8 @@ #include #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if -#include // atof, atoi, atoll +#include // std::atof, std::atoi, std::atoll +#include // std::strlen #include // std::system_error #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT @@ -10,7 +11,6 @@ #include // std::codecvt_utf8_utf16 #endif #include // std::vector -#include // strlen #include // std::copy #include // std::back_inserter #include // std::tuple, std::tuple_size, std::tuple_element @@ -18,7 +18,7 @@ #include #endif -#include "functional/cxx_universal.h" +#include "functional/cxx_universal.h" // ::nullptr_t, ::size_t #include "functional/cxx_functional_polyfill.h" #include "functional/static_magic.h" #include "tuple_helper/tuple_transformer.h" @@ -175,7 +175,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { using tag = arithmetic_tag_t; V extract(const char* columnText, const int_or_smaller_tag&) const { - return static_cast(atoi(columnText)); + return static_cast(std::atoi(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { @@ -187,7 +187,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } V extract(const char* columnText, const bigint_tag&) const { - return static_cast(atoll(columnText)); + return static_cast(std::atoll(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { @@ -199,7 +199,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } V extract(const char* columnText, const real_tag&) const { - return static_cast(atof(columnText)); + return static_cast(std::atof(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { @@ -389,7 +389,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { template<> struct row_extractor, void> { std::vector extract(const char* columnText) const { - return {columnText, columnText + (columnText ? ::strlen(columnText) : 0)}; + return {columnText, columnText + (columnText ? std::strlen(columnText) : 0)}; } std::vector extract(sqlite3_stmt* stmt, int columnIndex) const { diff --git a/dev/schema/column.h b/dev/schema/column.h index 42b2c28e8..2610fa748 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -147,8 +147,9 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * Factory function for a column definition from a member object pointer of the object to be mapped. */ template = true> - internal::column_t - make_column(std::string name, M memberPointer, Op... constraints) { + internal::column_t make_column(std::string name, + M memberPointer, + Op... constraints) { static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, diff --git a/dev/schema/table.h b/dev/schema/table.h index fb957631a..90be234a0 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -22,6 +22,7 @@ #include "../alias_traits.h" #include "../constraints.h" #include "../table_info.h" +#include "index.h" #include "column.h" _EXPORT_SQLITE_ORM namespace sqlite_orm { diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 07bb4a6c8..1e84668ce 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -3,7 +3,7 @@ #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include #endif -#include // std::remove_const +#include // std::remove_cvref, std::is_convertible, std::is_same, std::is_member_pointer #include // std::string #include // std::move #include // std::tuple, std::get, std::tuple_size @@ -21,6 +21,7 @@ #include "core_functions.h" #include "alias_traits.h" #include "cte_moniker.h" +#include "schema/column.h" _EXPORT_SQLITE_ORM namespace sqlite_orm { @@ -703,8 +704,9 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * @note The use of RECURSIVE does not force common table expressions to be recursive. */ template = true> - internal::with_t, CTEs...> - with_recursive(internal::common_table_expressions ctes, Compound sel) { + internal::with_t, CTEs...> with_recursive( + internal::common_table_expressions ctes, + Compound sel) { return {true, std::move(ctes), sqlite_orm::select(std::move(sel))}; } diff --git a/dev/serializing_util.h b/dev/serializing_util.h index abfcfe0d0..00dfc2097 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -5,7 +5,7 @@ #include #include #include -#include // std::exchange, std::tuple_size +#include // std::exchange, std::tuple_size, std::make_index_sequence #include "functional/cxx_universal.h" // ::size_t #include "functional/cxx_type_traits_polyfill.h" diff --git a/dev/statement_binder.h b/dev/statement_binder.h index 0d5c32d56..20cedc947 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -5,7 +5,7 @@ #include // std::default_delete #include // std::string, std::wstring #include // std::vector -#include // ::strncpy, ::strlen +#include // std::strncpy, std::strlen #include "functional/cxx_string_view.h" #ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED #include // ::wcsncpy, ::wcslen @@ -140,7 +140,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { auto stringData = this->string_data(value); auto dataCopy = new char[stringData.second + 1]; constexpr auto deleter = std::default_delete{}; - ::strncpy(dataCopy, stringData.first, stringData.second + 1); + std::strncpy(dataCopy, stringData.first, stringData.second + 1); sqlite3_result_text(context, dataCopy, stringData.second, obtain_xdestroy_for(deleter, dataCopy)); } @@ -155,7 +155,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } std::pair string_data(const char* s) const { - return {s, int(::strlen(s))}; + return {s, int(std::strlen(s))}; } #endif }; diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 0e7249257..c1f0f2c26 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -14,7 +14,7 @@ #include "functional/cxx_string_view.h" #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" +#include "functional/cxx_universal.h" // ::nullptr_t, ::size_t #include "functional/cxx_functional_polyfill.h" #include "functional/mpl.h" #include "tuple_helper/tuple_filter.h" @@ -28,7 +28,6 @@ #include "core_functions.h" #include "constraints.h" #include "conditions.h" -#include "schema/column.h" #include "indexed_column.h" #include "function.h" #include "prepared_statement.h" @@ -45,12 +44,13 @@ #include "serialize_result_type.h" #include "statement_binder.h" #include "values.h" -#include "schema/triggers.h" #include "table_type_of.h" -#include "schema/index.h" -#include "schema/table.h" #include "util.h" #include "error_code.h" +#include "schema/triggers.h" +#include "schema/column.h" +#include "schema/index.h" +#include "schema/table.h" namespace sqlite_orm { diff --git a/dev/storage.h b/dev/storage.h index 1c65adce2..4c33dae6e 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -7,6 +7,7 @@ #include // std::remove_reference, std::is_base_of, std::decay, std::false_type, std::true_type #include // std::identity #include // std::stringstream +#include // std::flush #include // std::map #include // std::vector #include // std::tuple_size, std::tuple, std::make_tuple, std::tie diff --git a/dev/storage_base.h b/dev/storage_base.h index 4360fa246..0ff06a637 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -5,6 +5,7 @@ #include // std::function, std::bind, std::bind_front #include // std::string #include // std::stringstream +#include // std::flush #include // std::move #include // std::system_error #include // std::vector @@ -30,6 +31,7 @@ #include "xdestroy_handling.h" #include "udf_proxy.h" #include "serializing_util.h" +#include "table_info.h" _EXPORT_SQLITE_ORM namespace sqlite_orm { diff --git a/dev/storage_impl.h b/dev/storage_impl.h index be629ced5..d364daba9 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -11,6 +11,8 @@ #include "type_traits.h" #include "select_constraints.h" #include "cte_types.h" +#include "schema/column.h" +#include "schema/table.h" #include "storage_lookup.h" // interface functions diff --git a/dev/table_type_of.h b/dev/table_type_of.h index 8dacf846d..52604d788 100644 --- a/dev/table_type_of.h +++ b/dev/table_type_of.h @@ -1,4 +1,5 @@ #pragma once + #include // std::enable_if, std::is_convertible namespace sqlite_orm { diff --git a/dev/tuple_helper/same_or_void.h b/dev/tuple_helper/same_or_void.h index ce2726084..c0bd482b1 100644 --- a/dev/tuple_helper/same_or_void.h +++ b/dev/tuple_helper/same_or_void.h @@ -1,4 +1,5 @@ #pragma once + #include // std::common_type namespace sqlite_orm { diff --git a/dev/tuple_helper/tuple_filter.h b/dev/tuple_helper/tuple_filter.h index 785c9a9a3..159a50b0f 100644 --- a/dev/tuple_helper/tuple_filter.h +++ b/dev/tuple_helper/tuple_filter.h @@ -1,7 +1,7 @@ #pragma once #include // std::integral_constant, std::index_sequence, std::conditional, std::declval -#include // std::tuple +#include // std::tuple, std::tuple_cat, std::tuple_element #include "../functional/cxx_universal.h" // ::size_t #include "../functional/mpl/conditional.h" diff --git a/dev/udf_proxy.h b/dev/udf_proxy.h index a750fe601..cf152de95 100644 --- a/dev/udf_proxy.h +++ b/dev/udf_proxy.h @@ -1,7 +1,7 @@ #pragma once #include -#include // assert +#include // assert macro #include // std::true_type, std::false_type #include // std::bad_alloc #include // std::allocator, std::allocator_traits, std::unique_ptr diff --git a/dev/values.h b/dev/values.h index 2643d47b2..2ddea6b30 100644 --- a/dev/values.h +++ b/dev/values.h @@ -40,5 +40,4 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { internal::dynamic_values_t values(std::vector vector) { return {{std::move(vector)}}; } - } diff --git a/examples/iteration.cpp b/examples/iteration.cpp index a7c8bcc43..e83ae3a09 100644 --- a/examples/iteration.cpp +++ b/examples/iteration.cpp @@ -37,9 +37,9 @@ int main(int, char**) { storage.insert(MarvelHero{-1, "Natasha Romanoff", "Black widow"}); storage.insert(MarvelHero{-1, "Groot", "I am Groot!"}); - cout << "Heros count = " << storage.count() << endl; + cout << "Heroes count = " << storage.count() << endl; - // iterate through heros - iteration takes less memory than `get_all` because + // iterate through heroes - iteration takes less memory than `get_all` because // iteration fetches row by row once it is needed. If you break at any iteration // statement will be cleared without fetching remaining rows. for(auto& hero: storage.iterate()) { @@ -53,7 +53,7 @@ int main(int, char**) { cout << "hero = " << storage.dump(hero) << endl; } - cout << "Heros with LENGTH(name) < 6 :" << endl; + cout << "Heroes with LENGTH(name) < 6 :" << endl; for(auto& hero: storage.iterate(where(length(&MarvelHero::name) < 6))) { cout << "hero = " << storage.dump(hero) << endl; } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index fc45ba9f2..ca7062344 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -194,6 +194,12 @@ using std::nullptr_t; #define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS #endif +#ifdef _BUILD_SQLITE_ORM_MODULE +#define _EXPORT_SQLITE_ORM export +#else +#define _EXPORT_SQLITE_ORM +#endif + #if SQLITE_ORM_HAS_INCLUDE() #include #endif @@ -278,16 +284,42 @@ namespace sqlite_orm { } #pragma once -#include // std::enable_if, std::is_same, std::is_empty, std::is_aggregate -#if __cpp_lib_unwrap_ref >= 201811L -#include // std::reference_wrapper -#else -#include // std::reference_wrapper +#include +#include // std::unique_ptr/shared_ptr, std::make_unique/std::make_shared +#include // std::system_error +#include // std::string +#include // std::remove_reference, std::is_base_of, std::decay, std::false_type, std::true_type +#include // std::identity +#include // std::stringstream +#include // std::flush +#include // std::map +#include // std::vector +#include // std::tuple_size, std::tuple, std::make_tuple, std::tie +#include // std::forward, std::pair +#include // std::for_each, std::ranges::for_each +// #include "functional/cxx_optional.h" + +// #include "cxx_core_features.h" + +#if SQLITE_ORM_HAS_INCLUDE() +#include #endif -// #include "functional/cxx_core_features.h" +#if __cpp_lib_optional >= 201606L +#define SQLITE_ORM_OPTIONAL_SUPPORTED +#endif -// #include "functional/cxx_type_traits_polyfill.h" +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_functional_polyfill.h" + +#include +#if __cpp_lib_invoke < 201411L +#include // std::enable_if, std::is_member_object_pointer, std::is_member_function_pointer +#endif +#include // std::forward + +// #include "cxx_type_traits_polyfill.h" #include @@ -476,167 +508,255 @@ namespace sqlite_orm { namespace polyfill = internal::polyfill; } -namespace sqlite_orm { - // C++ generic traits used throughout the library - namespace internal { - template - using is_any_of = polyfill::disjunction...>; +// #include "../member_traits/member_traits.h" - template - struct value_unref_type : polyfill::remove_cvref {}; +#include // std::enable_if, std::is_function, std::true_type, std::false_type - template - struct value_unref_type> : std::remove_const {}; +// #include "../functional/cxx_universal.h" + +// #include "../functional/cxx_type_traits_polyfill.h" +_EXPORT_SQLITE_ORM namespace sqlite_orm { + namespace internal { + // SFINAE friendly trait to get a member object pointer's field type template - using value_unref_type_t = typename value_unref_type::type; + struct object_field_type {}; template - using is_eval_order_garanteed = -#if __cpp_lib_is_aggregate >= 201703L - std::is_aggregate; -#else - std::is_pod; -#endif + using object_field_type_t = typename object_field_type::type; - // enable_if for types - template class Op, class... Args> - using match_if = std::enable_if_t::value>; + template + struct object_field_type : std::enable_if::value, F> {}; - // enable_if for types - template class Op, class... Args> - using match_if_not = std::enable_if_t>::value>; + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified return type) + template + struct getter_field_type {}; - // enable_if for types - template class Primary> - using match_specialization_of = std::enable_if_t::value>; + template + using getter_field_type_t = typename getter_field_type::type; - // enable_if for functions - template class Op, class... Args> - using satisfies = std::enable_if_t::value, bool>; + template + struct getter_field_type : getter_field_type {}; - // enable_if for functions - template class Op, class... Args> - using satisfies_not = std::enable_if_t>::value, bool>; + template + struct getter_field_type : polyfill::remove_cvref {}; - // enable_if for functions - template class Primary> - using satisfies_is_specialization_of = - std::enable_if_t::value, bool>; - } + template + struct getter_field_type : polyfill::remove_cvref {}; - // type name template aliases for syntactic sugar - namespace internal { - template - using type_t = typename T::type; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct getter_field_type : polyfill::remove_cvref {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - using auto_type_t = typename decltype(a)::type; + template + struct getter_field_type : polyfill::remove_cvref {}; #endif - template - using value_type_t = typename T::value_type; - - template - using field_type_t = typename T::field_type; - - template - using constraints_type_t = typename T::constraints_type; - - template - using columns_tuple_t = typename T::columns_tuple; - - template - using object_type_t = typename T::object_type; - - template - using elements_type_t = typename T::elements_type; - - template - using table_type_t = typename T::table_type; - - template - using target_type_t = typename T::target_type; + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) + template + struct setter_field_type {}; - template - using left_type_t = typename T::left_type; + template + using setter_field_type_t = typename setter_field_type::type; - template - using right_type_t = typename T::right_type; + template + struct setter_field_type : setter_field_type {}; - template - using on_type_t = typename T::on_type; + template + struct setter_field_type : polyfill::remove_cvref {}; - template - using expression_type_t = typename T::expression_type; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct setter_field_type : polyfill::remove_cvref {}; +#endif - template - using alias_type_t = typename As::alias_type; + template + struct is_getter : std::false_type {}; + template + struct is_getter>> : std::true_type {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES template - using udf_type_t = typename T::udf_type; + SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter::value; - template - using auto_udf_type_t = typename decltype(a)::udf_type; -#endif + template + struct is_setter : std::false_type {}; + template + struct is_setter>> : std::true_type {}; -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template - using cte_moniker_type_t = typename T::cte_moniker_type; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter::value; - template - using cte_mapper_type_t = typename T::cte_mapper_type; + template + struct member_field_type : object_field_type, getter_field_type, setter_field_type {}; - // T::alias_type or nonesuch template - using alias_holder_type_or_none = polyfill::detected; + using member_field_type_t = typename member_field_type::type; template - using alias_holder_type_or_none_t = typename alias_holder_type_or_none::type; -#endif + struct member_object_type {}; -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - template - concept stateless = std::is_empty_v; -#endif - } + template + struct member_object_type : polyfill::type_identity {}; -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - template - concept orm_names_type = requires { typename T::type; }; -#endif + template + using member_object_type_t = typename member_object_type::type; + } } -#pragma once namespace sqlite_orm { - namespace internal { + namespace polyfill { + // C++20 or later (unfortunately there's no feature test macro). + // Stupidly, clang says C++20, but `std::identity` was only implemented in libc++ 13 and libstd++-v3 10 + // (the latter is used on Linux). + // gcc got it right and reports C++20 only starting with v10. + // The check here doesn't care and checks the library versions in use. + // + // Another way of detection would be the constrained algorithms feature-test macro __cpp_lib_ranges +#if(__cplusplus >= 202002L) && \ + ((!_LIBCPP_VERSION || _LIBCPP_VERSION >= 13000) && (!_GLIBCXX_RELEASE || _GLIBCXX_RELEASE >= 10)) + using std::identity; +#else + struct identity { + template + constexpr T&& operator()(T&& v) const noexcept { + return std::forward(v); + } - enum class collate_argument { - binary, - nocase, - rtrim, - }; - } - -} -#pragma once - -#include // std::system_error -#include // std::ostream -#include // std::string -#include // std::tuple -#include // std::is_base_of, std::false_type, std::true_type - -// #include "functional/cxx_universal.h" - -// #include "functional/cxx_type_traits_polyfill.h" + using is_transparent = int; + }; +#endif -// #include "functional/mpl.h" +#if __cpp_lib_invoke >= 201411L + using std::invoke; +#else + // pointer-to-data-member+object + template, + std::enable_if_t::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return std::forward(object).*callable; + } -/* + // pointer-to-member-function+object + template, + std::enable_if_t::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return (std::forward(object).*callable)(std::forward(args)...); + } + + // pointer-to-member+reference-wrapped object (expect `reference_wrapper::*`) + template>, + std::reference_wrapper>>::value, + bool> = true> + decltype(auto) invoke(Callable&& callable, std::reference_wrapper wrapper, Args&&... args) { + return invoke(std::forward(callable), wrapper.get(), std::forward(args)...); + } + + // functor + template + decltype(auto) invoke(Callable&& callable, Args&&... args) { + return std::forward(callable)(std::forward(args)...); + } +#endif + } + } + + namespace polyfill = internal::polyfill; +} + +// #include "functional/static_magic.h" + +#ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED +#include // std::false_type, std::true_type, std::integral_constant +#endif +#include // std::forward + +namespace sqlite_orm { + + // got from here + // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co + namespace internal { + + // note: this is a class template accompanied with a variable template because older compilers (e.g. VC 2017) + // cannot handle a static lambda variable inside a template function + template + struct empty_callable_t { + template + R operator()(Args&&...) const { + return R(); + } + }; + template + constexpr empty_callable_t empty_callable{}; + +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + template + decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { + if constexpr(B) { + return std::forward(trueFn); + } else { + return std::forward(falseFn); + } + } + + template + decltype(auto) static_if([[maybe_unused]] T&& trueFn) { + if constexpr(B) { + return std::forward(trueFn); + } else { + return empty_callable<>; + } + } + + template + void call_if_constexpr([[maybe_unused]] L&& lambda, [[maybe_unused]] Args&&... args) { + if constexpr(B) { + lambda(std::forward(args)...); + } + } +#else + template + decltype(auto) static_if(std::true_type, T&& trueFn, const F&) { + return std::forward(trueFn); + } + + template + decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { + return std::forward(falseFn); + } + + template + decltype(auto) static_if(T&& trueFn, F&& falseFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); + } + + template + decltype(auto) static_if(T&& trueFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable<>); + } + + template + void call_if_constexpr(L&& lambda, Args&&... args) { + static_if(std::forward(lambda))(std::forward(args)...); + } +#endif + } + +} + +// #include "functional/mpl.h" + +/* * Symbols for 'template metaprogramming' (compile-time template programming), * inspired by the MPL of Aleksey Gurtovoy and David Abrahams, and the Mp11 of Peter Dimov and Bjorn Reese. * @@ -1180,53 +1300,6 @@ namespace sqlite_orm { } } -// #include "tuple_helper/same_or_void.h" - -#include // std::common_type - -namespace sqlite_orm { - namespace internal { - - /** - * Accepts any number of arguments and evaluates a nested `type` typename as `T` if all arguments are the same, otherwise `void`. - */ - template - struct same_or_void { - using type = void; - }; - - template - struct same_or_void { - using type = A; - }; - - template - struct same_or_void { - using type = A; - }; - - template - using same_or_void_t = typename same_or_void::type; - - template - struct same_or_void : same_or_void {}; - - template - struct common_type_of; - - template class Pack, class... Types> - struct common_type_of> : std::common_type {}; - - /** - * Accepts a pack of types and defines a nested `type` typename to a common type if possible, otherwise nonexistent. - * - * @note: SFINAE friendly like `std::common_type`. - */ - template - using common_type_of_t = typename common_type_of::type; - } -} - // #include "tuple_helper/tuple_traits.h" // #include "../functional/cxx_type_traits_polyfill.h" @@ -1297,7 +1370,7 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_filter.h" #include // std::integral_constant, std::index_sequence, std::conditional, std::declval -#include // std::tuple +#include // std::tuple, std::tuple_cat, std::tuple_element // #include "../functional/cxx_universal.h" // ::size_t @@ -1458,12081 +1531,11694 @@ namespace sqlite_orm { } } -// #include "type_traits.h" - -// #include "collate_argument.h" - -// #include "error_code.h" +// #include "tuple_helper/tuple_transformer.h" -#include -#include // std::error_code, std::system_error -#include // std::string -#include -#include // std::ostringstream -#include +#include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval +#include // std::tuple_size, std::get -namespace sqlite_orm { +// #include "../functional/cxx_universal.h" +// ::size_t +// #include "../functional/cxx_type_traits_polyfill.h" - /** @short Enables classifying sqlite error codes. +// #include "../functional/cxx_functional_polyfill.h" - @note We don't bother listing all possible values; - this also allows for compatibility with - 'Construction rules for enum class values (P0138R2)' - */ - enum class sqlite_errc {}; +// #include "../functional/mpl.h" - enum class orm_error_code { - not_found = 1, - type_is_not_mapped_to_storage, - trying_to_dereference_null_iterator, - too_many_tables_specified, - incorrect_set_fields_specified, - column_not_found, - table_has_no_primary_key_column, - cannot_start_a_transaction_within_a_transaction, - no_active_transaction, - incorrect_journal_mode_string, - invalid_collate_argument_enum, - failed_to_init_a_backup, - unknown_member_value, - incorrect_order, - cannot_use_default_value, - arguments_count_does_not_match, - function_not_found, - index_is_out_of_bounds, - value_is_null, - no_tables_specified, - }; +namespace sqlite_orm { + namespace internal { -} + template class Op> + struct tuple_transformer; -namespace std { - template<> - struct is_error_code_enum<::sqlite_orm::sqlite_errc> : true_type {}; + template class Pack, class... Types, template class Op> + struct tuple_transformer, Op> { + using type = Pack...>; + }; - template<> - struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; -} + /* + * Transform specified tuple. + * + * `Op` is a metafunction. + */ + template class Op> + using transform_tuple_t = typename tuple_transformer::type; -namespace sqlite_orm { + // note: applying a combiner like `plus_fold_integrals` needs fold expressions +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + /* + * Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results. + * + * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. + * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + * + * @note `project` is called using `std::invoke`, which is `constexpr` since C++20. + */ + template + SQLITE_ORM_CONSTEXPR_CPP20 auto recombine_tuple(CombineOp combine, + const Tpl& tpl, + std::index_sequence, + Projector project, + Init initial) { + return combine(initial, polyfill::invoke(project, std::get(tpl))...); + } - class orm_error_category : public std::error_category { - public: - const char* name() const noexcept override final { - return "ORM error"; + /* + * Apply a projection to a tuple's elements, and combine the results. + * + * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. + * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + * + * @note `project` is called using `std::invoke`, which is `constexpr` since C++20. + */ + template + SQLITE_ORM_CONSTEXPR_CPP20 auto + recombine_tuple(CombineOp combine, const Tpl& tpl, Projector project, Init initial) { + return recombine_tuple(std::move(combine), + std::forward(tpl), + std::make_index_sequence::value>{}, + std::move(project), + std::move(initial)); } - std::string message(int c) const override final { - switch(static_cast(c)) { - case orm_error_code::not_found: - return "Not found"; - case orm_error_code::type_is_not_mapped_to_storage: - return "Type is not mapped to storage"; - case orm_error_code::trying_to_dereference_null_iterator: - return "Trying to dereference null iterator"; - case orm_error_code::too_many_tables_specified: - return "Too many tables specified"; - case orm_error_code::incorrect_set_fields_specified: - return "Incorrect set fields specified"; - case orm_error_code::column_not_found: - return "Column not found"; - case orm_error_code::table_has_no_primary_key_column: - return "Table has no primary key column"; - case orm_error_code::cannot_start_a_transaction_within_a_transaction: - return "Cannot start a transaction within a transaction"; - case orm_error_code::no_active_transaction: - return "No active transaction"; - case orm_error_code::invalid_collate_argument_enum: - return "Invalid collate_argument enum"; - case orm_error_code::failed_to_init_a_backup: - return "Failed to init a backup"; - case orm_error_code::unknown_member_value: - return "Unknown member value"; - case orm_error_code::incorrect_order: - return "Incorrect order"; - case orm_error_code::cannot_use_default_value: - return "The statement 'INSERT INTO * DEFAULT VALUES' can be used with only one row"; - case orm_error_code::arguments_count_does_not_match: - return "Arguments count does not match"; - case orm_error_code::function_not_found: - return "Function not found"; - case orm_error_code::index_is_out_of_bounds: - return "Index is out of bounds"; - case orm_error_code::value_is_null: - return "Value is null"; - case orm_error_code::no_tables_specified: - return "No tables specified"; - default: - return "unknown error"; + /* + * Function object that takes integral constants and returns the sum of their values as an integral constant. + * Because it's a "transparent" functor, it must be called with at least one argument, otherwise it cannot deduce the integral constant type. + */ + struct plus_fold_integrals { + template + constexpr auto operator()(const Integrals&...) const { + using integral_type = std::common_type_t; + return std::integral_constant{}; } - } - }; - - class sqlite_error_category : public std::error_category { - public: - const char* name() const noexcept override final { - return "SQLite error"; - } + }; - std::string message(int c) const override final { - return sqlite3_errstr(c); - } - }; + /* + * Function object that takes a type, applies a projection on it, and returns the tuple size of the projected type (as an integral constant). + * The projection is applied on the argument type, not the argument value/object. + */ + template class NestedProject> + struct project_nested_tuple_size { + template + constexpr auto operator()(const T&) const { + return typename std::tuple_size>::type{}; + } + }; - inline const orm_error_category& get_orm_error_category() { - static orm_error_category res; - return res; - } + template class NestedProject, class Tpl, class IdxSeq> + using nested_tuple_size_for_t = decltype(recombine_tuple(plus_fold_integrals{}, + std::declval(), + IdxSeq{}, + project_nested_tuple_size{}, + std::integral_constant{})); +#endif - inline const sqlite_error_category& get_sqlite_error_category() { - static sqlite_error_category res; - return res; - } + template + constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } - inline std::error_code make_error_code(sqlite_errc ev) noexcept { - return {static_cast(ev), get_sqlite_error_category()}; + /* + * Like `std::make_from_tuple`, but using a projection on the tuple elements. + */ + template + constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } } +} - inline std::error_code make_error_code(orm_error_code ev) noexcept { - return {static_cast(ev), get_orm_error_category()}; - } +// #include "tuple_helper/tuple_iteration.h" - template - std::string get_error_message(sqlite3* db, T&&... args) { - std::ostringstream stream; - using unpack = int[]; - (void)unpack{0, (stream << args, 0)...}; - stream << sqlite3_errmsg(db); - return stream.str(); - } +#include // std::get, std::tuple_element, std::tuple_size +#include // std::index_sequence, std::make_index_sequence +#include // std::forward, std::move - template - [[noreturn]] void throw_error(sqlite3* db, T&&... args) { - throw std::system_error{sqlite_errc(sqlite3_errcode(db)), get_error_message(db, std::forward(args)...)}; - } +// #include "../functional/cxx_universal.h" +// ::size_t - inline std::system_error sqlite_to_system_error(int ev) { - return {sqlite_errc(ev)}; - } +namespace sqlite_orm { + namespace internal { +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + template + void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { + if constexpr(reversed) { + // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= + int sink; + // note: `(void)` cast silences warning 'expression result unused' + (void)((lambda(std::get(tpl)), sink) = ... = 0); + } else { + (lambda(std::get(tpl)), ...); + } + } +#else + template + void iterate_tuple(Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} - inline std::system_error sqlite_to_system_error(sqlite3* db) { - return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(db)}; - } + template + void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { + if SQLITE_ORM_CONSTEXPR_IF(reversed) { + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + lambda(std::get(tpl)); + } else { + lambda(std::get(tpl)); + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + } + } +#endif + template + void iterate_tuple(Tpl&& tpl, L&& lambda) { + iterate_tuple(tpl, + std::make_index_sequence>::value>{}, + std::forward(lambda)); + } - [[noreturn]] inline void throw_translated_sqlite_error(int ev) { - throw sqlite_to_system_error(ev); - } +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void iterate_tuple(std::index_sequence, L&& lambda) { + (lambda((std::tuple_element_t*)nullptr), ...); + } +#else + template + void iterate_tuple(std::index_sequence, L&& lambda) { + using Sink = int[sizeof...(Idx)]; + (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; + } +#endif + template + void iterate_tuple(L&& lambda) { + iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); + } - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { - throw sqlite_to_system_error(db); - } + template class Base, class L> + struct lambda_as_template_base : L { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} +#endif + template + decltype(auto) operator()(const Base& object) { + return L::operator()(object); + } + }; - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { - throw sqlite_to_system_error(sqlite3_db_handle(stmt)); + /* + * This method wraps the specified callable in another function object, + * which in turn implicitly casts its single argument to the specified template base class, + * then passes the converted argument to the lambda. + * + * Note: This method is useful for reducing combinatorial instantiation of template lambdas, + * as long as this library supports compilers that do not implement + * explicit template parameters in generic lambdas [SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED]. + * Unfortunately it doesn't work with user-defined conversion operators in order to extract + * parts of a class. In other words, the destination type must be a direct template base class. + */ + template class Base, class L> + lambda_as_template_base call_as_template_base(L lambda) { + return {std::move(lambda)}; + } } } -// #include "table_type_of.h" +// #include "type_traits.h" -#include // std::enable_if, std::is_convertible +#include // std::enable_if, std::is_same, std::is_empty, std::is_aggregate +#if __cpp_lib_unwrap_ref >= 201811L +#include // std::reference_wrapper +#else +#include // std::reference_wrapper +#endif -namespace sqlite_orm { +// #include "functional/cxx_core_features.h" + +// #include "functional/cxx_type_traits_polyfill.h" +_EXPORT_SQLITE_ORM namespace sqlite_orm { + // C++ generic traits used throughout the library namespace internal { + template + using is_any_of = polyfill::disjunction...>; - template - struct column_pointer; + template + struct value_unref_type : polyfill::remove_cvref {}; - template - struct indexed_column_t; + template + struct value_unref_type> : std::remove_const {}; - /** - * Trait class used to define table mapped type by setter/getter/member - * T - member pointer - * `type` is a type which is mapped. - * E.g. - * - `table_type_of::type` is `User` - * - `table_type_of::type` is `User` - * - `table_type_of::type` is `User` - * - `table_type_of(&User::id))>::type` is `User` - * - `table_type_of*&User::id)>::type` is `User` - */ template - struct table_type_of; + using value_unref_type_t = typename value_unref_type::type; - template - struct table_type_of { - using type = O; - }; + template + using is_eval_order_garanteed = +#if __cpp_lib_is_aggregate >= 201703L + std::is_aggregate; +#else + std::is_pod; +#endif - template - struct table_type_of> { - using type = T; - }; + // enable_if for types + template class Op, class... Args> + using match_if = std::enable_if_t::value>; - template - struct table_type_of> : table_type_of {}; + // enable_if for types + template class Op, class... Args> + using match_if_not = std::enable_if_t>::value>; - template - using table_type_of_t = typename table_type_of::type; + // enable_if for types + template class Primary> + using match_specialization_of = std::enable_if_t::value>; - /* - * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. - * - * One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer. - * E.g. - * regular: `alias_column>(column(&Base::field))` - * short: `alias_column>(&Base::field)` - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; + // enable_if for functions + template class Op, class... Args> + using satisfies = std::enable_if_t::value, bool>; - /* - * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_field_of_v::value>> = true; + // enable_if for functions + template class Op, class... Args> + using satisfies_not = std::enable_if_t>::value, bool>; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; + // enable_if for functions + template class Primary> + using satisfies_is_specialization_of = + std::enable_if_t::value, bool>; } -} - -// #include "type_printer.h" - -#include // std::string -#include // std::shared_ptr, std::unique_ptr -#include // std::vector -// #include "functional/cxx_optional.h" -// #include "cxx_core_features.h" + // type name template aliases for syntactic sugar + namespace internal { + template + using type_t = typename T::type; -#if SQLITE_ORM_HAS_INCLUDE() -#include +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using auto_type_t = typename decltype(a)::type; #endif -#if __cpp_lib_optional >= 201606L -#define SQLITE_ORM_OPTIONAL_SUPPORTED -#endif + template + using value_type_t = typename T::value_type; -// #include "functional/cxx_type_traits_polyfill.h" + template + using field_type_t = typename T::field_type; -// #include "type_traits.h" + template + using constraints_type_t = typename T::constraints_type; -// #include "is_std_ptr.h" + template + using columns_tuple_t = typename T::columns_tuple; -#include -#include + template + using object_type_t = typename T::object_type; -namespace sqlite_orm { + template + using elements_type_t = typename T::elements_type; - /** - * Specialization for optional type (std::shared_ptr / std::unique_ptr). - */ - template - struct is_std_ptr : std::false_type {}; + template + using table_type_t = typename T::table_type; - template - struct is_std_ptr> : std::true_type { - using element_type = typename std::shared_ptr::element_type; + template + using target_type_t = typename T::target_type; - static std::shared_ptr make(std::remove_cv_t&& v) { - return std::make_shared(std::move(v)); - } - }; + template + using left_type_t = typename T::left_type; - template - struct is_std_ptr> : std::true_type { - using element_type = typename std::unique_ptr::element_type; + template + using right_type_t = typename T::right_type; - static auto make(std::remove_cv_t&& v) { - return std::make_unique(std::move(v)); - } - }; -} + template + using on_type_t = typename T::on_type; -namespace sqlite_orm { + template + using expression_type_t = typename T::expression_type; - /** - * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) - */ - template - struct type_printer {}; + template + using alias_type_t = typename As::alias_type; - struct integer_printer { - const std::string& print() const { - static const std::string res = "INTEGER"; - return res; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using udf_type_t = typename T::udf_type; - struct text_printer { - const std::string& print() const { - static const std::string res = "TEXT"; - return res; - } - }; + template + using auto_udf_type_t = typename decltype(a)::udf_type; +#endif - struct real_printer { - const std::string& print() const { - static const std::string res = "REAL"; - return res; - } - }; +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template + using cte_moniker_type_t = typename T::cte_moniker_type; - struct blob_printer { - const std::string& print() const { - static const std::string res = "BLOB"; - return res; - } - }; + template + using cte_mapper_type_t = typename T::cte_mapper_type; - // Note: char, unsigned/signed char are used for storing integer values, not char values. - template - struct type_printer + using alias_holder_type_or_none = polyfill::detected; + + template + using alias_holder_type_or_none_t = typename alias_holder_type_or_none::type; #endif - char16_t, - char32_t>>, - std::is_integral>::value>> : integer_printer { - }; - template - struct type_printer::value>> : real_printer {}; +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept stateless = std::is_empty_v; +#endif + } +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED template - struct type_printer, - std::is_base_of, - std::is_base_of>::value>> - : text_printer {}; + concept orm_names_type = requires { typename T::type; }; +#endif +} - template - struct type_printer::value>> : type_printer {}; +// #include "alias.h" -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct type_printer>> - : type_printer {}; +#include // std::enable_if, std::is_same +#include // std::make_index_sequence, std::move +#include // std::string +#include // std::stringstream +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#include #endif - template<> - struct type_printer, void> : blob_printer {}; -} +// #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +// #include "functional/mpl/conditional.h" - namespace internal { +// #include "functional/cstring_literal.h" - enum class conflict_clause_t { - rollback, - abort, - fail, - ignore, - replace, - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include // std::index_sequence +#include // std::copy_n +#endif - struct primary_key_base { - enum class order_by { - unspecified, - ascending, - descending, - }; - struct { - order_by asc_option = order_by::unspecified; - conflict_clause_t conflict_clause = conflict_clause_t::rollback; - bool conflict_clause_is_on = false; - } options; - }; - - template - struct primary_key_with_autoincrement : T { - using primary_key_type = T; +// #include "cxx_universal.h" +// ::size_t - const primary_key_type& as_base() const { - return *this; - } -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} -#endif - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +namespace sqlite_orm::internal { + /* + * Wraps a C string of fixed size. + * Its main purpose is to enable the user-defined string literal operator template. + */ + template + struct cstring_literal { + static constexpr size_t size() { + return N - 1; + } - /** - * PRIMARY KEY constraint class. - * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when - * used within `make_column` function. - */ - template - struct primary_key_t : primary_key_base { - using self = primary_key_t; - using order_by = primary_key_base::order_by; - using columns_tuple = std::tuple; + constexpr cstring_literal(const char (&cstr)[N]) { + std::copy_n(cstr, N, this->cstr); + } - columns_tuple columns; + char cstr[N]; + }; - primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} + template class Template, cstring_literal literal, size_t... Idx> + consteval auto explode_into(std::index_sequence) { + return Template{}; + } +} +#endif - self asc() const { - auto res = *this; - res.options.asc_option = order_by::ascending; - return res; - } +// #include "type_traits.h" - self desc() const { - auto res = *this; - res.options.asc_option = order_by::descending; - return res; - } +// #include "alias_traits.h" - primary_key_with_autoincrement autoincrement() const { - return {*this}; - } +#include // std::is_base_of, std::is_same +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include +#endif - self on_conflict_rollback() const { - auto res = *this; - res.options.conflict_clause_is_on = true; - res.options.conflict_clause = conflict_clause_t::rollback; - return res; - } +// #include "functional/cxx_universal.h" - self on_conflict_abort() const { - auto res = *this; - res.options.conflict_clause_is_on = true; - res.options.conflict_clause = conflict_clause_t::abort; - return res; - } +// #include "functional/cxx_type_traits_polyfill.h" - self on_conflict_fail() const { - auto res = *this; - res.options.conflict_clause_is_on = true; - res.options.conflict_clause = conflict_clause_t::fail; - return res; - } +// #include "type_traits.h" - self on_conflict_ignore() const { - auto res = *this; - res.options.conflict_clause_is_on = true; - res.options.conflict_clause = conflict_clause_t::ignore; - return res; - } +// #include "table_reference.h" - self on_conflict_replace() const { - auto res = *this; - res.options.conflict_clause_is_on = true; - res.options.conflict_clause = conflict_clause_t::replace; - return res; - } - }; +#include // std::remove_const, std::type_identity +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include +#endif - struct unique_base { - operator std::string() const { - return "UNIQUE"; - } - }; +// #include "functional/cxx_type_traits_polyfill.h" - /** - * UNIQUE constraint class. +_EXPORT_SQLITE_ORM namespace sqlite_orm { + namespace internal { + /* + * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. */ - template - struct unique_t : unique_base { - using columns_tuple = std::tuple; + template + struct table_reference : polyfill::type_identity {}; - columns_tuple columns; + template + struct decay_table_ref : std::remove_const {}; + template + struct decay_table_ref> : polyfill::type_identity {}; + template + struct decay_table_ref> : polyfill::type_identity {}; - unique_t(columns_tuple columns_) : columns(std::move(columns_)) {} - }; + template + using decay_table_ref_t = typename decay_table_ref::type; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using auto_decay_table_ref_t = typename decay_table_ref::type; +#endif - struct unindexed_t {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_table_reference_v = + polyfill::is_specialization_of_v, table_reference>; - template - struct prefix_t { - using value_type = T; + template + struct is_table_reference : polyfill::bool_constant> {}; + } - value_type value; - }; +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. + * + * A concrete table reference has the following traits: + * - specialization of `table_reference`, whose `type` typename references a mapped object. + */ + template + concept orm_table_reference = polyfill::is_specialization_of_v, internal::table_reference>; +#endif +} - template - struct tokenize_t { - using value_type = T; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - value_type value; - }; + /** @short Base class for a custom table alias, column alias or expression alias. + */ + struct alias_tag {}; - template - struct content_t { - using value_type = T; + namespace internal { - value_type value; - }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_alias_v = std::is_base_of::value; - template - struct table_content_t { - using mapped_type = T; - }; + template + struct is_alias : polyfill::bool_constant> {}; - /** - * DEFAULT constraint class. - * T is a value type. + /** @short Alias of a column in a record set, see `orm_column_alias`. */ - template - struct default_t { - using value_type = T; - - value_type value; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_alias_v = + polyfill::conjunction, polyfill::negation>>::value; - operator std::string() const { - return "DEFAULT"; - } - }; + template + struct is_column_alias : is_alias {}; -#if SQLITE_VERSION_NUMBER >= 3006019 - /** - * FOREIGN KEY constraint class. - * Cs are columns which has foreign key - * Rs are column which C references to - * Available in SQLite 3.6.19 or higher + /** @short Alias of any type of record set, see `orm_recordset_alias`. */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_recordset_alias_v = + polyfill::conjunction, polyfill::is_detected>::value; - template - struct foreign_key_t; - - enum class foreign_key_action { - none, // not specified - no_action, - restrict_, - set_null, - set_default, - cascade, - }; + template + struct is_recordset_alias : polyfill::bool_constant> {}; - inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { - switch(action) { - case foreign_key_action::no_action: - os << "NO ACTION"; - break; - case foreign_key_action::restrict_: - os << "RESTRICT"; - break; - case foreign_key_action::set_null: - os << "SET NULL"; - break; - case foreign_key_action::set_default: - os << "SET DEFAULT"; - break; - case foreign_key_action::cascade: - os << "CASCADE"; - break; - case foreign_key_action::none: - break; - } - return os; - } + /** @short Alias of a concrete table, see `orm_table_alias`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction< + is_recordset_alias, + polyfill::negation, std::remove_const_t>>>::value; - struct on_update_delete_base { - const bool update; // true if update and false if delete + template + struct is_table_alias : polyfill::bool_constant> {}; - operator std::string() const { - if(this->update) { - return "ON UPDATE"; - } else { - return "ON DELETE"; - } - } - }; - - /** - * F - foreign key class + /** @short Moniker of a CTE, see `orm_cte_moniker`. */ - template - struct on_update_delete_t : on_update_delete_base { - using foreign_key_type = F; - - const foreign_key_type& fk; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_cte_moniker_v = +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + polyfill::conjunction_v, + std::is_same, std::remove_const_t>>; +#else + false; +#endif - on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : - on_update_delete_base{update_}, fk(fk_), _action(action_) {} + template + using is_cte_moniker = polyfill::bool_constant>; + } - foreign_key_action _action = foreign_key_action::none; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + concept orm_alias = std::derived_from; - foreign_key_type no_action() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::no_action; - } else { - res.on_delete._action = foreign_key_action::no_action; - } - return res; - } + /** @short Specifies that a type is an alias of a column in a record set. + * + * A column alias has the following traits: + * - is derived from `alias_tag` + * - must not have a nested `type` typename + */ + template + concept orm_column_alias = (orm_alias && !orm_names_type); - foreign_key_type restrict_() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::restrict_; - } else { - res.on_delete._action = foreign_key_action::restrict_; - } - return res; - } + /** @short Specifies that a type is an alias of any type of record set. + * + * A record set alias has the following traits: + * - is derived from `alias_tag`. + * - has a nested `type` typename, which refers to a mapped object. + */ + template + concept orm_recordset_alias = (orm_alias && orm_names_type); - foreign_key_type set_null() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::set_null; - } else { - res.on_delete._action = foreign_key_action::set_null; - } - return res; - } + /** @short Specifies that a type is an alias of a concrete table. + * + * A concrete table alias has the following traits: + * - is derived from `alias_tag`. + * - has a `type` typename, which refers to another mapped object (i.e. doesn't refer to itself). + */ + template + concept orm_table_alias = (orm_recordset_alias && !std::same_as>); - foreign_key_type set_default() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::set_default; - } else { - res.on_delete._action = foreign_key_action::set_default; - } - return res; - } + /** @short Moniker of a CTE. + * + * A CTE moniker has the following traits: + * - is derived from `alias_tag`. + * - has a `type` typename, which refers to itself. + */ + template + concept orm_cte_moniker = (orm_recordset_alias && std::same_as>); - foreign_key_type cascade() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::cascade; - } else { - res.on_delete._action = foreign_key_action::cascade; - } - return res; - } + /** @short Specifies that a type refers to a mapped table (possibly aliased). + */ + template + concept orm_refers_to_table = (orm_table_reference || orm_table_alias); - operator bool() const { - return this->_action != foreign_key_action::none; - } - }; + /** @short Specifies that a type refers to a recordset. + */ + template + concept orm_refers_to_recordset = (orm_table_reference || orm_recordset_alias); - template - bool operator==(const on_update_delete_t& lhs, const on_update_delete_t& rhs) { - return lhs._action == rhs._action; - } + /** @short Specifies that a type is a mapped recordset (table reference). + */ + template + concept orm_mapped_recordset = (orm_table_reference || orm_cte_moniker); +#endif +} - template - struct foreign_key_t, std::tuple> { - using columns_type = std::tuple; - using references_type = std::tuple; - using self = foreign_key_t; +// #include "table_type_of.h" - /** - * Holds obect type of all referenced columns. - */ - using target_type = same_or_void_t...>; +#include // std::enable_if, std::is_convertible - /** - * Holds obect type of all source columns. - */ - using source_type = same_or_void_t...>; +namespace sqlite_orm { - columns_type columns; - references_type references; + namespace internal { - on_update_delete_t on_update; - on_update_delete_t on_delete; + template + struct column_pointer; - static_assert(std::tuple_size::value == std::tuple_size::value, - "Columns size must be equal to references tuple"); - static_assert(!std::is_same::value, "All references must have the same type"); + template + struct indexed_column_t; - foreign_key_t(columns_type columns_, references_type references_) : - columns(std::move(columns_)), references(std::move(references_)), - on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} + /** + * Trait class used to define table mapped type by setter/getter/member + * T - member pointer + * `type` is a type which is mapped. + * E.g. + * - `table_type_of::type` is `User` + * - `table_type_of::type` is `User` + * - `table_type_of::type` is `User` + * - `table_type_of(&User::id))>::type` is `User` + * - `table_type_of*&User::id)>::type` is `User` + */ + template + struct table_type_of; - foreign_key_t(const self& other) : - columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), - on_delete(*this, false, other.on_delete._action) {} + template + struct table_type_of { + using type = O; + }; - self& operator=(const self& other) { - this->columns = other.columns; - this->references = other.references; - this->on_update = {*this, true, other.on_update._action}; - this->on_delete = {*this, false, other.on_delete._action}; - return *this; - } + template + struct table_type_of> { + using type = T; }; - template - bool operator==(const foreign_key_t& lhs, const foreign_key_t& rhs) { - return lhs.columns == rhs.columns && lhs.references == rhs.references && lhs.on_update == rhs.on_update && - lhs.on_delete == rhs.on_delete; - } + template + struct table_type_of> : table_type_of {}; - /** - * Cs can be a class member pointer, a getter function member pointer or setter - * func member pointer - * Available in SQLite 3.6.19 or higher + template + using table_type_of_t = typename table_type_of::type; + + /* + * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. + * + * One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer. + * E.g. + * regular: `alias_column>(column(&Base::field))` + * short: `alias_column>(&Base::field)` */ - template - struct foreign_key_intermediate_t { - using tuple_type = std::tuple; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; - tuple_type columns; + /* + * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_field_of_v::value>> = true; - template - foreign_key_t, std::tuple> references(Rs... refs) { - return {std::move(this->columns), {std::forward(refs)...}}; - } - }; -#endif + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; + } +} - struct collate_constraint_t { - collate_argument argument = collate_argument::binary; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - collate_constraint_t(collate_argument argument) : argument{argument} {} -#endif +// #include "tags.h" - operator std::string() const { - return "COLLATE " + this->string_from_collate_argument(this->argument); - } +// #include "functional/cxx_functional_polyfill.h" - static std::string string_from_collate_argument(collate_argument argument) { - switch(argument) { - case collate_argument::binary: - return "BINARY"; - case collate_argument::nocase: - return "NOCASE"; - case collate_argument::rtrim: - return "RTRIM"; - } - throw std::system_error{orm_error_code::invalid_collate_argument_enum}; - } - }; +namespace sqlite_orm { + namespace internal { + struct negatable_t {}; - template - struct check_t { - using expression_type = T; + /** + * Inherit from this class to support arithmetic types overloading + */ + struct arithmetic_t {}; - expression_type expression; - }; + /** + * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + */ + struct condition_t {}; - struct basic_generated_always { - enum class storage_type { - not_specified, - virtual_, - stored, - }; + /** + * Specialize if a type participates as an argument to overloaded operators (arithmetic, conditional, negation, chaining) + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v = false; -#if SQLITE_VERSION_NUMBER >= 3031000 - bool full = true; - storage_type storage = storage_type::not_specified; -#endif + template + using is_operator_argument = polyfill::bool_constant>; + } +} -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} -#endif - }; +// #include "column_pointer.h" -#if SQLITE_VERSION_NUMBER >= 3031000 - template - struct generated_always_t : basic_generated_always { - using expression_type = T; +#include // std::enable_if, std::is_convertible +#include // std::move - expression_type expression; +// #include "functional/cxx_core_features.h" - generated_always_t(expression_type expression_, bool full, storage_type storage) : - basic_generated_always{full, storage}, expression(std::move(expression_)) {} +// #include "functional/cxx_type_traits_polyfill.h" - generated_always_t virtual_() { - return {std::move(this->expression), this->full, storage_type::virtual_}; - } +// #include "type_traits.h" - generated_always_t stored() { - return {std::move(this->expression), this->full, storage_type::stored}; - } - }; -#endif +// #include "table_reference.h" - struct null_t {}; +// #include "alias_traits.h" - struct not_null_t {}; - } +// #include "tags.h" +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { + /** + * This class is used to store explicit mapped type T and its column descriptor (member pointer/getter/setter). + * Is useful when mapped type is derived from other type and base class has members mapped to a storage. + */ + template + struct column_pointer { + using type = T; + using field_type = F; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = -#if SQLITE_VERSION_NUMBER >= 3006019 - polyfill::is_specialization_of::value; -#else - false; -#endif + field_type field; + }; template - struct is_foreign_key : polyfill::bool_constant> {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_column_pointer_v = + polyfill::is_specialization_of::value; template - SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = std::is_base_of::value; + struct is_column_pointer : polyfill::bool_constant> {}; template - struct is_primary_key : polyfill::bool_constant> {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v::value>> = + true; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = -#if SQLITE_VERSION_NUMBER >= 3031000 - polyfill::is_specialization_of::value; -#else - false; +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template + struct alias_holder; #endif - - template - struct is_generated_always : polyfill::bool_constant> {}; - - /** - * PRIMARY KEY INSERTABLE traits. - */ - template - struct is_primary_key_insertable - : polyfill::disjunction< - mpl::invoke_t, - check_if_has_template>, - constraints_type_t>, - std::is_base_of>>> { - - static_assert(tuple_has, is_primary_key>::value, - "an unexpected type was passed"); - }; - - template - using is_column_constraint = mpl::invoke_t>, - check_if_is_type, - check_if_is_type, - check_if_is_type>, - check_if_is_template, - check_if_is_template, - check_if_is_type, - check_if, - check_if_is_type>, - T>; - } - -#if SQLITE_VERSION_NUMBER >= 3031000 - template - internal::generated_always_t generated_always_as(T expression) { - return {std::move(expression), true, internal::basic_generated_always::storage_type::not_specified}; - } - - template - internal::generated_always_t as(T expression) { - return {std::move(expression), false, internal::basic_generated_always::storage_type::not_specified}; } -#endif -#if SQLITE_VERSION_NUMBER >= 3006019 /** - * FOREIGN KEY constraint construction function that takes member pointer as argument - * Available in SQLite 3.6.19 or higher + * Explicitly refer to a column, used in contexts + * where the automatic object mapping deduction needs to be overridden. + * + * Example: + * struct BaseType : { int64 id; }; + * struct MyType : BaseType { ... }; + * storage.select(column(&BaseType::id)); */ - template - internal::foreign_key_intermediate_t foreign_key(Cs... columns) { - return {{std::forward(columns)...}}; + template = true> + constexpr internal::column_pointer column(F Base::*field) { + static_assert(std::is_convertible::value, "Field must be from derived class"); + return {field}; } -#endif +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * UNIQUE table constraint builder function. + * Explicitly refer to a column. */ - template - internal::unique_t unique(Args... args) { - return {{std::forward(args)...}}; + template + constexpr auto column(F O::*field) { + return column>(field); } - /** - * UNIQUE column constraint builder function. - */ - inline internal::unique_t<> unique() { - return {{}}; + // Intentionally place pointer-to-member operator for table references in the internal namespace + // to facilitate ADL (Argument Dependent Lookup) + namespace internal { + /** + * Explicitly refer to a column. + */ + template + constexpr auto operator->*(const R& /*table*/, F O::*field) { + return column(field); + } } -#if SQLITE_VERSION_NUMBER >= 3009000 /** - * UNINDEXED column constraint builder function. Used in FTS virtual tables. - * - * https://www.sqlite.org/fts5.html#the_unindexed_column_option + * Make a table reference. */ - inline internal::unindexed_t unindexed() { + template + requires(!orm_recordset_alias) + consteval internal::table_reference column() { return {}; } /** - * prefix=N table constraint builder function. Used in FTS virtual tables. - * - * https://www.sqlite.org/fts5.html#prefix_indexes + * Make a table reference. */ - template - internal::prefix_t prefix(T value) { - return {std::move(value)}; + template + requires(!orm_recordset_alias) + consteval internal::table_reference c() { + return {}; } +#endif +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) /** - * tokenize='...'' table constraint builder function. Used in FTS virtual tables. - * - * https://www.sqlite.org/fts5.html#tokenizers + * Explicitly refer to a column alias mapped into a CTE or subquery. + * + * Example: + * struct Object { ... }; + * using cte_1 = decltype(1_ctealias); + * storage.with(cte()(select(&Object::id)), select(column(&Object::id))); + * storage.with(cte()(select(&Object::id)), select(column(1_colalias))); + * storage.with(cte()(select(as(&Object::id))), select(column(colalias_a{}))); + * storage.with(cte(colalias_a{})(select(&Object::id)), select(column(colalias_a{}))); + * storage.with(cte()(select(as(&Object::id))), select(column(get()))); */ - template - internal::tokenize_t tokenize(T value) { - return {std::move(value)}; + template = true> + constexpr auto column(F field) { + using namespace ::sqlite_orm::internal; + + static_assert(is_cte_moniker_v, "`Moniker' must be a CTE moniker"); + + if constexpr(polyfill::is_specialization_of_v) { + static_assert(is_column_alias_v>); + return column_pointer{{}}; + } else if constexpr(is_column_alias_v) { + return column_pointer>{{}}; + } else { + return column_pointer{std::move(field)}; + } + (void)field; } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * content='' table constraint builder function. Used in FTS virtual tables. - * - * https://www.sqlite.org/fts5.html#contentless_tables + * Explicitly refer to a column mapped into a CTE or subquery. + * + * Example: + * struct Object { ... }; + * storage.with(cte<"z"_cte>()(select(&Object::id)), select(column<"z"_cte>(&Object::id))); + * storage.with(cte<"z"_cte>()(select(&Object::id)), select(column<"z"_cte>(1_colalias))); */ - template - internal::content_t content(T value) { - return {std::move(value)}; + template + constexpr auto column(F field) { + using Moniker = std::remove_const_t; + return column(std::forward(field)); } /** - * content='table' table constraint builder function. Used in FTS virtual tables. - * - * https://www.sqlite.org/fts5.html#external_content_tables + * Explicitly refer to a column mapped into a CTE or subquery. + * + * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) + * because recordset aliases are derived from `sqlite_orm::alias_tag` + * + * Example: + * struct Object { ... }; + * using cte_1 = decltype(1_ctealias); + * storage.with(cte()(select(&Object::id)), select(1_ctealias->*&Object::id)); + * storage.with(cte()(select(&Object::id)), select(1_ctealias->*1_colalias)); */ - template - internal::table_content_t content() { - return {}; + template + constexpr auto operator->*(const Moniker& /*moniker*/, F field) { + return column(std::forward(field)); } #endif - - /** - * PRIMARY KEY table constraint builder function. - */ - template - internal::primary_key_t primary_key(Cs... cs) { - return {{std::forward(cs)...}}; - } - - /** - * PRIMARY KEY column constraint builder function. - */ - inline internal::primary_key_t<> primary_key() { - return {{}}; - } - - template - internal::default_t default_value(T t) { - return {std::move(t)}; - } - - inline internal::collate_constraint_t collate_nocase() { - return {internal::collate_argument::nocase}; - } - - inline internal::collate_constraint_t collate_binary() { - return {internal::collate_argument::binary}; - } - - inline internal::collate_constraint_t collate_rtrim() { - return {internal::collate_argument::rtrim}; - } - - template - internal::check_t check(T t) { - return {std::move(t)}; - } - - inline internal::null_t null() { - return {}; - } - - inline internal::not_null_t not_null() { - return {}; - } +#endif } -#pragma once - -#include // std::false_type, std::true_type, std::enable_if -#include // std::shared_ptr, std::unique_ptr -// #include "functional/cxx_optional.h" - -// #include "functional/cxx_type_traits_polyfill.h" - -namespace sqlite_orm { - /** - * This is class that tells `sqlite_orm` that type is nullable. Nullable types - * are mapped to sqlite database as `NULL` and not-nullable are mapped as `NOT NULL`. - * Default nullability status for all types is `NOT NULL`. So if you want to map - * custom type as `NULL` (for example: boost::optional) you have to create a specialization - * of `type_is_nullable` for your type and derive from `std::true_type`. - */ - template - struct type_is_nullable : std::false_type { - bool operator()(const T&) const { - return true; - } - }; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - /** - * This is a specialization for std::shared_ptr, std::unique_ptr, std::optional, which are nullable in sqlite_orm. - */ - template - struct type_is_nullable, + namespace internal { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + inline constexpr bool is_operator_argument_v>> = true; #endif - polyfill::is_specialization_of, - polyfill::is_specialization_of>::value>> : std::true_type { - bool operator()(const T& t) const { - return static_cast(t); - } - }; -} -#pragma once -#include // std::false_type, std::true_type -#include // std::move + /** + * This is a common built-in class used for character based table aliases. + * For convenience there exist public type aliases `alias_a`, `alias_b`, ... + * The easiest way to create a table alias is using `"z"_alias.for_()`. + */ + template + struct recordset_alias : alias_tag { + using type = T; -// #include "functional/cxx_type_traits_polyfill.h" + static std::string get() { + return {A, X...}; + } + }; -// #include "is_base_of_template.h" + /** + * Column expression with table alias attached like 'C.ID'. This is not a column alias + */ + template + struct alias_column_t { + using alias_type = T; + using column_type = C; -#include // std::true_type, std::false_type, std::declval + column_type column; + }; -namespace sqlite_orm { + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = + true; - namespace internal { + struct basic_table; /* - * This is because of bug in MSVC, for more information, please visit - * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 + * Encapsulates extracting the alias identifier of a non-alias. + * + * `extract()` always returns the empty string. + * `as_alias()` is used in contexts where a table might be aliased, and the empty string is returned. + * `as_qualifier()` is used in contexts where a table might be aliased, and the given table's name is returned. */ -#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION - template class Base> - struct is_base_of_template_impl { - template - static constexpr std::true_type test(const Base&); + template + struct alias_extractor { + static std::string extract() { + return {}; + } - static constexpr std::false_type test(...); + static std::string as_alias() { + return {}; + } + + template + static const std::string& as_qualifier(const X& table) { + return table.name; + } }; - template class C> - using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); -#else - template class C, typename... Ts> - std::true_type is_base_of_template_impl(const C&); + /* + * Encapsulates extracting the alias identifier of an alias. + * + * `extract()` always returns the alias identifier or CTE moniker. + * `as_alias()` is used in contexts where a recordset is aliased, and the alias identifier is returned. + * `as_qualifier()` is used in contexts where a table is aliased, and the alias identifier is returned. + */ + template + struct alias_extractor> { + static std::string extract() { + std::stringstream ss; + ss << A::get(); + return ss.str(); + } - template class C> - std::false_type is_base_of_template_impl(...); + // for column and regular table aliases -> alias identifier + template, A> = true> + static std::string as_alias() { + return alias_extractor::extract(); + } - template class C> - using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + // for CTE monikers -> empty + template, A> = true> + static std::string as_alias() { + return {}; + } #endif - template class C> - SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; - } -} - -// #include "tags.h" - -// #include "functional/cxx_functional_polyfill.h" - -#include -#if __cpp_lib_invoke < 201411L -#include // std::enable_if, std::is_member_object_pointer, std::is_member_function_pointer -#endif -#include // std::forward + // for regular table aliases -> alias identifier + template = true> + static std::string as_qualifier(const basic_table&) { + return alias_extractor::extract(); + } + }; -// #include "cxx_type_traits_polyfill.h" + /** + * Used to store alias for expression + */ + template + struct as_t { + using alias_type = T; + using expression_type = E; -// #include "../member_traits/member_traits.h" + expression_type expression; + }; -#include // std::enable_if, std::is_function, std::true_type, std::false_type + /** + * Built-in column alias. + * For convenience there exist type aliases `colalias_a`, `colalias_b`, ... + * The easiest way to create a column alias is using `"xyz"_col`. + */ + template + struct column_alias : alias_tag { + static std::string get() { + return {A, X...}; + } + }; -// #include "../functional/cxx_universal.h" + template + struct alias_holder { + using type = T; -// #include "../functional/cxx_type_traits_polyfill.h" + alias_holder() = default; + // CTE feature needs it to implicitly convert a column alias to an alias_holder; see `cte()` factory function + alias_holder(const T&) noexcept {} + }; -namespace sqlite_orm { - namespace internal { - // SFINAE friendly trait to get a member object pointer's field type template - struct object_field_type {}; - - template - using object_field_type_t = typename object_field_type::type; - - template - struct object_field_type : std::enable_if::value, F> {}; - - // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified return type) - template - struct getter_field_type {}; - - template - using getter_field_type_t = typename getter_field_type::type; - - template - struct getter_field_type : getter_field_type {}; - - template - struct getter_field_type : polyfill::remove_cvref {}; - - template - struct getter_field_type : polyfill::remove_cvref {}; + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = true; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct getter_field_type : polyfill::remove_cvref {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + struct recordset_alias_builder { + template + [[nodiscard]] consteval recordset_alias for_() const { + return {}; + } - template - struct getter_field_type : polyfill::remove_cvref {}; + template + [[nodiscard]] consteval auto for_() const { + using T = std::remove_const_t; + return recordset_alias{}; + } + }; #endif - // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) - template - struct setter_field_type {}; +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template + SQLITE_ORM_CONSTEVAL auto n_to_colalias() { + constexpr column_alias<'1' + n % 10, C...> colalias{}; + if constexpr(n > 10) { + return n_to_colalias(); + } else { + return colalias; + } + } template - using setter_field_type_t = typename setter_field_type::type; - - template - struct setter_field_type : setter_field_type {}; - - template - struct setter_field_type : polyfill::remove_cvref {}; - -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct setter_field_type : polyfill::remove_cvref {}; + inline constexpr bool is_builtin_numeric_column_alias_v = false; + template + inline constexpr bool is_builtin_numeric_column_alias_v> = ((C >= '0' && C <= '9') && ...); #endif + } - template - struct is_getter : std::false_type {}; - template - struct is_getter>> : std::true_type {}; + /** + * Using a column pointer, create a column reference to an aliased table column. + * + * Example: + * using als = alias_u; + * select(alias_column(column(&User::id))) + */ + template, + polyfill::negation>>>::value, + bool> = true> + constexpr auto alias_column(C field) { + using namespace ::sqlite_orm::internal; + using aliased_type = type_t; + static_assert(is_field_of_v, "Column must be from aliased table"); - template - SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter::value; + return alias_column_t{std::move(field)}; + } - template - struct is_setter : std::false_type {}; - template - struct is_setter>> : std::true_type {}; + /** + * Using an object member field, create a column reference to an aliased table column. + * + * @note The object member pointer can be from a derived class without explicitly forming a column pointer. + * + * Example: + * using als = alias_u; + * select(alias_column(&User::id)) + */ + template, + polyfill::negation>>>::value, + bool> = true> + constexpr auto alias_column(F O::*field) { + using namespace ::sqlite_orm::internal; + using aliased_type = type_t; + static_assert(is_field_of_v, "Column must be from aliased table"); - template - SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter::value; + using C1 = + mpl::conditional_t::value, F O::*, column_pointer>; + return alias_column_t{C1{field}}; + } - template - struct member_field_type : object_field_type, getter_field_type, setter_field_type {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a column reference to an aliased table column. + * + * @note An object member pointer can be from a derived class without explicitly forming a column pointer. + * + * Example: + * constexpr orm_table_alias auto als = "u"_alias.for_(); + * select(alias_column(&User::id)) + */ + template + requires(!orm_cte_moniker>) + constexpr auto alias_column(C field) { + using namespace ::sqlite_orm::internal; + using A = decltype(als); + using aliased_type = type_t; + static_assert(is_field_of_v, "Column must be from aliased table"); - template - using member_field_type_t = typename member_field_type::type; + if constexpr(is_column_pointer_v) { + return alias_column_t{std::move(field)}; + } else if constexpr(std::is_same_v, aliased_type>) { + return alias_column_t{field}; + } else { + // wrap in column_pointer + using C1 = column_pointer; + return alias_column_t{{field}}; + } + } - template - struct member_object_type {}; + /** + * Create a column reference to an aliased table column. + * + * @note An object member pointer can be from a derived class without explicitly forming a column pointer. + * + * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) + * because recordset aliases are derived from `sqlite_orm::alias_tag` + * + * Example: + * constexpr auto als = "u"_alias.for_(); + * select(als->*&User::id) + */ + template + requires(!orm_cte_moniker>) + constexpr auto operator->*(const A& /*tableAlias*/, F field) { + return alias_column(std::move(field)); + } +#endif - template - struct member_object_type : polyfill::type_identity {}; +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + /** + * Create a column reference to an aliased CTE column. + */ + template, internal::is_cte_moniker>>, + bool> = true> + constexpr auto alias_column(C c) { + using namespace internal; + using cte_moniker_t = type_t; - template - using member_object_type_t = typename member_object_type::type; + if constexpr(is_column_pointer_v) { + static_assert(std::is_same, cte_moniker_t>::value, + "Column pointer must match aliased CTE"); + return alias_column_t{c}; + } else { + auto cp = column(c); + return alias_column_t{std::move(cp)}; + } } -} -namespace sqlite_orm { - namespace internal { - namespace polyfill { - // C++20 or later (unfortunately there's no feature test macro). - // Stupidly, clang says C++20, but `std::identity` was only implemented in libc++ 13 and libstd++-v3 10 - // (the latter is used on Linux). - // gcc got it right and reports C++20 only starting with v10. - // The check here doesn't care and checks the library versions in use. - // - // Another way of detection would be the constrained algorithms feature-test macro __cpp_lib_ranges -#if(__cplusplus >= 202002L) && \ - ((!_LIBCPP_VERSION || _LIBCPP_VERSION >= 13000) && (!_GLIBCXX_RELEASE || _GLIBCXX_RELEASE >= 10)) - using std::identity; -#else - struct identity { - template - constexpr T&& operator()(T&& v) const noexcept { - return std::forward(v); - } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a column reference to an aliased CTE column. + * + * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) + * because recordset aliases are derived from `sqlite_orm::alias_tag` + */ + template + requires(orm_cte_moniker>) + constexpr auto operator->*(const A& /*tableAlias*/, C c) { + return alias_column(std::move(c)); + } - using is_transparent = int; - }; + /** + * Create a column reference to an aliased CTE column. + */ + template + requires(orm_cte_moniker>) + constexpr auto alias_column(C c) { + using A = std::remove_const_t; + return alias_column(std::move(c)); + } +#endif #endif -#if __cpp_lib_invoke >= 201411L - using std::invoke; -#else - // pointer-to-data-member+object - template, - std::enable_if_t::value, bool> = true> - decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { - return std::forward(object).*callable; - } - - // pointer-to-member-function+object - template, - std::enable_if_t::value, bool> = true> - decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { - return (std::forward(object).*callable)(std::forward(args)...); - } + /** + * Alias a column expression. + */ + template = true> + internal::as_t as(E expression) { + return {std::move(expression)}; + } - // pointer-to-member+reference-wrapped object (expect `reference_wrapper::*`) - template>, - std::reference_wrapper>>::value, - bool> = true> - decltype(auto) invoke(Callable&& callable, std::reference_wrapper wrapper, Args&&... args) { - return invoke(std::forward(callable), wrapper.get(), std::forward(args)...); - } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Alias a column expression. + */ + template + auto as(E expression) { + return internal::as_t{std::move(expression)}; + } - // functor - template - decltype(auto) invoke(Callable&& callable, Args&&... args) { - return std::forward(callable)(std::forward(args)...); - } + /** + * Alias a column expression. + */ + template + internal::as_t operator>>=(E expression, const A&) { + return {std::move(expression)}; + } +#else + /** + * Alias a column expression. + */ + template = true> + internal::as_t operator>>=(E expression, const A&) { + return {std::move(expression)}; + } #endif - } + + /** + * Wrap a column alias in an alias holder. + */ + template + internal::alias_holder get() { + static_assert(internal::is_column_alias_v, ""); + return {}; } - namespace polyfill = internal::polyfill; -} +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto get() { + return internal::alias_holder{}; + } +#endif -namespace sqlite_orm { - namespace internal { - struct negatable_t {}; + template + using alias_a = internal::recordset_alias; + template + using alias_b = internal::recordset_alias; + template + using alias_c = internal::recordset_alias; + template + using alias_d = internal::recordset_alias; + template + using alias_e = internal::recordset_alias; + template + using alias_f = internal::recordset_alias; + template + using alias_g = internal::recordset_alias; + template + using alias_h = internal::recordset_alias; + template + using alias_i = internal::recordset_alias; + template + using alias_j = internal::recordset_alias; + template + using alias_k = internal::recordset_alias; + template + using alias_l = internal::recordset_alias; + template + using alias_m = internal::recordset_alias; + template + using alias_n = internal::recordset_alias; + template + using alias_o = internal::recordset_alias; + template + using alias_p = internal::recordset_alias; + template + using alias_q = internal::recordset_alias; + template + using alias_r = internal::recordset_alias; + template + using alias_s = internal::recordset_alias; + template + using alias_t = internal::recordset_alias; + template + using alias_u = internal::recordset_alias; + template + using alias_v = internal::recordset_alias; + template + using alias_w = internal::recordset_alias; + template + using alias_x = internal::recordset_alias; + template + using alias_y = internal::recordset_alias; + template + using alias_z = internal::recordset_alias; - /** - * Inherit from this class to support arithmetic types overloading + using colalias_a = internal::column_alias<'a'>; + using colalias_b = internal::column_alias<'b'>; + using colalias_c = internal::column_alias<'c'>; + using colalias_d = internal::column_alias<'d'>; + using colalias_e = internal::column_alias<'e'>; + using colalias_f = internal::column_alias<'f'>; + using colalias_g = internal::column_alias<'g'>; + using colalias_h = internal::column_alias<'h'>; + using colalias_i = internal::column_alias<'i'>; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** @short Create a table alias. + * + * Examples: + * constexpr orm_table_alias auto z_alias = alias<'z'>.for_(); + */ + template + inline constexpr internal::recordset_alias_builder alias{}; + + inline namespace literals { + /** @short Create a table alias. + * + * Examples: + * constexpr orm_table_alias auto z_alias = "z"_alias.for_(); */ - struct arithmetic_t {}; + template + [[nodiscard]] consteval auto operator"" _alias() { + return internal::explode_into( + std::make_index_sequence{}); + } - /** - * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + /** @short Create a column alias. + * column_alias<'a'[, ...]> from a string literal. + * E.g. "a"_col, "b"_col */ - struct condition_t {}; + template + [[nodiscard]] consteval auto operator"" _col() { + return internal::explode_into(std::make_index_sequence{}); + } + } +#endif +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + inline namespace literals { /** - * Specialize if a type participates as an argument to overloaded operators (arithmetic, conditional, negation, chaining) + * column_alias<'1'[, ...]> from a numeric literal. + * E.g. 1_colalias, 2_colalias */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v = false; - - template - using is_operator_argument = polyfill::bool_constant>; + template + [[nodiscard]] SQLITE_ORM_CONSTEVAL auto operator"" _colalias() { + // numeric identifiers are used for automatically assigning implicit aliases to unaliased column expressions, + // which start at "1". + static_assert(std::array{Chars...}[0] > '0'); + return internal::column_alias{}; + } } +#endif } -// #include "serialize_result_type.h" - -// #include "functional/cxx_string_view.h" +// #include "error_code.h" -// #include "cxx_core_features.h" +#include +#include // std::error_code, std::system_error +#include // std::string +#include +#include // std::ostringstream +#include -#if SQLITE_ORM_HAS_INCLUDE() -#include -#endif +_EXPORT_SQLITE_ORM namespace sqlite_orm { -#if __cpp_lib_string_view >= 201606L -#define SQLITE_ORM_STRING_VIEW_SUPPORTED -#endif + /** @short Enables classifying sqlite error codes. -#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED -#include // std::string -#endif + @note We don't bother listing all possible values; + this also allows for compatibility with + 'Construction rules for enum class values (P0138R2)' + */ + enum class sqlite_errc {}; -namespace sqlite_orm { - namespace internal { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - using serialize_result_type = std::string_view; - using serialize_arg_type = std::string_view; -#else - using serialize_result_type = std::string; - using serialize_arg_type = const std::string&; -#endif - } + enum class orm_error_code { + not_found = 1, + type_is_not_mapped_to_storage, + trying_to_dereference_null_iterator, + too_many_tables_specified, + incorrect_set_fields_specified, + column_not_found, + table_has_no_primary_key_column, + cannot_start_a_transaction_within_a_transaction, + no_active_transaction, + incorrect_journal_mode_string, + invalid_collate_argument_enum, + failed_to_init_a_backup, + unknown_member_value, + incorrect_order, + cannot_use_default_value, + arguments_count_does_not_match, + function_not_found, + index_is_out_of_bounds, + value_is_null, + no_tables_specified, + }; } -namespace sqlite_orm { +namespace std { + template<> + struct is_error_code_enum<::sqlite_orm::sqlite_errc> : true_type {}; - namespace internal { + template<> + struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; +} - template - struct binary_operator : Ds... { - using left_type = L; - using right_type = R; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - left_type lhs; - right_type rhs; + class orm_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "ORM error"; + } - binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} - }; + std::string message(int c) const override final { + switch(static_cast(c)) { + case orm_error_code::not_found: + return "Not found"; + case orm_error_code::type_is_not_mapped_to_storage: + return "Type is not mapped to storage"; + case orm_error_code::trying_to_dereference_null_iterator: + return "Trying to dereference null iterator"; + case orm_error_code::too_many_tables_specified: + return "Too many tables specified"; + case orm_error_code::incorrect_set_fields_specified: + return "Incorrect set fields specified"; + case orm_error_code::column_not_found: + return "Column not found"; + case orm_error_code::table_has_no_primary_key_column: + return "Table has no primary key column"; + case orm_error_code::cannot_start_a_transaction_within_a_transaction: + return "Cannot start a transaction within a transaction"; + case orm_error_code::no_active_transaction: + return "No active transaction"; + case orm_error_code::invalid_collate_argument_enum: + return "Invalid collate_argument enum"; + case orm_error_code::failed_to_init_a_backup: + return "Failed to init a backup"; + case orm_error_code::unknown_member_value: + return "Unknown member value"; + case orm_error_code::incorrect_order: + return "Incorrect order"; + case orm_error_code::cannot_use_default_value: + return "The statement 'INSERT INTO * DEFAULT VALUES' can be used with only one row"; + case orm_error_code::arguments_count_does_not_match: + return "Arguments count does not match"; + case orm_error_code::function_not_found: + return "Function not found"; + case orm_error_code::index_is_out_of_bounds: + return "Index is out of bounds"; + case orm_error_code::value_is_null: + return "Value is null"; + case orm_error_code::no_tables_specified: + return "No tables specified"; + default: + return "unknown error"; + } + } + }; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_of_template::value; + class sqlite_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "SQLite error"; + } - template - using is_binary_operator = polyfill::bool_constant>; + std::string message(int c) const override final { + return sqlite3_errstr(c); + } + }; - struct conc_string { - serialize_result_type serialize() const { - return "||"; - } - }; + inline const orm_error_category& get_orm_error_category() { + static orm_error_category res; + return res; + } - /** - * Result of concatenation || operator - */ - template - using conc_t = binary_operator; + inline const sqlite_error_category& get_sqlite_error_category() { + static sqlite_error_category res; + return res; + } - struct add_string { - serialize_result_type serialize() const { - return "+"; - } - }; + inline std::error_code make_error_code(sqlite_errc ev) noexcept { + return {static_cast(ev), get_sqlite_error_category()}; + } - /** - * Result of addition + operator - */ - template - using add_t = binary_operator; + inline std::error_code make_error_code(orm_error_code ev) noexcept { + return {static_cast(ev), get_orm_error_category()}; + } - struct sub_string { - serialize_result_type serialize() const { - return "-"; - } - }; + template + std::string get_error_message(sqlite3 * db, T && ... args) { + std::ostringstream stream; + using unpack = int[]; + (void)unpack{0, (stream << args, 0)...}; + stream << sqlite3_errmsg(db); + return stream.str(); + } - /** - * Result of substitute - operator - */ - template - using sub_t = binary_operator; + template + [[noreturn]] void throw_error(sqlite3 * db, T && ... args) { + throw std::system_error{sqlite_errc(sqlite3_errcode(db)), get_error_message(db, std::forward(args)...)}; + } - struct mul_string { - serialize_result_type serialize() const { - return "*"; - } - }; + inline std::system_error sqlite_to_system_error(int ev) { + return {sqlite_errc(ev)}; + } - /** - * Result of multiply * operator - */ - template - using mul_t = binary_operator; + inline std::system_error sqlite_to_system_error(sqlite3 * db) { + return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(db)}; + } - struct div_string { - serialize_result_type serialize() const { - return "/"; - } - }; + [[noreturn]] inline void throw_translated_sqlite_error(int ev) { + throw sqlite_to_system_error(ev); + } - /** - * Result of divide / operator - */ - template - using div_t = binary_operator; + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3 * db) { + throw sqlite_to_system_error(db); + } - struct mod_operator_string { - serialize_result_type serialize() const { - return "%"; - } - }; + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt * stmt) { + throw sqlite_to_system_error(sqlite3_db_handle(stmt)); + } +} - /** - * Result of mod % operator - */ - template - using mod_t = binary_operator; +// #include "type_printer.h" - struct bitwise_shift_left_string { - serialize_result_type serialize() const { - return "<<"; - } - }; +#include // std::string +#include // std::shared_ptr, std::unique_ptr +#include // std::vector +// #include "functional/cxx_optional.h" - /** - * Result of bitwise shift left << operator - */ - template - using bitwise_shift_left_t = binary_operator; +// #include "functional/cxx_type_traits_polyfill.h" - struct bitwise_shift_right_string { - serialize_result_type serialize() const { - return ">>"; - } - }; +// #include "type_traits.h" - /** - * Result of bitwise shift right >> operator - */ - template - using bitwise_shift_right_t = binary_operator; +// #include "is_std_ptr.h" - struct bitwise_and_string { - serialize_result_type serialize() const { - return "&"; - } - }; +#include +#include - /** - * Result of bitwise and & operator - */ - template - using bitwise_and_t = binary_operator; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - struct bitwise_or_string { - serialize_result_type serialize() const { - return "|"; - } - }; + /** + * Specialization for optional type (std::shared_ptr / std::unique_ptr). + */ + template + struct is_std_ptr : std::false_type {}; - /** - * Result of bitwise or | operator - */ - template - using bitwise_or_t = binary_operator; + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::shared_ptr::element_type; - struct bitwise_not_string { - serialize_result_type serialize() const { - return "~"; - } - }; + static std::shared_ptr make(std::remove_cv_t&& v) { + return std::make_shared(std::move(v)); + } + }; - /** - * Result of bitwise not ~ operator - */ - template - struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { - using argument_type = T; + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::unique_ptr::element_type; - argument_type argument; + static auto make(std::remove_cv_t&& v) { + return std::make_unique(std::move(v)); + } + }; +} - bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} - }; - - struct assign_string { - serialize_result_type serialize() const { - return "="; - } - }; - - /** - * Result of assign = operator - */ - template - using assign_t = binary_operator; - - /** - * Assign operator traits. Common case - */ - template - struct is_assign_t : public std::false_type {}; - - /** - * Assign operator traits. Specialized case - */ - template - struct is_assign_t> : public std::true_type {}; - } - - /** - * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT - * name || '@gmail.com' FROM users - */ - template - internal::conc_t conc(L l, R r) { - return {std::move(l), std::move(r)}; - } - - /** - * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users - */ - template - internal::add_t add(L l, R r) { - return {std::move(l), std::move(r)}; - } +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** - * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users + * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) */ - template - internal::sub_t sub(L l, R r) { - return {std::move(l), std::move(r)}; - } + template + struct type_printer {}; - /** - * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users - */ - template - internal::mul_t mul(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct integer_printer { + const std::string& print() const { + static const std::string res = "INTEGER"; + return res; + } + }; - /** - * Public interface for / operator. Example: `select(div(&User::salary, 3));` => SELECT salary / 3 FROM users - * @note Please notice that ::div function already exists in pure C standard library inside header. - * If you use `using namespace sqlite_orm` directive you an specify which `div` you call explicitly using `::div` or `sqlite_orm::div` statements. - */ - template - internal::div_t div(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct text_printer { + const std::string& print() const { + static const std::string res = "TEXT"; + return res; + } + }; - /** - * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users - */ - template - internal::mod_t mod(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct real_printer { + const std::string& print() const { + static const std::string res = "REAL"; + return res; + } + }; - template - internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct blob_printer { + const std::string& print() const { + static const std::string res = "BLOB"; + return res; + } + }; - template - internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { - return {std::move(l), std::move(r)}; - } + // Note: char, unsigned/signed char are used for storing integer values, not char values. + template + struct type_printer>, + std::is_integral>::value>> : integer_printer { + }; - template - internal::bitwise_and_t bitwise_and(L l, R r) { - return {std::move(l), std::move(r)}; - } + template + struct type_printer::value>> : real_printer {}; - template - internal::bitwise_or_t bitwise_or(L l, R r) { - return {std::move(l), std::move(r)}; - } + template + struct type_printer, + std::is_base_of, + std::is_base_of>::value>> + : text_printer {}; template - internal::bitwise_not_t bitwise_not(T t) { - return {std::move(t)}; - } + struct type_printer::value>> : type_printer {}; - template - internal::assign_t assign(L l, R r) { - return {std::move(l), std::move(r)}; - } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct type_printer>> + : type_printer {}; +#endif + template<> + struct type_printer, void> : blob_printer {}; } -#pragma once -#include // std::tuple -#include // std::string -#include // std::unique_ptr -#include // std::is_same, std::is_member_object_pointer -#include // std::move - -// #include "../functional/cxx_universal.h" - -// #include "../functional/cxx_type_traits_polyfill.h" +// #include "constraints.h" -// #include "../tuple_helper/tuple_traits.h" +#include // std::is_base_of, std::false_type, std::true_type +#include // std::system_error +#include // std::ostream +#include // std::string +#include // std::tuple -// #include "../tuple_helper/tuple_filter.h" +// #include "functional/cxx_universal.h" -// #include "../type_traits.h" +// #include "functional/cxx_type_traits_polyfill.h" -// #include "../member_traits/member_traits.h" +// #include "functional/mpl.h" -// #include "../type_is_nullable.h" +// #include "tuple_helper/same_or_void.h" -// #include "../constraints.h" +#include // std::common_type namespace sqlite_orm { - namespace internal { - struct column_identifier { + /** + * Accepts any number of arguments and evaluates a nested `type` typename as `T` if all arguments are the same, otherwise `void`. + */ + template + struct same_or_void { + using type = void; + }; - /** - * Column name. - */ - std::string name; + template + struct same_or_void { + using type = A; }; - struct empty_setter {}; + template + struct same_or_void { + using type = A; + }; - /* - * Encapsulates object member pointers that are used as column fields, - * and whose object is mapped to storage. - * - * G is a member object pointer or member function pointer - * S is a member function pointer or `empty_setter` - */ - template - struct column_field { - using member_pointer_t = G; - using setter_type = S; - using object_type = member_object_type_t; - using field_type = member_field_type_t; + template + using same_or_void_t = typename same_or_void::type; - /** - * Member pointer used to read a field value. - * If it is a object member pointer it is also used to write a field value. - */ - const member_pointer_t member_pointer; + template + struct same_or_void : same_or_void {}; - /** - * Setter member function to write a field value - */ - SQLITE_ORM_NOUNIQUEADDRESS - const setter_type setter; + template + struct common_type_of; - /** - * Simplified interface for `NOT NULL` constraint - */ - constexpr bool is_not_null() const { - return !type_is_nullable::value; - } - }; + template class Pack, class... Types> + struct common_type_of> : std::common_type {}; - /* - * Encapsulates a tuple of column constraints. + /** + * Accepts a pack of types and defines a nested `type` typename to a common type if possible, otherwise nonexistent. * - * Op... is a constraints pack, e.g. primary_key_t, unique_t etc + * @note: SFINAE friendly like `std::common_type`. */ - template - struct column_constraints { - using constraints_type = std::tuple; + template + using common_type_of_t = typename common_type_of::type; + } +} - SQLITE_ORM_NOUNIQUEADDRESS - constraints_type constraints; +// #include "tuple_helper/tuple_traits.h" - /** - * Checks whether contraints contain specified type. - */ - template class Trait> - constexpr static bool is() { - return tuple_has::value; - } +// #include "tuple_helper/tuple_filter.h" - /** - * Simplified interface for `DEFAULT` constraint - * @return string representation of default value if it exists otherwise nullptr - */ - std::unique_ptr default_value() const; - }; +// #include "type_traits.h" - /** - * Column definition. - * - * It is a composition of orthogonal information stored in different base classes. - */ - template - struct column_t : column_identifier, column_field, column_constraints { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - column_t(std::string name, G memberPointer, S setter, std::tuple op) : - column_identifier{std::move(name)}, column_field{memberPointer, setter}, - column_constraints{std::move(op)} {} -#endif - }; +// #include "collate_argument.h" - template - struct column_field_expression { - using type = void; - }; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - template - struct column_field_expression, void> { - using type = typename column_t::member_pointer_t; - }; + namespace internal { - template - using column_field_expression_t = typename column_field_expression::type; + enum class collate_argument { + binary, + nocase, + rtrim, + }; + } +} - template - SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of::value; +// #include "error_code.h" - template - using is_column = polyfill::bool_constant>; +// #include "table_type_of.h" - template - using col_index_sequence_with_field_type = - filter_tuple_sequence_t::template fn, - field_type_t, - filter_tuple_sequence_t>; +// #include "type_printer.h" - template class TraitFn> - using col_index_sequence_with = filter_tuple_sequence_t::template fn, - constraints_type_t, - filter_tuple_sequence_t>; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - template class TraitFn> - using col_index_sequence_excluding = filter_tuple_sequence_t::template fn, - constraints_type_t, - filter_tuple_sequence_t>; - } + namespace internal { - /** - * Factory function for a column definition from a member object pointer of the object to be mapped. - */ - template = true> - internal::column_t - make_column(std::string name, M memberPointer, Op... constraints) { - static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + enum class conflict_clause_t { + rollback, + abort, + fail, + ignore, + replace, + }; - // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, - // as this will lead to UB with Clang on MinGW! - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), memberPointer, {}, std::tuple{std::move(constraints)...}}); - } + struct primary_key_base { + enum class order_by { + unspecified, + ascending, + descending, + }; + struct { + order_by asc_option = order_by::unspecified; + conflict_clause_t conflict_clause = conflict_clause_t::rollback; + bool conflict_clause_is_on = false; + } options; + }; - /** - * Factory function for a column definition from "setter" and "getter" member function pointers of the object to be mapped. - */ - template = true, - internal::satisfies = true> - internal::column_t make_column(std::string name, S setter, G getter, Op... constraints) { - static_assert(std::is_same, internal::getter_field_type_t>::value, - "Getter and setter must get and set same data type"); - static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + template + struct primary_key_with_autoincrement : T { + using primary_key_type = T; - // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, - // as this will lead to UB with Clang on MinGW! - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), getter, setter, std::tuple{std::move(constraints)...}}); - } + const primary_key_type& as_base() const { + return *this; + } +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} +#endif + }; - /** - * Factory function for a column definition from "getter" and "setter" member function pointers of the object to be mapped. - */ - template = true, - internal::satisfies = true> - internal::column_t make_column(std::string name, G getter, S setter, Op... constraints) { - static_assert(std::is_same, internal::getter_field_type_t>::value, - "Getter and setter must get and set same data type"); - static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + /** + * PRIMARY KEY constraint class. + * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when + * used within `make_column` function. + */ + template + struct primary_key_t : primary_key_base { + using self = primary_key_t; + using order_by = primary_key_base::order_by; + using columns_tuple = std::tuple; - // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, - // as this will lead to UB with Clang on MinGW! - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), getter, setter, std::tuple{std::move(constraints)...}}); - } -} -#pragma once + columns_tuple columns; -#include // std::string -#include // std::enable_if, std::is_same, std::remove_const -#include // std::vector -#include // std::tuple -#include // std::move, std::forward -#include // std::stringstream + primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} -// #include "functional/cxx_universal.h" + self asc() const { + auto res = *this; + res.options.asc_option = order_by::ascending; + return res; + } -// #include "functional/cxx_type_traits_polyfill.h" + self desc() const { + auto res = *this; + res.options.asc_option = order_by::descending; + return res; + } -// #include "is_base_of_template.h" + primary_key_with_autoincrement autoincrement() const { + return {*this}; + } -// #include "type_traits.h" + self on_conflict_rollback() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::rollback; + return res; + } -// #include "collate_argument.h" + self on_conflict_abort() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::abort; + return res; + } -// #include "constraints.h" + self on_conflict_fail() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::fail; + return res; + } -// #include "optional_container.h" + self on_conflict_ignore() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::ignore; + return res; + } -namespace sqlite_orm { + self on_conflict_replace() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::replace; + return res; + } + }; - namespace internal { + struct unique_base { + operator std::string() const { + return "UNIQUE"; + } + }; /** - * This is a cute class which allows storing something or nothing - * depending on template argument. Useful for optional class members + * UNIQUE constraint class. */ - template - struct optional_container { - using type = T; + template + struct unique_t : unique_base { + using columns_tuple = std::tuple; - type field; + columns_tuple columns; - template - void apply(const L& l) const { - l(this->field); - } + unique_t(columns_tuple columns_) : columns(std::move(columns_)) {} }; - template<> - struct optional_container { - using type = void; + struct unindexed_t {}; - template - void apply(const L&) const { - //.. - } + template + struct prefix_t { + using value_type = T; + + value_type value; }; - } -} -// #include "serializer_context.h" + template + struct tokenize_t { + using value_type = T; -namespace sqlite_orm { + value_type value; + }; - namespace internal { + template + struct content_t { + using value_type = T; - struct serializer_context_base { - bool replace_bindable_with_question = false; - bool skip_table_name = true; - bool use_parentheses = true; - bool fts5_columns = false; + value_type value; }; - template - struct serializer_context : serializer_context_base { - using db_objects_type = DBOs; - - const db_objects_type& db_objects; - - serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} + template + struct table_content_t { + using mapped_type = T; }; - template - struct serializer_context_builder { - using storage_type = S; - using db_objects_type = typename storage_type::db_objects_type; + /** + * DEFAULT constraint class. + * T is a value type. + */ + template + struct default_t { + using value_type = T; - serializer_context_builder(const storage_type& storage_) : storage{storage_} {} + value_type value; - serializer_context operator()() const { - return {obtain_db_objects(this->storage)}; + operator std::string() const { + return "DEFAULT"; } - - const storage_type& storage; }; - } -} +#if SQLITE_VERSION_NUMBER >= 3006019 + /** + * FOREIGN KEY constraint class. + * Cs are columns which has foreign key + * Rs are column which C references to + * Available in SQLite 3.6.19 or higher + */ -// #include "serialize_result_type.h" + template + struct foreign_key_t; -// #include "tags.h" + enum class foreign_key_action { + none, // not specified + no_action, + restrict_, + set_null, + set_default, + cascade, + }; -// #include "table_reference.h" + inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { + switch(action) { + case foreign_key_action::no_action: + os << "NO ACTION"; + break; + case foreign_key_action::restrict_: + os << "RESTRICT"; + break; + case foreign_key_action::set_null: + os << "SET NULL"; + break; + case foreign_key_action::set_default: + os << "SET DEFAULT"; + break; + case foreign_key_action::cascade: + os << "CASCADE"; + break; + case foreign_key_action::none: + break; + } + return os; + } -#include // std::remove_const, std::type_identity -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED -#include -#endif + struct on_update_delete_base { + const bool update; // true if update and false if delete -// #include "functional/cxx_type_traits_polyfill.h" + operator std::string() const { + if(this->update) { + return "ON UPDATE"; + } else { + return "ON DELETE"; + } + } + }; -namespace sqlite_orm { - namespace internal { - /* - * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. + /** + * F - foreign key class */ - template - struct table_reference : polyfill::type_identity {}; - - template - struct decay_table_ref : std::remove_const {}; - template - struct decay_table_ref> : polyfill::type_identity {}; - template - struct decay_table_ref> : polyfill::type_identity {}; + template + struct on_update_delete_t : on_update_delete_base { + using foreign_key_type = F; - template - using decay_table_ref_t = typename decay_table_ref::type; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - using auto_decay_table_ref_t = typename decay_table_ref::type; -#endif + const foreign_key_type& fk; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_table_reference_v = - polyfill::is_specialization_of_v, table_reference>; + on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : + on_update_delete_base{update_}, fk(fk_), _action(action_) {} - template - struct is_table_reference : polyfill::bool_constant> {}; - } + foreign_key_action _action = foreign_key_action::none; -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. - * - * A concrete table reference has the following traits: - * - specialization of `table_reference`, whose `type` typename references a mapped object. - */ - template - concept orm_table_reference = polyfill::is_specialization_of_v, internal::table_reference>; -#endif -} + foreign_key_type no_action() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::no_action; + } else { + res.on_delete._action = foreign_key_action::no_action; + } + return res; + } -// #include "alias_traits.h" + foreign_key_type restrict_() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::restrict_; + } else { + res.on_delete._action = foreign_key_action::restrict_; + } + return res; + } -#include // std::is_base_of, std::is_same -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include -#endif + foreign_key_type set_null() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::set_null; + } else { + res.on_delete._action = foreign_key_action::set_null; + } + return res; + } -// #include "functional/cxx_universal.h" + foreign_key_type set_default() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::set_default; + } else { + res.on_delete._action = foreign_key_action::set_default; + } + return res; + } -// #include "functional/cxx_type_traits_polyfill.h" + foreign_key_type cascade() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::cascade; + } else { + res.on_delete._action = foreign_key_action::cascade; + } + return res; + } -// #include "type_traits.h" + operator bool() const { + return this->_action != foreign_key_action::none; + } + }; -// #include "table_reference.h" + template + bool operator==(const on_update_delete_t& lhs, const on_update_delete_t& rhs) { + return lhs._action == rhs._action; + } -namespace sqlite_orm { + template + struct foreign_key_t, std::tuple> { + using columns_type = std::tuple; + using references_type = std::tuple; + using self = foreign_key_t; - /** @short Base class for a custom table alias, column alias or expression alias. - */ - struct alias_tag {}; + /** + * Holds obect type of all referenced columns. + */ + using target_type = same_or_void_t...>; - namespace internal { + /** + * Holds obect type of all source columns. + */ + using source_type = same_or_void_t...>; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_alias_v = std::is_base_of::value; + columns_type columns; + references_type references; - template - struct is_alias : polyfill::bool_constant> {}; + on_update_delete_t on_update; + on_update_delete_t on_delete; - /** @short Alias of a column in a record set, see `orm_column_alias`. - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_column_alias_v = - polyfill::conjunction, polyfill::negation>>::value; + static_assert(std::tuple_size::value == std::tuple_size::value, + "Columns size must be equal to references tuple"); + static_assert(!std::is_same::value, "All references must have the same type"); - template - struct is_column_alias : is_alias {}; + foreign_key_t(columns_type columns_, references_type references_) : + columns(std::move(columns_)), references(std::move(references_)), + on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} - /** @short Alias of any type of record set, see `orm_recordset_alias`. - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_recordset_alias_v = - polyfill::conjunction, polyfill::is_detected>::value; + foreign_key_t(const self& other) : + columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), + on_delete(*this, false, other.on_delete._action) {} - template - struct is_recordset_alias : polyfill::bool_constant> {}; + self& operator=(const self& other) { + this->columns = other.columns; + this->references = other.references; + this->on_update = {*this, true, other.on_update._action}; + this->on_delete = {*this, false, other.on_delete._action}; + return *this; + } + }; - /** @short Alias of a concrete table, see `orm_table_alias`. + template + bool operator==(const foreign_key_t& lhs, const foreign_key_t& rhs) { + return lhs.columns == rhs.columns && lhs.references == rhs.references && lhs.on_update == rhs.on_update && + lhs.on_delete == rhs.on_delete; + } + + /** + * Cs can be a class member pointer, a getter function member pointer or setter + * func member pointer + * Available in SQLite 3.6.19 or higher */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction< - is_recordset_alias, - polyfill::negation, std::remove_const_t>>>::value; + template + struct foreign_key_intermediate_t { + using tuple_type = std::tuple; - template - struct is_table_alias : polyfill::bool_constant> {}; + tuple_type columns; - /** @short Moniker of a CTE, see `orm_cte_moniker`. - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_cte_moniker_v = -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - polyfill::conjunction_v, - std::is_same, std::remove_const_t>>; -#else - false; + template + foreign_key_t, std::tuple> references(Rs... refs) { + return {std::move(this->columns), {std::forward(refs)...}}; + } + }; #endif - template - using is_cte_moniker = polyfill::bool_constant>; - } - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - concept orm_alias = std::derived_from; + struct collate_constraint_t { + collate_argument argument = collate_argument::binary; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + collate_constraint_t(collate_argument argument) : argument{argument} {} +#endif - /** @short Specifies that a type is an alias of a column in a record set. - * - * A column alias has the following traits: - * - is derived from `alias_tag` - * - must not have a nested `type` typename - */ - template - concept orm_column_alias = (orm_alias && !orm_names_type); + operator std::string() const { + return "COLLATE " + this->string_from_collate_argument(this->argument); + } - /** @short Specifies that a type is an alias of any type of record set. - * - * A record set alias has the following traits: - * - is derived from `alias_tag`. - * - has a nested `type` typename, which refers to a mapped object. - */ - template - concept orm_recordset_alias = (orm_alias && orm_names_type); + static std::string string_from_collate_argument(collate_argument argument) { + switch(argument) { + case collate_argument::binary: + return "BINARY"; + case collate_argument::nocase: + return "NOCASE"; + case collate_argument::rtrim: + return "RTRIM"; + } + throw std::system_error{orm_error_code::invalid_collate_argument_enum}; + } + }; - /** @short Specifies that a type is an alias of a concrete table. - * - * A concrete table alias has the following traits: - * - is derived from `alias_tag`. - * - has a `type` typename, which refers to another mapped object (i.e. doesn't refer to itself). - */ - template - concept orm_table_alias = (orm_recordset_alias && !std::same_as>); + template + struct check_t { + using expression_type = T; - /** @short Moniker of a CTE. - * - * A CTE moniker has the following traits: - * - is derived from `alias_tag`. - * - has a `type` typename, which refers to itself. - */ - template - concept orm_cte_moniker = (orm_recordset_alias && std::same_as>); + expression_type expression; + }; - /** @short Specifies that a type refers to a mapped table (possibly aliased). - */ - template - concept orm_refers_to_table = (orm_table_reference || orm_table_alias); + struct basic_generated_always { + enum class storage_type { + not_specified, + virtual_, + stored, + }; - /** @short Specifies that a type refers to a recordset. - */ - template - concept orm_refers_to_recordset = (orm_table_reference || orm_recordset_alias); +#if SQLITE_VERSION_NUMBER >= 3031000 + bool full = true; + storage_type storage = storage_type::not_specified; +#endif - /** @short Specifies that a type is a mapped recordset (table reference). - */ - template - concept orm_mapped_recordset = (orm_table_reference || orm_cte_moniker); +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} #endif -} + }; -// #include "expression.h" +#if SQLITE_VERSION_NUMBER >= 3031000 + template + struct generated_always_t : basic_generated_always { + using expression_type = T; -#include -#include // std::enable_if -#include // std::move, std::forward, std::declval -// #include "functional/cxx_optional.h" + expression_type expression; -// #include "functional/cxx_universal.h" + generated_always_t(expression_type expression_, bool full, storage_type storage) : + basic_generated_always{full, storage}, expression(std::move(expression_)) {} -// #include "functional/cxx_type_traits_polyfill.h" + generated_always_t virtual_() { + return {std::move(this->expression), this->full, storage_type::virtual_}; + } -// #include "tags.h" + generated_always_t stored() { + return {std::move(this->expression), this->full, storage_type::stored}; + } + }; +#endif -namespace sqlite_orm { + struct null_t {}; - namespace internal { + struct not_null_t {}; + } - template - struct in_t; + namespace internal { - template - struct and_condition_t; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = +#if SQLITE_VERSION_NUMBER >= 3006019 + polyfill::is_specialization_of::value; +#else + false; +#endif - template - struct or_condition_t; + template + struct is_foreign_key : polyfill::bool_constant> {}; - /** - * Result of c(...) function. Has operator= overloaded which returns assign_t - */ template - struct expression_t { - T value; + SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = std::is_base_of::value; - template - assign_t operator=(R r) const { - return {this->value, std::move(r)}; - } + template + struct is_primary_key : polyfill::bool_constant> {}; - assign_t operator=(nullptr_t) const { - return {this->value, nullptr}; - } -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - assign_t operator=(std::nullopt_t) const { - return {this->value, std::nullopt}; - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = +#if SQLITE_VERSION_NUMBER >= 3031000 + polyfill::is_specialization_of::value; +#else + false; #endif - template - in_t in(Args... args) const { - return {this->value, {std::forward(args)...}, false}; - } - template - in_t not_in(Args... args) const { - return {this->value, {std::forward(args)...}, true}; - } + template + struct is_generated_always : polyfill::bool_constant> {}; - template - and_condition_t and_(R right) const { - return {this->value, std::move(right)}; - } + /** + * PRIMARY KEY INSERTABLE traits. + */ + template + struct is_primary_key_insertable + : polyfill::disjunction< + mpl::invoke_t, + check_if_has_template>, + constraints_type_t>, + std::is_base_of>>> { - template - or_condition_t or_(R right) const { - return {this->value, std::move(right)}; - } + static_assert(tuple_has, is_primary_key>::value, + "an unexpected type was passed"); }; template - SQLITE_ORM_INLINE_VAR constexpr bool - is_operator_argument_v::value>> = true; - - template - T get_from_expression(T value) { - return std::move(value); - } - - template - T get_from_expression(expression_t expression) { - return std::move(expression.value); - } + using is_column_constraint = mpl::invoke_t>, + check_if_is_type, + check_if_is_type, + check_if_is_type>, + check_if_is_template, + check_if_is_template, + check_if_is_type, + check_if, + check_if_is_type>, + T>; + } - template - using unwrap_expression_t = decltype(get_from_expression(std::declval())); +#if SQLITE_VERSION_NUMBER >= 3031000 + template + internal::generated_always_t generated_always_as(T expression) { + return {std::move(expression), true, internal::basic_generated_always::storage_type::not_specified}; } - /** - * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or - * `storage.update(set(c(&User::name) = "Dua Lipa")); - */ template - internal::expression_t c(T value) { - return {std::move(value)}; + internal::generated_always_t as(T expression) { + return {std::move(expression), false, internal::basic_generated_always::storage_type::not_specified}; } -} - -// #include "column_pointer.h" - -#include // std::enable_if, std::is_convertible -#include // std::move - -// #include "functional/cxx_core_features.h" - -// #include "functional/cxx_type_traits_polyfill.h" - -// #include "type_traits.h" - -// #include "table_reference.h" - -// #include "alias_traits.h" - -// #include "tags.h" - -namespace sqlite_orm { - namespace internal { - /** - * This class is used to store explicit mapped type T and its column descriptor (member pointer/getter/setter). - * Is useful when mapped type is derived from other type and base class has members mapped to a storage. - */ - template - struct column_pointer { - using type = T; - using field_type = F; - - field_type field; - }; - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_column_pointer_v = - polyfill::is_specialization_of::value; - - template - struct is_column_pointer : polyfill::bool_constant> {}; - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v::value>> = - true; - -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template - struct alias_holder; #endif - } +#if SQLITE_VERSION_NUMBER >= 3006019 /** - * Explicitly refer to a column, used in contexts - * where the automatic object mapping deduction needs to be overridden. - * - * Example: - * struct BaseType : { int64 id; }; - * struct MyType : BaseType { ... }; - * storage.select(column(&BaseType::id)); + * FOREIGN KEY constraint construction function that takes member pointer as argument + * Available in SQLite 3.6.19 or higher */ - template = true> - constexpr internal::column_pointer column(F Base::*field) { - static_assert(std::is_convertible::value, "Field must be from derived class"); - return {field}; + template + internal::foreign_key_intermediate_t foreign_key(Cs... columns) { + return {{std::forward(columns)...}}; } +#endif -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Explicitly refer to a column. + * UNIQUE table constraint builder function. */ - template - constexpr auto column(F O::*field) { - return column>(field); + template + internal::unique_t unique(Args... args) { + return {{std::forward(args)...}}; } - // Intentionally place pointer-to-member operator for table references in the internal namespace - // to facilitate ADL (Argument Dependent Lookup) - namespace internal { - /** - * Explicitly refer to a column. - */ - template - constexpr auto operator->*(const R& /*table*/, F O::*field) { - return column(field); - } + /** + * UNIQUE column constraint builder function. + */ + inline internal::unique_t<> unique() { + return {{}}; } +#if SQLITE_VERSION_NUMBER >= 3009000 /** - * Make a table reference. + * UNINDEXED column constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#the_unindexed_column_option */ - template - requires(!orm_recordset_alias) - consteval internal::table_reference column() { + inline internal::unindexed_t unindexed() { return {}; } /** - * Make a table reference. + * prefix=N table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#prefix_indexes */ - template - requires(!orm_recordset_alias) - consteval internal::table_reference c() { - return {}; + template + internal::prefix_t prefix(T value) { + return {std::move(value)}; } -#endif -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) /** - * Explicitly refer to a column alias mapped into a CTE or subquery. - * - * Example: - * struct Object { ... }; - * using cte_1 = decltype(1_ctealias); - * storage.with(cte()(select(&Object::id)), select(column(&Object::id))); - * storage.with(cte()(select(&Object::id)), select(column(1_colalias))); - * storage.with(cte()(select(as(&Object::id))), select(column(colalias_a{}))); - * storage.with(cte(colalias_a{})(select(&Object::id)), select(column(colalias_a{}))); - * storage.with(cte()(select(as(&Object::id))), select(column(get()))); + * tokenize='...'' table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#tokenizers */ - template = true> - constexpr auto column(F field) { - using namespace ::sqlite_orm::internal; + template + internal::tokenize_t tokenize(T value) { + return {std::move(value)}; + } - static_assert(is_cte_moniker_v, "`Moniker' must be a CTE moniker"); + /** + * content='' table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#contentless_tables + */ + template + internal::content_t content(T value) { + return {std::move(value)}; + } - if constexpr(polyfill::is_specialization_of_v) { - static_assert(is_column_alias_v>); - return column_pointer{{}}; - } else if constexpr(is_column_alias_v) { - return column_pointer>{{}}; - } else { - return column_pointer{std::move(field)}; - } - (void)field; + /** + * content='table' table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#external_content_tables + */ + template + internal::table_content_t content() { + return {}; } +#endif -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Explicitly refer to a column mapped into a CTE or subquery. - * - * Example: - * struct Object { ... }; - * storage.with(cte<"z"_cte>()(select(&Object::id)), select(column<"z"_cte>(&Object::id))); - * storage.with(cte<"z"_cte>()(select(&Object::id)), select(column<"z"_cte>(1_colalias))); + * PRIMARY KEY table constraint builder function. */ - template - constexpr auto column(F field) { - using Moniker = std::remove_const_t; - return column(std::forward(field)); + template + internal::primary_key_t primary_key(Cs... cs) { + return {{std::forward(cs)...}}; } /** - * Explicitly refer to a column mapped into a CTE or subquery. - * - * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) - * because recordset aliases are derived from `sqlite_orm::alias_tag` - * - * Example: - * struct Object { ... }; - * using cte_1 = decltype(1_ctealias); - * storage.with(cte()(select(&Object::id)), select(1_ctealias->*&Object::id)); - * storage.with(cte()(select(&Object::id)), select(1_ctealias->*1_colalias)); + * PRIMARY KEY column constraint builder function. */ - template - constexpr auto operator->*(const Moniker& /*moniker*/, F field) { - return column(std::forward(field)); + inline internal::primary_key_t<> primary_key() { + return {{}}; } -#endif -#endif -} -// #include "tags.h" -// #include "type_printer.h" + template + internal::default_t default_value(T t) { + return {std::move(t)}; + } -// #include "literal.h" + inline internal::collate_constraint_t collate_nocase() { + return {internal::collate_argument::nocase}; + } -namespace sqlite_orm { - namespace internal { + inline internal::collate_constraint_t collate_binary() { + return {internal::collate_argument::binary}; + } - /* - * Protect an otherwise bindable element so that it is always serialized as a literal value. - */ - template - struct literal_holder { - using type = T; + inline internal::collate_constraint_t collate_rtrim() { + return {internal::collate_argument::rtrim}; + } - type value; - }; + template + internal::check_t check(T t) { + return {std::move(t)}; + } + + inline internal::null_t null() { + return {}; + } + inline internal::not_null_t not_null() { + return {}; } } -namespace sqlite_orm { +// #include "field_printer.h" - namespace internal { +#include // std::string +#include // std::stringstream +#include // std::vector +#include // std::shared_ptr, std::unique_ptr +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert +#include // std::codecvt_utf8_utf16 +#endif +// #include "functional/cxx_optional.h" - struct limit_string { - operator std::string() const { - return "LIMIT"; - } - }; +// #include "functional/cxx_universal.h" +// ::nullptr_t +// #include "functional/cxx_type_traits_polyfill.h" - /** - * Stores LIMIT/OFFSET info - */ - template - struct limit_t : limit_string { - T lim; - optional_container off; - - limit_t() = default; - - limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} +// #include "is_std_ptr.h" - limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} - }; +// #include "type_traits.h" - template - struct is_limit : std::false_type {}; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - template - struct is_limit> : std::true_type {}; + /** + * Is used to print members mapped to objects in storage_t::dump member function. + * Other developers can create own specialization to map custom types + */ + template + struct field_printer; - /** - * Stores OFFSET only info + namespace internal { + /* + * Implementation note: the technique of indirect expression testing is because + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + * It must also be a type that differs from those for `is_preparable_v`, `is_bindable_v`. */ - template - struct offset_t { - T off; - }; + template + struct indirectly_test_printable; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v = false; template - using is_offset = polyfill::is_specialization_of; + SQLITE_ORM_INLINE_VAR constexpr bool + is_printable_v{})>>> = true; - /** - * Collated something - */ template - struct collate_t : public condition_t { - T expr; - collate_argument argument; - - collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} + struct is_printable : polyfill::bool_constant> {}; + } - operator std::string() const { - return collate_constraint_t{this->argument}; - } - }; + template + struct field_printer> { + std::string operator()(const T& t) const { + std::stringstream ss; + ss << t; + return ss.str(); + } + }; - struct named_collate_base { - std::string name; + /** + * Upgrade to integer is required when using unsigned char(uint8_t) + */ + template<> + struct field_printer { + std::string operator()(const unsigned char& t) const { + std::stringstream ss; + ss << +t; + return ss.str(); + } + }; - operator std::string() const { - return "COLLATE " + this->name; - } - }; + /** + * Upgrade to integer is required when using signed char(int8_t) + */ + template<> + struct field_printer { + std::string operator()(const signed char& t) const { + std::stringstream ss; + ss << +t; + return ss.str(); + } + }; - /** - * Collated something with custom collate function - */ - template - struct named_collate : named_collate_base { - T expr; + /** + * char is neither signed char nor unsigned char so it has its own specialization + */ + template<> + struct field_printer { + std::string operator()(const char& t) const { + std::stringstream ss; + ss << +t; + return ss.str(); + } + }; - named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} - }; + template + struct field_printer> { + std::string operator()(std::string string) const { + return string; + } + }; - struct negated_condition_string { - operator std::string() const { - return "NOT"; + template<> + struct field_printer, void> { + std::string operator()(const std::vector& t) const { + std::stringstream ss; + ss << std::hex; + for(auto c: t) { + ss << c; } - }; - - /** - * Result of not operator - */ - template - struct negated_condition_t : condition_t, negated_condition_string { - C c; + return ss.str(); + } + }; +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring (UTF-16 assumed). + */ + template + struct field_printer> { + std::string operator()(const std::wstring& wideString) const { + std::wstring_convert> converter; + return converter.to_bytes(wideString); + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT + template<> + struct field_printer { + std::string operator()(const nullptr_t&) const { + return "NULL"; + } + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<> + struct field_printer { + std::string operator()(const std::nullopt_t&) const { + return "NULL"; + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct field_printer, + internal::is_printable>>::value>> { + using unqualified_type = std::remove_cv_t; - negated_condition_t(C c_) : c(std::move(c_)) {} - }; + std::string operator()(const T& t) const { + if(t) { + return field_printer()(*t); + } else { + return field_printer{}(nullptr); + } + } + }; - /** - * Base class for binary conditions - * L is left argument type - * R is right argument type - * S is 'string' class (a class which has cast to `std::string` operator) - * Res is result type - */ - template - struct binary_condition : condition_t, S { - using left_type = L; - using right_type = R; - using result_type = Res; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct field_printer< + T, + std::enable_if_t, + internal::is_printable>>>> { + using unqualified_type = std::remove_cv_t; - left_type lhs; - right_type rhs; + std::string operator()(const T& t) const { + if(t.has_value()) { + return field_printer()(*t); + } else { + return field_printer{}(std::nullopt); + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} - binary_condition() = default; +// #include "rowid.h" - binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} - }; +#include // std::string - template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - template - struct is_binary_condition : polyfill::bool_constant> {}; + namespace internal { - struct and_condition_string { - serialize_result_type serialize() const { - return "AND"; + struct rowid_t { + operator std::string() const { + return "rowid"; } }; - /** - * Result of and operator - */ - template - struct and_condition_t : binary_condition { - using super = binary_condition; - - using super::super; + struct oid_t { + operator std::string() const { + return "oid"; + } }; - struct or_condition_string { - serialize_result_type serialize() const { - return "OR"; + struct _rowid_t { + operator std::string() const { + return "_rowid_"; } }; - /** - * Result of or operator - */ - template - struct or_condition_t : binary_condition { - using super = binary_condition; - - using super::super; + template + struct table_rowid_t : public rowid_t { + using type = T; }; - struct is_equal_string { - serialize_result_type serialize() const { - return "="; - } + template + struct table_oid_t : public oid_t { + using type = T; + }; + template + struct table__rowid_t : public _rowid_t { + using type = T; }; - /** - * = and == operators object - */ - template - struct is_equal_t : binary_condition, negatable_t { - using self = is_equal_t; + } - using binary_condition::binary_condition; + inline internal::rowid_t rowid() { + return {}; + } - collate_t collate_binary() const { - return {*this, collate_argument::binary}; - } + inline internal::oid_t oid() { + return {}; + } - collate_t collate_nocase() const { - return {*this, collate_argument::nocase}; - } + inline internal::_rowid_t _rowid_() { + return {}; + } - collate_t collate_rtrim() const { - return {*this, collate_argument::rtrim}; - } + template + internal::table_rowid_t rowid() { + return {}; + } - named_collate collate(std::string name) const { - return {*this, std::move(name)}; - } + template + internal::table_oid_t oid() { + return {}; + } - template - named_collate collate() const { - std::stringstream ss; - ss << C::name() << std::flush; - return {*this, ss.str()}; - } + template + internal::table__rowid_t _rowid_() { + return {}; + } +} + +// #include "operators.h" + +#include // std::false_type, std::true_type +#include // std::move + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_base_of_template.h" + +#include // std::true_type, std::false_type, std::declval + +namespace sqlite_orm { + + namespace internal { + + /* + * This is because of bug in MSVC, for more information, please visit + * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 + */ +#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template class Base> + struct is_base_of_template_impl { + template + static constexpr std::true_type test(const Base&); + + static constexpr std::false_type test(...); }; - template - struct is_equal_with_table_t : negatable_t { + template class C> + using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); +#else + template class C, typename... Ts> + std::true_type is_base_of_template_impl(const C&); + + template class C> + std::false_type is_base_of_template_impl(...); + + template class C> + using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); +#endif + + template class C> + SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; + } +} + +// #include "tags.h" + +// #include "serialize_result_type.h" + +// #include "functional/cxx_string_view.h" + +// #include "cxx_core_features.h" + +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif + +#if __cpp_lib_string_view >= 201606L +#define SQLITE_ORM_STRING_VIEW_SUPPORTED +#endif + +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // std::string +#endif + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + using serialize_result_type = std::string_view; + using serialize_arg_type = std::string_view; +#else + using serialize_result_type = std::string; + using serialize_arg_type = const std::string&; +#endif + } +} + +_EXPORT_SQLITE_ORM namespace sqlite_orm { + + namespace internal { + + template + struct binary_operator : Ds... { using left_type = L; using right_type = R; + left_type lhs; right_type rhs; - is_equal_with_table_t(right_type rhs) : rhs(std::move(rhs)) {} + binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} }; - struct is_not_equal_string { + template + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_of_template::value; + + template + using is_binary_operator = polyfill::bool_constant>; + + struct conc_string { serialize_result_type serialize() const { - return "!="; + return "||"; } }; /** - * != operator object + * Result of concatenation || operator */ template - struct is_not_equal_t : binary_condition, negatable_t { - using self = is_not_equal_t; - - using binary_condition::binary_condition; - - collate_t collate_binary() const { - return {*this, collate_argument::binary}; - } - - collate_t collate_nocase() const { - return {*this, collate_argument::nocase}; - } - - collate_t collate_rtrim() const { - return {*this, collate_argument::rtrim}; - } - }; + using conc_t = binary_operator; - struct greater_than_string { + struct add_string { serialize_result_type serialize() const { - return ">"; + return "+"; } }; /** - * > operator object. + * Result of addition + operator */ template - struct greater_than_t : binary_condition, negatable_t { - using self = greater_than_t; - - using binary_condition::binary_condition; + using add_t = binary_operator; - collate_t collate_binary() const { - return {*this, collate_argument::binary}; + struct sub_string { + serialize_result_type serialize() const { + return "-"; } + }; - collate_t collate_nocase() const { - return {*this, collate_argument::nocase}; - } + /** + * Result of substitute - operator + */ + template + using sub_t = binary_operator; - collate_t collate_rtrim() const { - return {*this, collate_argument::rtrim}; + struct mul_string { + serialize_result_type serialize() const { + return "*"; } }; - struct greater_or_equal_string { + /** + * Result of multiply * operator + */ + template + using mul_t = binary_operator; + + struct div_string { serialize_result_type serialize() const { - return ">="; + return "/"; } }; /** - * >= operator object. + * Result of divide / operator */ template - struct greater_or_equal_t : binary_condition, negatable_t { - using self = greater_or_equal_t; - - using binary_condition::binary_condition; + using div_t = binary_operator; - collate_t collate_binary() const { - return {*this, collate_argument::binary}; + struct mod_operator_string { + serialize_result_type serialize() const { + return "%"; } + }; - collate_t collate_nocase() const { - return {*this, collate_argument::nocase}; - } + /** + * Result of mod % operator + */ + template + using mod_t = binary_operator; - collate_t collate_rtrim() const { - return {*this, collate_argument::rtrim}; + struct bitwise_shift_left_string { + serialize_result_type serialize() const { + return "<<"; } }; - struct less_than_string { + /** + * Result of bitwise shift left << operator + */ + template + using bitwise_shift_left_t = binary_operator; + + struct bitwise_shift_right_string { serialize_result_type serialize() const { - return "<"; + return ">>"; } }; /** - * < operator object. + * Result of bitwise shift right >> operator */ template - struct less_than_t : binary_condition, negatable_t { - using self = less_than_t; + using bitwise_shift_right_t = binary_operator; - using binary_condition::binary_condition; - - collate_t collate_binary() const { - return {*this, collate_argument::binary}; - } - - collate_t collate_nocase() const { - return {*this, collate_argument::nocase}; - } - - collate_t collate_rtrim() const { - return {*this, collate_argument::rtrim}; - } - }; - - struct less_or_equal_string { + struct bitwise_and_string { serialize_result_type serialize() const { - return "<="; + return "&"; } }; /** - * <= operator object. + * Result of bitwise and & operator */ template - struct less_or_equal_t : binary_condition, negatable_t { - using self = less_or_equal_t; - - using binary_condition::binary_condition; - - collate_t collate_binary() const { - return {*this, collate_argument::binary}; - } - - collate_t collate_nocase() const { - return {*this, collate_argument::nocase}; - } + using bitwise_and_t = binary_operator; - collate_t collate_rtrim() const { - return {*this, collate_argument::rtrim}; + struct bitwise_or_string { + serialize_result_type serialize() const { + return "|"; } }; - struct in_base { - bool negative = false; // used in not_in - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - in_base(bool negative) : negative{negative} {} -#endif - }; - /** - * IN operator object. + * Result of bitwise or | operator */ - template - struct dynamic_in_t : condition_t, in_base, negatable_t { - using self = dynamic_in_t; - - L left; // left expression - A argument; // in arg - - dynamic_in_t(L left_, A argument_, bool negative_) : - in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} - }; - - template - struct in_t : condition_t, in_base, negatable_t { - L left; - std::tuple argument; - - in_t(L left_, decltype(argument) argument_, bool negative_) : - in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} - }; + template + using bitwise_or_t = binary_operator; - struct is_null_string { - operator std::string() const { - return "IS NULL"; + struct bitwise_not_string { + serialize_result_type serialize() const { + return "~"; } }; /** - * IS NULL operator object. + * Result of bitwise not ~ operator */ template - struct is_null_t : is_null_string, negatable_t { - using self = is_null_t; + struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { + using argument_type = T; - T t; + argument_type argument; - is_null_t(T t_) : t(std::move(t_)) {} + bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} }; - struct is_not_null_string { - operator std::string() const { - return "IS NOT NULL"; + struct assign_string { + serialize_result_type serialize() const { + return "="; } }; /** - * IS NOT NULL operator object. + * Result of assign = operator */ - template - struct is_not_null_t : is_not_null_string, negatable_t { - using self = is_not_null_t; - - T t; - - is_not_null_t(T t_) : t(std::move(t_)) {} - }; - - struct order_by_base { - int asc_desc = 0; // 1: asc, -1: desc - std::string _collate_argument; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - order_by_base() = default; - - order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : - asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} -#endif - }; + template + using assign_t = binary_operator; - struct order_by_string { - operator std::string() const { - return "ORDER BY"; - } - }; + /** + * Assign operator traits. Common case + */ + template + struct is_assign_t : public std::false_type {}; /** - * ORDER BY argument holder. + * Assign operator traits. Specialized case */ - template - struct order_by_t : order_by_base, order_by_string { - using expression_type = O; - using self = order_by_t; + template + struct is_assign_t> : public std::true_type {}; + } - expression_type expression; + /** + * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT + * name || '@gmail.com' FROM users + */ + template + internal::conc_t conc(L l, R r) { + return {std::move(l), std::move(r)}; + } - order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} + /** + * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users + */ + template + internal::add_t add(L l, R r) { + return {std::move(l), std::move(r)}; + } - self asc() const { - auto res = *this; - res.asc_desc = 1; - return res; - } + /** + * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users + */ + template + internal::sub_t sub(L l, R r) { + return {std::move(l), std::move(r)}; + } - self desc() const { - auto res = *this; - res.asc_desc = -1; - return res; - } + /** + * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users + */ + template + internal::mul_t mul(L l, R r) { + return {std::move(l), std::move(r)}; + } - self collate_binary() const { - auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); - return res; - } + /** + * Public interface for / operator. Example: `select(div(&User::salary, 3));` => SELECT salary / 3 FROM users + * @note Please notice that ::div function already exists in pure C standard library inside header. + * If you use `using namespace sqlite_orm` directive you an specify which `div` you call explicitly using `::div` or `sqlite_orm::div` statements. + */ + template + internal::div_t div(L l, R r) { + return {std::move(l), std::move(r)}; + } - self collate_nocase() const { - auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); - return res; - } + /** + * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users + */ + template + internal::mod_t mod(L l, R r) { + return {std::move(l), std::move(r)}; + } - self collate_rtrim() const { - auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); - return res; - } + template + internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { + return {std::move(l), std::move(r)}; + } - self collate(std::string name) const { - auto res = *this; - res._collate_argument = std::move(name); - return res; - } + template + internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { + return {std::move(l), std::move(r)}; + } - template - self collate() const { - std::stringstream ss; - ss << C::name() << std::flush; - return this->collate(ss.str()); - } - }; + template + internal::bitwise_and_t bitwise_and(L l, R r) { + return {std::move(l), std::move(r)}; + } - /** - * ORDER BY pack holder. - */ - template - struct multi_order_by_t : order_by_string { - using args_type = std::tuple; + template + internal::bitwise_or_t bitwise_or(L l, R r) { + return {std::move(l), std::move(r)}; + } - args_type args; + template + internal::bitwise_not_t bitwise_not(T t) { + return {std::move(t)}; + } - multi_order_by_t(args_type args_) : args{std::move(args_)} {} - }; + template + internal::assign_t assign(L l, R r) { + return {std::move(l), std::move(r)}; + } +} - struct dynamic_order_by_entry_t : order_by_base { - std::string name; +// #include "select_constraints.h" - dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : - order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} - }; - - /** - * C - serializer context class - */ - template - struct dynamic_order_by_t : order_by_string { - using context_t = C; - using entry_t = dynamic_order_by_entry_t; - using const_iterator = typename std::vector::const_iterator; - - dynamic_order_by_t(const context_t& context_) : context(context_) {} +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include +#endif +#include // std::remove_cvref, std::is_convertible, std::is_same, std::is_member_pointer +#include // std::string +#include // std::move +#include // std::tuple, std::get, std::tuple_size +// #include "functional/cxx_optional.h" - template - void push_back(order_by_t order_by) { - auto newContext = this->context; - newContext.skip_table_name = true; - auto columnName = serialize(order_by.expression, newContext); - this->entries.emplace_back(std::move(columnName), - order_by.asc_desc, - std::move(order_by._collate_argument)); - } +// #include "functional/cxx_universal.h" +// ::size_t +// #include "functional/cxx_type_traits_polyfill.h" - const_iterator begin() const { - return this->entries.begin(); - } +// #include "is_base_of_template.h" - const_iterator end() const { - return this->entries.end(); - } +// #include "tuple_helper/tuple_traits.h" - void clear() { - this->entries.clear(); - } +// #include "tuple_helper/tuple_transformer.h" - protected: - std::vector entries; - context_t context; - }; +// #include "tuple_helper/tuple_iteration.h" - template - SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = - polyfill::disjunction, - polyfill::is_specialization_of, - polyfill::is_specialization_of>::value; +// #include "optional_container.h" - template - struct is_order_by : polyfill::bool_constant> {}; +namespace sqlite_orm { - struct between_string { - operator std::string() const { - return "BETWEEN"; - } - }; + namespace internal { /** - * BETWEEN operator object. + * This is a cute class which allows storing something or nothing + * depending on template argument. Useful for optional class members */ - template - struct between_t : condition_t, between_string { - using expression_type = A; - using lower_type = T; - using upper_type = T; - - expression_type expr; - lower_type b1; - upper_type b2; + template + struct optional_container { + using type = T; - between_t(expression_type expr_, lower_type b1_, upper_type b2_) : - expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} - }; + type field; - struct like_string { - operator std::string() const { - return "LIKE"; + template + void apply(const L& l) const { + l(this->field); } }; - /** - * LIKE operator object. - */ - template - struct like_t : condition_t, like_string, negatable_t { - using self = like_t; - using arg_t = A; - using pattern_t = T; - using escape_t = E; - - arg_t arg; - pattern_t pattern; - optional_container arg3; // not escape cause escape exists as a function here - - like_t(arg_t arg_, pattern_t pattern_, optional_container escape_) : - arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} + template<> + struct optional_container { + using type = void; - template - like_t escape(C c) const { - optional_container newArg3{std::move(c)}; - return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; + template + void apply(const L&) const { + //.. } }; + } +} - struct glob_string { - operator std::string() const { - return "GLOB"; - } - }; +// #include "ast/where.h" - template - struct glob_t : condition_t, glob_string, negatable_t { - using self = glob_t; - using arg_t = A; - using pattern_t = T; +#include // std::false_type, std::true_type +#include // std::move - arg_t arg; - pattern_t pattern; +// #include "../functional/cxx_universal.h" - glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} - }; +// #include "../functional/cxx_type_traits_polyfill.h" - struct cross_join_string { - operator std::string() const { - return "CROSS JOIN"; - } - }; +// #include "../serialize_result_type.h" - /** - * CROSS JOIN holder. - * T is joined type which represents any mapped table. - */ - template - struct cross_join_t : cross_join_string { - using type = T; - }; +_EXPORT_SQLITE_ORM namespace sqlite_orm { + namespace internal { - struct natural_join_string { - operator std::string() const { - return "NATURAL JOIN"; + struct where_string { + serialize_result_type serialize() const { + return "WHERE"; } }; /** - * NATURAL JOIN holder. - * T is joined type which represents any mapped table. + * WHERE argument holder. + * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc + * Don't construct it manually. Call `where(...)` function instead. */ - template - struct natural_join_t : natural_join_string { - using type = T; - }; + template + struct where_t : where_string { + using expression_type = C; - struct left_join_string { - operator std::string() const { - return "LEFT JOIN"; - } + expression_type expression; + + where_t(expression_type expression_) : expression(std::move(expression_)) {} }; - /** - * LEFT JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. - */ - template - struct left_join_t : left_join_string { - using type = T; - using on_type = O; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of::value; - on_type constraint; + template + struct is_where : polyfill::bool_constant> {}; + } - left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} - }; + /** + * WHERE clause. Use it to add WHERE conditions wherever you like. + * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc + * @example + * // SELECT name + * // FROM letters + * // WHERE id > 3 + * auto rows = storage.select(&Letter::name, where(greater_than(&Letter::id, 3))); + */ + template + internal::where_t where(C expression) { + return {std::move(expression)}; + } +} - struct join_string { - operator std::string() const { - return "JOIN"; - } - }; +// #include "ast/group_by.h" - /** - * Simple JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. - */ - template - struct join_t : join_string { - using type = T; - using on_type = O; +#include // std::tuple, std::make_tuple +#include // std::true_type, std::false_type +#include // std::forward, std::move - on_type constraint; +// #include "../functional/cxx_type_traits_polyfill.h" - join_t(on_type constraint_) : constraint(std::move(constraint_)) {} - }; +_EXPORT_SQLITE_ORM namespace sqlite_orm { + namespace internal { - struct left_outer_join_string { - operator std::string() const { - return "LEFT OUTER JOIN"; - } + template + struct group_by_with_having { + using args_type = std::tuple; + using expression_type = T; + + args_type args; + expression_type expression; }; /** - * LEFT OUTER JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. + * GROUP BY pack holder. */ - template - struct left_outer_join_t : left_outer_join_string { - using type = T; - using on_type = O; + template + struct group_by_t { + using args_type = std::tuple; - on_type constraint; + args_type args; - left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} - }; - - struct on_string { - operator std::string() const { - return "ON"; + template + group_by_with_having having(T expression) { + return {std::move(this->args), std::move(expression)}; } }; - /** - * on(...) argument holder used for JOIN, LEFT JOIN, LEFT OUTER JOIN and INNER JOIN - * T is on type argument. - */ template - struct on_t : on_string { - using arg_type = T; + using is_group_by = polyfill::disjunction, + polyfill::is_specialization_of>; + } - arg_type arg; + /** + * GROUP BY column. + * Example: storage.get_all(group_by(&Employee::name)) + */ + template + internal::group_by_t group_by(Args... args) { + return {{std::forward(args)...}}; + } +} - on_t(arg_type arg_) : arg(std::move(arg_)) {} - }; +// #include "core_functions.h" - /** - * USING argument holder. - */ - template - struct using_t { - column_pointer column; +#include // std::string +#include // std::make_tuple, std::tuple_size +#include // std::forward, std::is_base_of, std::enable_if +#include // std::unique_ptr +#include // std::vector - operator std::string() const { - return "USING"; - } - }; +// #include "functional/cxx_type_traits_polyfill.h" - struct inner_join_string { - operator std::string() const { - return "INNER JOIN"; - } +// #include "functional/mpl/conditional.h" + +// #include "is_base_of_template.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "conditions.h" + +#include // std::string +#include // std::enable_if, std::is_same, std::remove_const +#include // std::vector +#include // std::tuple +#include // std::move, std::forward +#include // std::stringstream +#include // std::flush + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_base_of_template.h" + +// #include "type_traits.h" + +// #include "collate_argument.h" + +// #include "constraints.h" + +// #include "optional_container.h" + +// #include "serializer_context.h" + +namespace sqlite_orm { + + namespace internal { + + struct serializer_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; + bool fts5_columns = false; }; - /** - * INNER JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. - */ - template - struct inner_join_t : inner_join_string { - using type = T; - using on_type = O; + template + struct serializer_context : serializer_context_base { + using db_objects_type = DBOs; - on_type constraint; + const db_objects_type& db_objects; - inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} }; - struct cast_string { - operator std::string() const { - return "CAST"; + template + struct serializer_context_builder { + using storage_type = S; + using db_objects_type = typename storage_type::db_objects_type; + + serializer_context_builder(const storage_type& storage_) : storage{storage_} {} + + serializer_context operator()() const { + return {obtain_db_objects(this->storage)}; } + + const storage_type& storage; }; + } + +} + +// #include "serialize_result_type.h" + +// #include "tags.h" + +// #include "table_reference.h" + +// #include "alias_traits.h" + +// #include "expression.h" + +#include +#include // std::enable_if +#include // std::move, std::forward, std::declval +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" +// ::nullptr_t +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tags.h" + +// #include "operators.h" + +_EXPORT_SQLITE_ORM namespace sqlite_orm { + + namespace internal { + + template + struct in_t; + + template + struct and_condition_t; + + template + struct or_condition_t; /** - * CAST holder. - * T is a type to cast to - * E is an expression type - * Example: cast(&User::id) + * Result of c(...) function. Has operator= overloaded which returns assign_t */ - template - struct cast_t : cast_string { - using to_type = T; - using expression_type = E; + template + struct expression_t { + T value; - expression_type expression; + template + assign_t operator=(R r) const { + return {this->value, std::move(r)}; + } - cast_t(expression_type expression_) : expression(std::move(expression_)) {} - }; + assign_t operator=(nullptr_t) const { + return {this->value, nullptr}; + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + assign_t operator=(std::nullopt_t) const { + return {this->value, std::nullopt}; + } +#endif + template + in_t in(Args... args) const { + return {this->value, {std::forward(args)...}, false}; + } - template - struct from_t { - using tuple_type = std::tuple; + template + in_t not_in(Args... args) const { + return {this->value, {std::forward(args)...}, true}; + } + + template + and_condition_t and_(R right) const { + return {this->value, std::move(right)}; + } + + template + or_condition_t or_(R right) const { + return {this->value, std::move(right)}; + } }; template - using is_from = polyfill::is_specialization_of; + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = true; template - using is_constrained_join = polyfill::is_detected; - } + T get_from_expression(T value) { + return std::move(value); + } - /** - * Explicit FROM function. Usage: - * `storage.select(&User::id, from());` - */ - template - internal::from_t from() { - static_assert(sizeof...(Tables) > 0, ""); - return {}; + template + T get_from_expression(expression_t expression) { + return std::move(expression.value); + } + + template + using unwrap_expression_t = decltype(get_from_expression(std::declval())); } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Explicit FROM function. Usage: - * `storage.select(&User::id, from<"a"_alias.for_>());` + * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or + * `storage.update(set(c(&User::name) = "Dua Lipa")); */ - template - auto from() { - return from...>(); + template + internal::expression_t c(T value) { + return {std::move(value)}; } -#endif +} - // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace - // to facilitate ADL (Argument Dependent Lookup) - namespace internal { - template< - class T, - std::enable_if_t, is_operator_argument>::value, - bool> = true> - negated_condition_t operator!(T arg) { - return {std::move(arg)}; - } +// #include "column_pointer.h" - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - less_than_t, unwrap_expression_t> operator<(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } - - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - less_or_equal_t, unwrap_expression_t> operator<=(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } - - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - greater_than_t, unwrap_expression_t> operator>(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } +// #include "tags.h" - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - greater_or_equal_t, unwrap_expression_t> operator>=(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } +// #include "type_printer.h" - template, - std::is_base_of, - std::is_base_of, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - is_equal_t, unwrap_expression_t> operator==(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } +// #include "literal.h" - template, - std::is_base_of, - std::is_base_of, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - is_not_equal_t, unwrap_expression_t> operator!=(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } +_EXPORT_SQLITE_ORM namespace sqlite_orm { + namespace internal { - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - and_condition_t, unwrap_expression_t> operator&&(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + /* + * Protect an otherwise bindable element so that it is always serialized as a literal value. + */ + template + struct literal_holder { + using type = T; - template, std::is_base_of>::value, - bool> = true> - or_condition_t, unwrap_expression_t> operator||(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + type value; + }; - template< - class L, - class R, - std::enable_if_t, - std::is_base_of, - is_operator_argument, - is_operator_argument>, - // exclude conditions - polyfill::negation, - std::is_base_of>>>::value, - bool> = true> - conc_t, unwrap_expression_t> operator||(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } } +} - template - internal::using_t using_(F O::*field) { - return {field}; - } - template - internal::using_t using_(internal::column_pointer field) { - return {std::move(field)}; - } +_EXPORT_SQLITE_ORM namespace sqlite_orm { - template - internal::on_t on(T t) { - return {std::move(t)}; - } + namespace internal { - template - internal::cross_join_t cross_join() { - return {}; - } + struct limit_string { + operator std::string() const { + return "LIMIT"; + } + }; - template - internal::natural_join_t natural_join() { - return {}; - } + /** + * Stores LIMIT/OFFSET info + */ + template + struct limit_t : limit_string { + T lim; + optional_container off; - template - internal::left_join_t left_join(O o) { - return {std::move(o)}; - } + limit_t() = default; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto left_join(On on) { - return left_join, On>(std::move(on)); - } -#endif + limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} - template - internal::join_t join(O o) { - return {std::move(o)}; - } + limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto join(On on) { - return join, On>(std::move(on)); - } -#endif + template + struct is_limit : std::false_type {}; - template - internal::left_outer_join_t left_outer_join(O o) { - return {std::move(o)}; - } + template + struct is_limit> : std::true_type {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto left_outer_join(On on) { - return left_outer_join, On>(std::move(on)); - } -#endif + /** + * Stores OFFSET only info + */ + template + struct offset_t { + T off; + }; - template - internal::inner_join_t inner_join(O o) { - return {std::move(o)}; - } + template + using is_offset = polyfill::is_specialization_of; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto inner_join(On on) { - return inner_join, On>(std::move(on)); - } -#endif + /** + * Collated something + */ + template + struct collate_t : public condition_t { + T expr; + collate_argument argument; - template - internal::offset_t offset(T off) { - return {std::move(off)}; - } + collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} - template - internal::limit_t limit(T lim) { - return {std::move(lim)}; - } + operator std::string() const { + return collate_constraint_t{this->argument}; + } + }; - template = true> - internal::limit_t limit(O off, T lim) { - return {std::move(lim), {std::move(off)}}; - } + struct named_collate_base { + std::string name; - template - internal::limit_t limit(T lim, internal::offset_t offt) { - return {std::move(lim), {std::move(offt.off)}}; - } + operator std::string() const { + return "COLLATE " + this->name; + } + }; - template - auto and_(L l, R r) { - using namespace ::sqlite_orm::internal; - return and_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), - get_from_expression(std::forward(r))}; - } + /** + * Collated something with custom collate function + */ + template + struct named_collate : named_collate_base { + T expr; - template - auto or_(L l, R r) { - using namespace ::sqlite_orm::internal; - return or_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), - get_from_expression(std::forward(r))}; - } + named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} + }; - template - internal::is_not_null_t is_not_null(T t) { - return {std::move(t)}; - } - - template - internal::is_null_t is_null(T t) { - return {std::move(t)}; - } + struct negated_condition_string { + operator std::string() const { + return "NOT"; + } + }; - template - internal::dynamic_in_t> in(L l, std::vector values) { - return {std::move(l), std::move(values), false}; - } + /** + * Result of not operator + */ + template + struct negated_condition_t : condition_t, negated_condition_string { + C c; - template - internal::dynamic_in_t> in(L l, std::initializer_list values) { - return {std::move(l), std::move(values), false}; - } + negated_condition_t(C c_) : c(std::move(c_)) {} + }; - template - internal::dynamic_in_t in(L l, A arg) { - return {std::move(l), std::move(arg), false}; - } + /** + * Base class for binary conditions + * L is left argument type + * R is right argument type + * S is 'string' class (a class which has cast to `std::string` operator) + * Res is result type + */ + template + struct binary_condition : condition_t, S { + using left_type = L; + using right_type = R; + using result_type = Res; - template - internal::dynamic_in_t> not_in(L l, std::vector values) { - return {std::move(l), std::move(values), true}; - } + left_type lhs; + right_type rhs; - template - internal::dynamic_in_t> not_in(L l, std::initializer_list values) { - return {std::move(l), std::move(values), true}; - } + binary_condition() = default; - template - internal::dynamic_in_t not_in(L l, A arg) { - return {std::move(l), std::move(arg), true}; - } + binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} + }; - template - internal::is_equal_t is_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; - template - internal::is_equal_t eq(L l, R r) { - return {std::move(l), std::move(r)}; - } + template + struct is_binary_condition : polyfill::bool_constant> {}; - template - internal::is_equal_with_table_t is_equal(R rhs) { - return {std::move(rhs)}; - } + struct and_condition_string { + serialize_result_type serialize() const { + return "AND"; + } + }; - template - internal::is_not_equal_t is_not_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * Result of and operator + */ + template + struct and_condition_t : binary_condition { + using super = binary_condition; - template - internal::is_not_equal_t ne(L l, R r) { - return {std::move(l), std::move(r)}; - } + using super::super; + }; - template - internal::greater_than_t greater_than(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct or_condition_string { + serialize_result_type serialize() const { + return "OR"; + } + }; - template - internal::greater_than_t gt(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * Result of or operator + */ + template + struct or_condition_t : binary_condition { + using super = binary_condition; - template - internal::greater_or_equal_t greater_or_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + using super::super; + }; - template - internal::greater_or_equal_t ge(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct is_equal_string { + serialize_result_type serialize() const { + return "="; + } + }; - template - internal::less_than_t less_than(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * = and == operators object + */ + template + struct is_equal_t : binary_condition, negatable_t { + using self = is_equal_t; - /** - * [Deprecation notice] This function is deprecated and will be removed in v1.10. Use the accurately named function `less_than(...)` instead. - */ - template - [[deprecated("Use the accurately named function `less_than(...)` instead")]] internal::less_than_t - lesser_than(L l, R r) { - return {std::move(l), std::move(r)}; - } + using binary_condition::binary_condition; - template - internal::less_than_t lt(L l, R r) { - return {std::move(l), std::move(r)}; - } + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - template - internal::less_or_equal_t less_or_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } - /** - * [Deprecation notice] This function is deprecated and will be removed in v1.10. Use the accurately named function `less_or_equal(...)` instead. - */ - template - [[deprecated("Use the accurately named function `less_or_equal(...)` instead")]] internal::less_or_equal_t - lesser_or_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } - template - internal::less_or_equal_t le(L l, R r) { - return {std::move(l), std::move(r)}; - } + named_collate collate(std::string name) const { + return {*this, std::move(name)}; + } - /** - * ORDER BY column, column alias or expression - * - * Examples: - * storage.select(&User::name, order_by(&User::id)) - * storage.select(as(&User::name), order_by(get())) - */ - template> = true> - internal::order_by_t order_by(O o) { - return {std::move(o)}; - } + template + named_collate collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return {*this, ss.str()}; + } + }; - /** - * ORDER BY positional ordinal - * - * Examples: - * storage.select(&User::name, order_by(1)) - */ - template> = true> - internal::order_by_t> order_by(O o) { - return {{std::move(o)}}; - } + template + struct is_equal_with_table_t : negatable_t { + using left_type = L; + using right_type = R; - /** - * ORDER BY column1, column2 - * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) - */ - template - internal::multi_order_by_t multi_order_by(Args... args) { - return {{std::forward(args)...}}; - } + right_type rhs; - /** - * ORDER BY column1, column2 - * Difference from `multi_order_by` is that `dynamic_order_by` can be changed at runtime using `push_back` member - * function Example: - * auto orderBy = dynamic_order_by(storage); - * if(someCondition) { - * orderBy.push_back(&User::id); - * } else { - * orderBy.push_back(&User::name); - * orderBy.push_back(&User::birthDate); - * } - */ - template - internal::dynamic_order_by_t> - dynamic_order_by(const S& storage) { - internal::serializer_context_builder builder(storage); - return builder(); - } + is_equal_with_table_t(right_type rhs) : rhs(std::move(rhs)) {} + }; - /** - * X BETWEEN Y AND Z - * Example: storage.select(between(&User::id, 10, 20)) - */ - template - internal::between_t between(A expr, T b1, T b2) { - return {std::move(expr), std::move(b1), std::move(b2)}; - } + struct is_not_equal_string { + serialize_result_type serialize() const { + return "!="; + } + }; - /** - * X LIKE Y - * Example: storage.select(like(&User::name, "T%")) - */ - template - internal::like_t like(A a, T t) { - return {std::move(a), std::move(t), {}}; - } - - /** - * X GLOB Y - * Example: storage.select(glob(&User::name, "*S")) - */ - template - internal::glob_t glob(A a, T t) { - return {std::move(a), std::move(t)}; - } - - /** - * X LIKE Y ESCAPE Z - * Example: storage.select(like(&User::name, "T%", "%")) - */ - template - internal::like_t like(A a, T t, E e) { - return {std::move(a), std::move(t), {std::move(e)}}; - } - - /** - * CAST(X AS type). - * Example: cast(&User::id) - */ - template - internal::cast_t cast(E e) { - return {std::move(e)}; - } -} -#pragma once - -#include // std::enable_if, std::is_same, std::conditional -#include // std::make_index_sequence, std::move -#include // std::string -#include // std::stringstream -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include -#endif - -// #include "functional/cxx_type_traits_polyfill.h" + /** + * != operator object + */ + template + struct is_not_equal_t : binary_condition, negatable_t { + using self = is_not_equal_t; -// #include "functional/mpl/conditional.h" + using binary_condition::binary_condition; -// #include "functional/cstring_literal.h" + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include // std::index_sequence -#include // std::copy_n -#endif + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } -// #include "cxx_universal.h" -// ::size_t + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -namespace sqlite_orm::internal { - /* - * Wraps a C string of fixed size. - * Its main purpose is to enable the user-defined string literal operator template. - */ - template - struct cstring_literal { - static constexpr size_t size() { - return N - 1; - } + struct greater_than_string { + serialize_result_type serialize() const { + return ">"; + } + }; - constexpr cstring_literal(const char (&cstr)[N]) { - std::copy_n(cstr, N, this->cstr); - } + /** + * > operator object. + */ + template + struct greater_than_t : binary_condition, negatable_t { + using self = greater_than_t; - char cstr[N]; - }; + using binary_condition::binary_condition; - template class Template, cstring_literal literal, size_t... Idx> - consteval auto explode_into(std::index_sequence) { - return Template{}; - } -} -#endif + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } -// #include "type_traits.h" + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } -// #include "alias_traits.h" + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; -// #include "table_type_of.h" + struct greater_or_equal_string { + serialize_result_type serialize() const { + return ">="; + } + }; -// #include "tags.h" + /** + * >= operator object. + */ + template + struct greater_or_equal_t : binary_condition, negatable_t { + using self = greater_or_equal_t; -// #include "column_pointer.h" + using binary_condition::binary_condition; -namespace sqlite_orm { + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - namespace internal { -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - inline constexpr bool is_operator_argument_v>> = true; -#endif + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } - /** - * This is a common built-in class used for character based table aliases. - * For convenience there exist public type aliases `alias_a`, `alias_b`, ... - * The easiest way to create a table alias is using `"z"_alias.for_()`. - */ - template - struct recordset_alias : alias_tag { - using type = T; + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; - static std::string get() { - return {A, X...}; + struct less_than_string { + serialize_result_type serialize() const { + return "<"; } }; /** - * Column expression with table alias attached like 'C.ID'. This is not a column alias + * < operator object. */ - template - struct alias_column_t { - using alias_type = T; - using column_type = C; - - column_type column; - }; + template + struct less_than_t : binary_condition, negatable_t { + using self = less_than_t; - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_operator_argument_v::value>> = - true; + using binary_condition::binary_condition; - struct basic_table; + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - /* - * Encapsulates extracting the alias identifier of a non-alias. - * - * `extract()` always returns the empty string. - * `as_alias()` is used in contexts where a table might be aliased, and the empty string is returned. - * `as_qualifier()` is used in contexts where a table might be aliased, and the given table's name is returned. - */ - template - struct alias_extractor { - static std::string extract() { - return {}; + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; } - static std::string as_alias() { - return {}; + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; } + }; - template - static const std::string& as_qualifier(const X& table) { - return table.name; + struct less_or_equal_string { + serialize_result_type serialize() const { + return "<="; } }; - /* - * Encapsulates extracting the alias identifier of an alias. - * - * `extract()` always returns the alias identifier or CTE moniker. - * `as_alias()` is used in contexts where a recordset is aliased, and the alias identifier is returned. - * `as_qualifier()` is used in contexts where a table is aliased, and the alias identifier is returned. + /** + * <= operator object. */ - template - struct alias_extractor> { - static std::string extract() { - std::stringstream ss; - ss << A::get(); - return ss.str(); - } + template + struct less_or_equal_t : binary_condition, negatable_t { + using self = less_or_equal_t; - // for column and regular table aliases -> alias identifier - template, A> = true> - static std::string as_alias() { - return alias_extractor::extract(); + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, collate_argument::binary}; } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - // for CTE monikers -> empty - template, A> = true> - static std::string as_alias() { - return {}; + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; } -#endif - // for regular table aliases -> alias identifier - template = true> - static std::string as_qualifier(const basic_table&) { - return alias_extractor::extract(); + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; } }; + struct in_base { + bool negative = false; // used in not_in + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + in_base(bool negative) : negative{negative} {} +#endif + }; + /** - * Used to store alias for expression + * IN operator object. */ - template - struct as_t { - using alias_type = T; - using expression_type = E; + template + struct dynamic_in_t : condition_t, in_base, negatable_t { + using self = dynamic_in_t; - expression_type expression; - }; + L left; // left expression + A argument; // in arg - /** - * Built-in column alias. - * For convenience there exist type aliases `colalias_a`, `colalias_b`, ... - * The easiest way to create a column alias is using `"xyz"_col`. - */ - template - struct column_alias : alias_tag { - static std::string get() { - return {A, X...}; - } + dynamic_in_t(L left_, A argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} }; - template - struct alias_holder { - using type = T; + template + struct in_t : condition_t, in_base, negatable_t { + L left; + std::tuple argument; - alias_holder() = default; - // CTE feature needs it to implicitly convert a column alias to an alias_holder; see `cte()` factory function - alias_holder(const T&) noexcept {} + in_t(L left_, decltype(argument) argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + }; + + struct is_null_string { + operator std::string() const { + return "IS NULL"; + } }; + /** + * IS NULL operator object. + */ template - SQLITE_ORM_INLINE_VAR constexpr bool - is_operator_argument_v::value>> = true; + struct is_null_t : is_null_string, negatable_t { + using self = is_null_t; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - struct recordset_alias_builder { - template - [[nodiscard]] consteval recordset_alias for_() const { - return {}; - } + T t; - template - [[nodiscard]] consteval auto for_() const { - using T = std::remove_const_t; - return recordset_alias{}; - } + is_null_t(T t_) : t(std::move(t_)) {} }; -#endif -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template - SQLITE_ORM_CONSTEVAL auto n_to_colalias() { - constexpr column_alias<'1' + n % 10, C...> colalias{}; - if constexpr(n > 10) { - return n_to_colalias(); - } else { - return colalias; + struct is_not_null_string { + operator std::string() const { + return "IS NOT NULL"; } - } + }; + /** + * IS NOT NULL operator object. + */ template - inline constexpr bool is_builtin_numeric_column_alias_v = false; - template - inline constexpr bool is_builtin_numeric_column_alias_v> = ((C >= '0' && C <= '9') && ...); -#endif - } - - /** - * Using a column pointer, create a column reference to an aliased table column. - * - * Example: - * using als = alias_u; - * select(alias_column(column(&User::id))) - */ - template, - polyfill::negation>>>::value, - bool> = true> - constexpr auto alias_column(C field) { - using namespace ::sqlite_orm::internal; - using aliased_type = type_t; - static_assert(is_field_of_v, "Column must be from aliased table"); - - return alias_column_t{std::move(field)}; - } + struct is_not_null_t : is_not_null_string, negatable_t { + using self = is_not_null_t; - /** - * Using an object member field, create a column reference to an aliased table column. - * - * @note The object member pointer can be from a derived class without explicitly forming a column pointer. - * - * Example: - * using als = alias_u; - * select(alias_column(&User::id)) - */ - template, - polyfill::negation>>>::value, - bool> = true> - constexpr auto alias_column(F O::*field) { - using namespace ::sqlite_orm::internal; - using aliased_type = type_t; - static_assert(is_field_of_v, "Column must be from aliased table"); + T t; - using C1 = - mpl::conditional_t::value, F O::*, column_pointer>; - return alias_column_t{C1{field}}; - } + is_not_null_t(T t_) : t(std::move(t_)) {} + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Create a column reference to an aliased table column. - * - * @note An object member pointer can be from a derived class without explicitly forming a column pointer. - * - * Example: - * constexpr orm_table_alias auto als = "u"_alias.for_(); - * select(alias_column(&User::id)) - */ - template - requires(!orm_cte_moniker>) - constexpr auto alias_column(C field) { - using namespace ::sqlite_orm::internal; - using A = decltype(als); - using aliased_type = type_t; - static_assert(is_field_of_v, "Column must be from aliased table"); + struct order_by_base { + int asc_desc = 0; // 1: asc, -1: desc + std::string _collate_argument; - if constexpr(is_column_pointer_v) { - return alias_column_t{std::move(field)}; - } else if constexpr(std::is_same_v, aliased_type>) { - return alias_column_t{field}; - } else { - // wrap in column_pointer - using C1 = column_pointer; - return alias_column_t{{field}}; - } - } +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + order_by_base() = default; - /** - * Create a column reference to an aliased table column. - * - * @note An object member pointer can be from a derived class without explicitly forming a column pointer. - * - * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) - * because recordset aliases are derived from `sqlite_orm::alias_tag` - * - * Example: - * constexpr auto als = "u"_alias.for_(); - * select(als->*&User::id) - */ - template - requires(!orm_cte_moniker>) - constexpr auto operator->*(const A& /*tableAlias*/, F field) { - return alias_column(std::move(field)); - } + order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : + asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} #endif + }; -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - /** - * Create a column reference to an aliased CTE column. - */ - template, internal::is_cte_moniker>>, - bool> = true> - constexpr auto alias_column(C c) { - using namespace internal; - using cte_moniker_t = type_t; + struct order_by_string { + operator std::string() const { + return "ORDER BY"; + } + }; - if constexpr(is_column_pointer_v) { - static_assert(std::is_same, cte_moniker_t>::value, - "Column pointer must match aliased CTE"); - return alias_column_t{c}; - } else { - auto cp = column(c); - return alias_column_t{std::move(cp)}; - } - } + /** + * ORDER BY argument holder. + */ + template + struct order_by_t : order_by_base, order_by_string { + using expression_type = O; + using self = order_by_t; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Create a column reference to an aliased CTE column. - * - * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) - * because recordset aliases are derived from `sqlite_orm::alias_tag` - */ - template - requires(orm_cte_moniker>) - constexpr auto operator->*(const A& /*tableAlias*/, C c) { - return alias_column(std::move(c)); - } + expression_type expression; - /** - * Create a column reference to an aliased CTE column. - */ - template - requires(orm_cte_moniker>) - constexpr auto alias_column(C c) { - using A = std::remove_const_t; - return alias_column(std::move(c)); - } -#endif -#endif + order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} - /** - * Alias a column expression. - */ - template = true> - internal::as_t as(E expression) { - return {std::move(expression)}; - } + self asc() const { + auto res = *this; + res.asc_desc = 1; + return res; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Alias a column expression. - */ - template - auto as(E expression) { - return internal::as_t{std::move(expression)}; - } - - /** - * Alias a column expression. - */ - template - internal::as_t operator>>=(E expression, const A&) { - return {std::move(expression)}; - } -#else - /** - * Alias a column expression. - */ - template = true> - internal::as_t operator>>=(E expression, const A&) { - return {std::move(expression)}; - } -#endif - - /** - * Wrap a column alias in an alias holder. - */ - template - internal::alias_holder get() { - static_assert(internal::is_column_alias_v, ""); - return {}; - } - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto get() { - return internal::alias_holder{}; - } -#endif + self desc() const { + auto res = *this; + res.asc_desc = -1; + return res; + } - template - using alias_a = internal::recordset_alias; - template - using alias_b = internal::recordset_alias; - template - using alias_c = internal::recordset_alias; - template - using alias_d = internal::recordset_alias; - template - using alias_e = internal::recordset_alias; - template - using alias_f = internal::recordset_alias; - template - using alias_g = internal::recordset_alias; - template - using alias_h = internal::recordset_alias; - template - using alias_i = internal::recordset_alias; - template - using alias_j = internal::recordset_alias; - template - using alias_k = internal::recordset_alias; - template - using alias_l = internal::recordset_alias; - template - using alias_m = internal::recordset_alias; - template - using alias_n = internal::recordset_alias; - template - using alias_o = internal::recordset_alias; - template - using alias_p = internal::recordset_alias; - template - using alias_q = internal::recordset_alias; - template - using alias_r = internal::recordset_alias; - template - using alias_s = internal::recordset_alias; - template - using alias_t = internal::recordset_alias; - template - using alias_u = internal::recordset_alias; - template - using alias_v = internal::recordset_alias; - template - using alias_w = internal::recordset_alias; - template - using alias_x = internal::recordset_alias; - template - using alias_y = internal::recordset_alias; - template - using alias_z = internal::recordset_alias; + self collate_binary() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); + return res; + } - using colalias_a = internal::column_alias<'a'>; - using colalias_b = internal::column_alias<'b'>; - using colalias_c = internal::column_alias<'c'>; - using colalias_d = internal::column_alias<'d'>; - using colalias_e = internal::column_alias<'e'>; - using colalias_f = internal::column_alias<'f'>; - using colalias_g = internal::column_alias<'g'>; - using colalias_h = internal::column_alias<'h'>; - using colalias_i = internal::column_alias<'i'>; + self collate_nocase() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); + return res; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** @short Create a table alias. - * - * Examples: - * constexpr orm_table_alias auto z_alias = alias<'z'>.for_(); - */ - template - inline constexpr internal::recordset_alias_builder alias{}; + self collate_rtrim() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); + return res; + } - inline namespace literals { - /** @short Create a table alias. - * - * Examples: - * constexpr orm_table_alias auto z_alias = "z"_alias.for_(); - */ - template - [[nodiscard]] consteval auto operator"" _alias() { - return internal::explode_into( - std::make_index_sequence{}); - } + self collate(std::string name) const { + auto res = *this; + res._collate_argument = std::move(name); + return res; + } - /** @short Create a column alias. - * column_alias<'a'[, ...]> from a string literal. - * E.g. "a"_col, "b"_col - */ - template - [[nodiscard]] consteval auto operator"" _col() { - return internal::explode_into(std::make_index_sequence{}); - } - } -#endif + template + self collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return this->collate(ss.str()); + } + }; -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - inline namespace literals { /** - * column_alias<'1'[, ...]> from a numeric literal. - * E.g. 1_colalias, 2_colalias + * ORDER BY pack holder. */ - template - [[nodiscard]] SQLITE_ORM_CONSTEVAL auto operator"" _colalias() { - // numeric identifiers are used for automatically assigning implicit aliases to unaliased column expressions, - // which start at "1". - static_assert(std::array{Chars...}[0] > '0'); - return internal::column_alias{}; - } - } -#endif -} -#pragma once - -#include // std::string -#include // std::make_tuple, std::tuple_size -#include // std::forward, std::is_base_of, std::enable_if -#include // std::unique_ptr -#include // std::vector - -// #include "functional/cxx_type_traits_polyfill.h" - -// #include "functional/mpl/conditional.h" + template + struct multi_order_by_t : order_by_string { + using args_type = std::tuple; -// #include "is_base_of_template.h" + args_type args; -// #include "tuple_helper/tuple_traits.h" + multi_order_by_t(args_type args_) : args{std::move(args_)} {} + }; -// #include "conditions.h" + struct dynamic_order_by_entry_t : order_by_base { + std::string name; -// #include "serialize_result_type.h" + dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : + order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} + }; -// #include "operators.h" + /** + * C - serializer context class + */ + template + struct dynamic_order_by_t : order_by_string { + using context_t = C; + using entry_t = dynamic_order_by_entry_t; + using const_iterator = typename std::vector::const_iterator; -// #include "tags.h" + dynamic_order_by_t(const context_t& context_) : context(context_) {} -// #include "table_reference.h" + template + void push_back(order_by_t order_by) { + auto newContext = this->context; + newContext.skip_table_name = true; + auto columnName = serialize(order_by.expression, newContext); + this->entries.emplace_back(std::move(columnName), + order_by.asc_desc, + std::move(order_by._collate_argument)); + } -// #include "ast/into.h" + const_iterator begin() const { + return this->entries.begin(); + } -// #include "../functional/cxx_type_traits_polyfill.h" + const_iterator end() const { + return this->entries.end(); + } -namespace sqlite_orm { - namespace internal { + void clear() { + this->entries.clear(); + } - template - struct into_t { - using type = T; + protected: + std::vector entries; + context_t context; }; template - using is_into = polyfill::is_specialization_of; - } + SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = + polyfill::disjunction, + polyfill::is_specialization_of, + polyfill::is_specialization_of>::value; - template - internal::into_t into() { - return {}; - } -} + template + struct is_order_by : polyfill::bool_constant> {}; -namespace sqlite_orm { - - using int64 = sqlite_int64; - using uint64 = sqlite_uint64; - - namespace internal { - - template - struct unique_ptr_result_of {}; + struct between_string { + operator std::string() const { + return "BETWEEN"; + } + }; /** - * Base class for operator overloading - * R - return type - * S - class with operator std::string - * Args - function arguments types + * BETWEEN operator object. */ - template - struct built_in_function_t : S, arithmetic_t { - using return_type = R; - using string_type = S; - using args_type = std::tuple; - - static constexpr size_t args_size = std::tuple_size::value; + template + struct between_t : condition_t, between_string { + using expression_type = A; + using lower_type = T; + using upper_type = T; - args_type args; + expression_type expr; + lower_type b1; + upper_type b2; - built_in_function_t(args_type&& args_) : args(std::move(args_)) {} + between_t(expression_type expr_, lower_type b1_, upper_type b2_) : + expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} }; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = - is_base_of_template::value; - - template - struct is_built_in_function : polyfill::bool_constant> {}; - - template - struct filtered_aggregate_function { - using function_type = F; - using where_expression = W; - - function_type function; - where_expression where; + struct like_string { + operator std::string() const { + return "LIKE"; + } }; - template - struct where_t; + /** + * LIKE operator object. + */ + template + struct like_t : condition_t, like_string, negatable_t { + using self = like_t; + using arg_t = A; + using pattern_t = T; + using escape_t = E; - template - struct built_in_aggregate_function_t : built_in_function_t { - using super = built_in_function_t; + arg_t arg; + pattern_t pattern; + optional_container arg3; // not escape cause escape exists as a function here - using super::super; + like_t(arg_t arg_, pattern_t pattern_, optional_container escape_) : + arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} - template - filtered_aggregate_function, W> filter(where_t wh) { - return {*this, std::move(wh.expression)}; + template + like_t escape(C c) const { + optional_container newArg3{std::move(c)}; + return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; } }; - struct typeof_string { - serialize_result_type serialize() const { - return "TYPEOF"; + struct glob_string { + operator std::string() const { + return "GLOB"; } }; - struct unicode_string { - serialize_result_type serialize() const { - return "UNICODE"; - } - }; + template + struct glob_t : condition_t, glob_string, negatable_t { + using self = glob_t; + using arg_t = A; + using pattern_t = T; - struct length_string { - serialize_result_type serialize() const { - return "LENGTH"; - } - }; + arg_t arg; + pattern_t pattern; - struct abs_string { - serialize_result_type serialize() const { - return "ABS"; - } + glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} }; - struct lower_string { - serialize_result_type serialize() const { - return "LOWER"; + struct cross_join_string { + operator std::string() const { + return "CROSS JOIN"; } }; - struct upper_string { - serialize_result_type serialize() const { - return "UPPER"; - } + /** + * CROSS JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct cross_join_t : cross_join_string { + using type = T; }; - struct last_insert_rowid_string { - serialize_result_type serialize() const { - return "LAST_INSERT_ROWID"; + struct natural_join_string { + operator std::string() const { + return "NATURAL JOIN"; } }; - struct total_changes_string { - serialize_result_type serialize() const { - return "TOTAL_CHANGES"; - } + /** + * NATURAL JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct natural_join_t : natural_join_string { + using type = T; }; - struct changes_string { - serialize_result_type serialize() const { - return "CHANGES"; + struct left_join_string { + operator std::string() const { + return "LEFT JOIN"; } }; - struct trim_string { - serialize_result_type serialize() const { - return "TRIM"; - } - }; + /** + * LEFT JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct left_join_t : left_join_string { + using type = T; + using on_type = O; - struct ltrim_string { - serialize_result_type serialize() const { - return "LTRIM"; - } - }; + on_type constraint; - struct rtrim_string { - serialize_result_type serialize() const { - return "RTRIM"; - } + left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; - struct hex_string { - serialize_result_type serialize() const { - return "HEX"; + struct join_string { + operator std::string() const { + return "JOIN"; } }; - struct quote_string { - serialize_result_type serialize() const { - return "QUOTE"; - } - }; + /** + * Simple JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct join_t : join_string { + using type = T; + using on_type = O; - struct randomblob_string { - serialize_result_type serialize() const { - return "RANDOMBLOB"; - } - }; + on_type constraint; - struct instr_string { - serialize_result_type serialize() const { - return "INSTR"; - } + join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; - struct replace_string { - serialize_result_type serialize() const { - return "REPLACE"; + struct left_outer_join_string { + operator std::string() const { + return "LEFT OUTER JOIN"; } }; - struct round_string { - serialize_result_type serialize() const { - return "ROUND"; - } - }; + /** + * LEFT OUTER JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct left_outer_join_t : left_outer_join_string { + using type = T; + using on_type = O; -#if SQLITE_VERSION_NUMBER >= 3007016 - struct char_string { - serialize_result_type serialize() const { - return "CHAR"; - } + on_type constraint; + + left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; - struct random_string { - serialize_result_type serialize() const { - return "RANDOM"; + struct on_string { + operator std::string() const { + return "ON"; } }; -#endif + /** + * on(...) argument holder used for JOIN, LEFT JOIN, LEFT OUTER JOIN and INNER JOIN + * T is on type argument. + */ + template + struct on_t : on_string { + using arg_type = T; - struct coalesce_string { - serialize_result_type serialize() const { - return "COALESCE"; - } + arg_type arg; + + on_t(arg_type arg_) : arg(std::move(arg_)) {} }; - struct ifnull_string { - serialize_result_type serialize() const { - return "IFNULL"; - } - }; + /** + * USING argument holder. + */ + template + struct using_t { + column_pointer column; - struct nullif_string { - serialize_result_type serialize() const { - return "NULLIF"; + operator std::string() const { + return "USING"; } }; - struct date_string { - serialize_result_type serialize() const { - return "DATE"; + struct inner_join_string { + operator std::string() const { + return "INNER JOIN"; } }; - struct time_string { - serialize_result_type serialize() const { - return "TIME"; - } - }; + /** + * INNER JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct inner_join_t : inner_join_string { + using type = T; + using on_type = O; - struct datetime_string { - serialize_result_type serialize() const { - return "DATETIME"; - } - }; + on_type constraint; - struct julianday_string { - serialize_result_type serialize() const { - return "JULIANDAY"; - } + inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; - struct strftime_string { - serialize_result_type serialize() const { - return "STRFTIME"; + struct cast_string { + operator std::string() const { + return "CAST"; } }; - struct zeroblob_string { - serialize_result_type serialize() const { - return "ZEROBLOB"; - } - }; + /** + * CAST holder. + * T is a type to cast to + * E is an expression type + * Example: cast(&User::id) + */ + template + struct cast_t : cast_string { + using to_type = T; + using expression_type = E; - struct substr_string { - serialize_result_type serialize() const { - return "SUBSTR"; - } - }; -#ifdef SQLITE_SOUNDEX - struct soundex_string { - serialize_result_type serialize() const { - return "SOUNDEX"; - } - }; -#endif - struct total_string { - serialize_result_type serialize() const { - return "TOTAL"; - } - }; + expression_type expression; - struct sum_string { - serialize_result_type serialize() const { - return "SUM"; - } + cast_t(expression_type expression_) : expression(std::move(expression_)) {} }; - struct count_string { - serialize_result_type serialize() const { - return "COUNT"; - } + template + struct from_t { + using tuple_type = std::tuple; }; - /** - * T is use to specify type explicitly for queries like - * SELECT COUNT(*) FROM table_name; - * T can be omitted with void. - */ template - struct count_asterisk_t : count_string { - using type = T; + using is_from = polyfill::is_specialization_of; - template - filtered_aggregate_function, W> filter(where_t wh) { - return {*this, std::move(wh.expression)}; - } - }; + template + using is_constrained_join = polyfill::is_detected; + } - /** - * The same thing as count() but without T arg. - * Is used in cases like this: - * SELECT cust_code, cust_name, cust_city, grade - * FROM customer - * WHERE grade=2 AND EXISTS - * (SELECT COUNT(*) - * FROM customer - * WHERE grade=2 - * GROUP BY grade - * HAVING COUNT(*)>2); - * `c++` - * auto rows = - * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), - * where(is_equal(&Customer::grade, 2) - * and exists(select(count(), - * where(is_equal(&Customer::grade, 2)), - * group_by(&Customer::grade), - * having(greater_than(count(), 2)))))); - */ - struct count_asterisk_without_type : count_string {}; + /** + * Explicit FROM function. Usage: + * `storage.select(&User::id, from());` + */ + template + internal::from_t from() { + static_assert(sizeof...(Tables) > 0, ""); + return {}; + } - struct avg_string { - serialize_result_type serialize() const { - return "AVG"; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Explicit FROM function. Usage: + * `storage.select(&User::id, from<"a"_alias.for_>());` + */ + template + auto from() { + return from...>(); + } +#endif - struct max_string { - serialize_result_type serialize() const { - return "MAX"; - } - }; + // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace + // to facilitate ADL (Argument Dependent Lookup) + _EXPORT_SQLITE_ORM namespace internal { + template< + class T, + std::enable_if_t, is_operator_argument>::value, + bool> = true> + negated_condition_t operator!(T arg) { + return {std::move(arg)}; + } - struct min_string { - serialize_result_type serialize() const { - return "MIN"; - } - }; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + less_than_t, unwrap_expression_t> operator<(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct group_concat_string { - serialize_result_type serialize() const { - return "GROUP_CONCAT"; - } - }; -#ifdef SQLITE_ENABLE_MATH_FUNCTIONS - struct acos_string { - serialize_result_type serialize() const { - return "ACOS"; - } - }; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + less_or_equal_t, unwrap_expression_t> operator<=(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct acosh_string { - serialize_result_type serialize() const { - return "ACOSH"; - } - }; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + greater_than_t, unwrap_expression_t> operator>(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct asin_string { - serialize_result_type serialize() const { - return "ASIN"; - } - }; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + greater_or_equal_t, unwrap_expression_t> operator>=(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct asinh_string { - serialize_result_type serialize() const { - return "ASINH"; - } - }; + template, + std::is_base_of, + std::is_base_of, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + is_equal_t, unwrap_expression_t> operator==(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct atan_string { - serialize_result_type serialize() const { - return "ATAN"; - } - }; + template, + std::is_base_of, + std::is_base_of, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + is_not_equal_t, unwrap_expression_t> operator!=(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct atan2_string { - serialize_result_type serialize() const { - return "ATAN2"; - } - }; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + and_condition_t, unwrap_expression_t> operator&&(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct atanh_string { - serialize_result_type serialize() const { - return "ATANH"; - } - }; + template, std::is_base_of>::value, + bool> = true> + or_condition_t, unwrap_expression_t> operator||(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct ceil_string { - serialize_result_type serialize() const { - return "CEIL"; - } - }; + template< + class L, + class R, + std::enable_if_t, + std::is_base_of, + is_operator_argument, + is_operator_argument>, + // exclude conditions + polyfill::negation, + std::is_base_of>>>::value, + bool> = true> + conc_t, unwrap_expression_t> operator||(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + } - struct ceiling_string { - serialize_result_type serialize() const { - return "CEILING"; - } - }; + template + internal::using_t using_(F O::*field) { + return {field}; + } + template + internal::using_t using_(internal::column_pointer field) { + return {std::move(field)}; + } - struct cos_string { - serialize_result_type serialize() const { - return "COS"; - } - }; + template + internal::on_t on(T t) { + return {std::move(t)}; + } - struct cosh_string { - serialize_result_type serialize() const { - return "COSH"; - } - }; + template + internal::cross_join_t cross_join() { + return {}; + } - struct degrees_string { - serialize_result_type serialize() const { - return "DEGREES"; - } - }; + template + internal::natural_join_t natural_join() { + return {}; + } - struct exp_string { - serialize_result_type serialize() const { - return "EXP"; - } - }; + template + internal::left_join_t left_join(O o) { + return {std::move(o)}; + } - struct floor_string { - serialize_result_type serialize() const { - return "FLOOR"; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto left_join(On on) { + return left_join, On>(std::move(on)); + } +#endif - struct ln_string { - serialize_result_type serialize() const { - return "LN"; - } - }; + template + internal::join_t join(O o) { + return {std::move(o)}; + } - struct log_string { - serialize_result_type serialize() const { - return "LOG"; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto join(On on) { + return join, On>(std::move(on)); + } +#endif - struct log10_string { - serialize_result_type serialize() const { - return "LOG10"; - } - }; + template + internal::left_outer_join_t left_outer_join(O o) { + return {std::move(o)}; + } - struct log2_string { - serialize_result_type serialize() const { - return "LOG2"; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto left_outer_join(On on) { + return left_outer_join, On>(std::move(on)); + } +#endif - struct mod_string { - serialize_result_type serialize() const { - return "MOD"; - } - }; + template + internal::inner_join_t inner_join(O o) { + return {std::move(o)}; + } - struct pi_string { - serialize_result_type serialize() const { - return "PI"; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto inner_join(On on) { + return inner_join, On>(std::move(on)); + } +#endif - struct pow_string { - serialize_result_type serialize() const { - return "POW"; - } - }; + template + internal::offset_t offset(T off) { + return {std::move(off)}; + } - struct power_string { - serialize_result_type serialize() const { - return "POWER"; - } - }; + template + internal::limit_t limit(T lim) { + return {std::move(lim)}; + } - struct radians_string { - serialize_result_type serialize() const { - return "RADIANS"; - } - }; + template = true> + internal::limit_t limit(O off, T lim) { + return {std::move(lim), {std::move(off)}}; + } - struct sin_string { - serialize_result_type serialize() const { - return "SIN"; - } - }; + template + internal::limit_t limit(T lim, internal::offset_t offt) { + return {std::move(lim), {std::move(offt.off)}}; + } - struct sinh_string { - serialize_result_type serialize() const { - return "SINH"; - } - }; + template + auto and_(L l, R r) { + using namespace ::sqlite_orm::internal; + return and_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), + get_from_expression(std::forward(r))}; + } - struct sqrt_string { - serialize_result_type serialize() const { - return "SQRT"; - } - }; + template + auto or_(L l, R r) { + using namespace ::sqlite_orm::internal; + return or_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), + get_from_expression(std::forward(r))}; + } - struct tan_string { - serialize_result_type serialize() const { - return "TAN"; - } - }; + template + internal::is_not_null_t is_not_null(T t) { + return {std::move(t)}; + } - struct tanh_string { - serialize_result_type serialize() const { - return "TANH"; - } - }; - - struct trunc_string { - serialize_result_type serialize() const { - return "TRUNC"; - } - }; - -#endif // SQLITE_ENABLE_MATH_FUNCTIONS -#ifdef SQLITE_ENABLE_JSON1 - struct json_string { - serialize_result_type serialize() const { - return "JSON"; - } - }; + template + internal::is_null_t is_null(T t) { + return {std::move(t)}; + } - struct json_array_string { - serialize_result_type serialize() const { - return "JSON_ARRAY"; - } - }; + template + internal::dynamic_in_t> in(L l, std::vector values) { + return {std::move(l), std::move(values), false}; + } - struct json_array_length_string { - serialize_result_type serialize() const { - return "JSON_ARRAY_LENGTH"; - } - }; + template + internal::dynamic_in_t> in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), false}; + } - struct json_extract_string { - serialize_result_type serialize() const { - return "JSON_EXTRACT"; - } - }; + template + internal::dynamic_in_t in(L l, A arg) { + return {std::move(l), std::move(arg), false}; + } - struct json_insert_string { - serialize_result_type serialize() const { - return "JSON_INSERT"; - } - }; + template + internal::dynamic_in_t> not_in(L l, std::vector values) { + return {std::move(l), std::move(values), true}; + } - struct json_replace_string { - serialize_result_type serialize() const { - return "JSON_REPLACE"; - } - }; + template + internal::dynamic_in_t> not_in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), true}; + } - struct json_set_string { - serialize_result_type serialize() const { - return "JSON_SET"; - } - }; + template + internal::dynamic_in_t not_in(L l, A arg) { + return {std::move(l), std::move(arg), true}; + } - struct json_object_string { - serialize_result_type serialize() const { - return "JSON_OBJECT"; - } - }; + template + internal::is_equal_t is_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } - struct json_patch_string { - serialize_result_type serialize() const { - return "JSON_PATCH"; - } - }; + template + internal::is_equal_t eq(L l, R r) { + return {std::move(l), std::move(r)}; + } - struct json_remove_string { - serialize_result_type serialize() const { - return "JSON_REMOVE"; - } - }; + template + internal::is_equal_with_table_t is_equal(R rhs) { + return {std::move(rhs)}; + } - struct json_type_string { - serialize_result_type serialize() const { - return "JSON_TYPE"; - } - }; + template + internal::is_not_equal_t is_not_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } - struct json_valid_string { - serialize_result_type serialize() const { - return "JSON_VALID"; - } - }; + template + internal::is_not_equal_t ne(L l, R r) { + return {std::move(l), std::move(r)}; + } - struct json_quote_string { - serialize_result_type serialize() const { - return "JSON_QUOTE"; - } - }; + template + internal::greater_than_t greater_than(L l, R r) { + return {std::move(l), std::move(r)}; + } - struct json_group_array_string { - serialize_result_type serialize() const { - return "JSON_GROUP_ARRAY"; - } - }; + template + internal::greater_than_t gt(L l, R r) { + return {std::move(l), std::move(r)}; + } - struct json_group_object_string { - serialize_result_type serialize() const { - return "JSON_GROUP_OBJECT"; - } - }; -#endif // SQLITE_ENABLE_JSON1 + template + internal::greater_or_equal_t greater_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } - template - using field_type_or_type_t = polyfill::detected_or_t>; + template + internal::greater_or_equal_t ge(L l, R r) { + return {std::move(l), std::move(r)}; + } - template - struct highlight_t { - using table_type = T; - using argument0_type = X; - using argument1_type = Y; - using argument2_type = Z; + template + internal::less_than_t less_than(L l, R r) { + return {std::move(l), std::move(r)}; + } - argument0_type argument0; - argument1_type argument1; - argument2_type argument2; + /** + * [Deprecation notice] This function is deprecated and will be removed in v1.10. Use the accurately named function `less_than(...)` instead. + */ + template + [[deprecated("Use the accurately named function `less_than(...)` instead")]] internal::less_than_t + lesser_than(L l, R r) { + return {std::move(l), std::move(r)}; + } - highlight_t(argument0_type argument0, argument1_type argument1, argument2_type argument2) : - argument0(std::move(argument0)), argument1(std::move(argument1)), argument2(std::move(argument2)) {} - }; + template + internal::less_than_t lt(L l, R r) { + return {std::move(l), std::move(r)}; } -#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + template + internal::less_or_equal_t less_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } /** - * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos - * - * Example: - * - * auto rows = storage.select(sqlite_orm::acos(&Triangle::cornerA)); // decltype(rows) is std::vector + * [Deprecation notice] This function is deprecated and will be removed in v1.10. Use the accurately named function `less_or_equal(...)` instead. */ - template - internal::built_in_function_t acos(X x) { - return {std::tuple{std::forward(x)}}; + template + [[deprecated("Use the accurately named function `less_or_equal(...)` instead")]] internal::less_or_equal_t + lesser_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::less_or_equal_t le(L l, R r) { + return {std::move(l), std::move(r)}; } /** - * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::acos>(&Triangle::cornerA)); // decltype(rows) is std::vector> + * ORDER BY column, column alias or expression + * + * Examples: + * storage.select(&User::name, order_by(&User::id)) + * storage.select(as(&User::name), order_by(get())) */ - template - internal::built_in_function_t acos(X x) { - return {std::tuple{std::forward(x)}}; + template> = true> + internal::order_by_t order_by(O o) { + return {std::move(o)}; } /** - * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::acosh(&Triangle::cornerA)); // decltype(rows) is std::vector + * ORDER BY positional ordinal + * + * Examples: + * storage.select(&User::name, order_by(1)) */ - template - internal::built_in_function_t acosh(X x) { - return {std::tuple{std::forward(x)}}; + template> = true> + internal::order_by_t> order_by(O o) { + return {{std::move(o)}}; } /** - * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::acosh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + * ORDER BY column1, column2 + * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) */ - template - internal::built_in_function_t acosh(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::multi_order_by_t multi_order_by(Args... args) { + return {{std::forward(args)...}}; } /** - * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin - * - * Example: - * - * auto rows = storage.select(sqlite_orm::asin(&Triangle::cornerA)); // decltype(rows) is std::vector + * ORDER BY column1, column2 + * Difference from `multi_order_by` is that `dynamic_order_by` can be changed at runtime using `push_back` member + * function Example: + * auto orderBy = dynamic_order_by(storage); + * if(someCondition) { + * orderBy.push_back(&User::id); + * } else { + * orderBy.push_back(&User::name); + * orderBy.push_back(&User::birthDate); + * } */ - template - internal::built_in_function_t asin(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::dynamic_order_by_t> dynamic_order_by( + const S& storage) { + internal::serializer_context_builder builder(storage); + return builder(); } /** - * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::asin>(&Triangle::cornerA)); // decltype(rows) is std::vector> + * X BETWEEN Y AND Z + * Example: storage.select(between(&User::id, 10, 20)) */ - template - internal::built_in_function_t asin(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::between_t between(A expr, T b1, T b2) { + return {std::move(expr), std::move(b1), std::move(b2)}; } /** - * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::asinh(&Triangle::cornerA)); // decltype(rows) is std::vector + * X LIKE Y + * Example: storage.select(like(&User::name, "T%")) */ - template - internal::built_in_function_t asinh(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::like_t like(A a, T t) { + return {std::move(a), std::move(t), {}}; } /** - * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::asinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + * X GLOB Y + * Example: storage.select(glob(&User::name, "*S")) */ - template - internal::built_in_function_t asinh(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::glob_t glob(A a, T t) { + return {std::move(a), std::move(t)}; } /** - * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atan(1)); // decltype(rows) is std::vector + * X LIKE Y ESCAPE Z + * Example: storage.select(like(&User::name, "T%", "%")) */ - template - internal::built_in_function_t atan(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::like_t like(A a, T t, E e) { + return {std::move(a), std::move(t), {std::move(e)}}; } /** - * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atan>(1)); // decltype(rows) is std::vector> + * CAST(X AS type). + * Example: cast(&User::id) */ - template - internal::built_in_function_t atan(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::cast_t cast(E e) { + return {std::move(e)}; } +} - /** - * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atan2(1, 3)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t atan2(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } +// #include "serialize_result_type.h" - /** - * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atan2>(1, 3)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t atan2(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } +// #include "operators.h" - /** - * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atanh(1)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t atanh(X x) { - return {std::tuple{std::forward(x)}}; - } +// #include "tags.h" - /** - * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atanh>(1)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t atanh(X x) { - return {std::tuple{std::forward(x)}}; - } +// #include "table_reference.h" - /** - * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ceil(&User::rating)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t ceil(X x) { - return {std::tuple{std::forward(x)}}; - } +// #include "ast/into.h" - /** - * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ceil>(&User::rating)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t ceil(X x) { - return {std::tuple{std::forward(x)}}; - } +// #include "../functional/cxx_type_traits_polyfill.h" - /** - * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ceiling(&User::rating)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t ceiling(X x) { - return {std::tuple{std::forward(x)}}; - } +_EXPORT_SQLITE_ORM namespace sqlite_orm { + namespace internal { - /** - * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ceiling>(&User::rating)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t ceiling(X x) { - return {std::tuple{std::forward(x)}}; - } + template + struct into_t { + using type = T; + }; - /** - * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos - * - * Example: - * - * auto rows = storage.select(sqlite_orm::cos(&Triangle::cornerB)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t cos(X x) { - return {std::tuple{std::forward(x)}}; + template + using is_into = polyfill::is_specialization_of; } - /** - * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::cos>(&User::rating)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t cos(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::into_t into() { + return {}; } +} - /** - * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::cosh(&Triangle::cornerB)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t cosh(X x) { - return {std::tuple{std::forward(x)}}; - } +_EXPORT_SQLITE_ORM namespace sqlite_orm { - /** - * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::cosh>(&User::rating)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t cosh(X x) { - return {std::tuple{std::forward(x)}}; - } + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; - /** - * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees - * - * Example: - * - * auto rows = storage.select(sqlite_orm::degrees(&Triangle::cornerB)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t degrees(X x) { - return {std::tuple{std::forward(x)}}; - } + namespace internal { - /** - * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::degrees>(&User::rating)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t degrees(X x) { - return {std::tuple{std::forward(x)}}; - } + template + struct unique_ptr_result_of {}; - /** - * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp - * - * Example: - * - * auto rows = storage.select(sqlite_orm::exp(&Triangle::cornerB)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t exp(X x) { - return {std::tuple{std::forward(x)}}; - } + /** + * Base class for operator overloading + * R - return type + * S - class with operator std::string + * Args - function arguments types + */ + template + struct built_in_function_t : S, arithmetic_t { + using return_type = R; + using string_type = S; + using args_type = std::tuple; - /** - * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::exp>(&User::rating)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t exp(X x) { - return {std::tuple{std::forward(x)}}; - } + static constexpr size_t args_size = std::tuple_size::value; - /** - * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor - * - * Example: - * - * auto rows = storage.select(sqlite_orm::floor(&User::rating)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t floor(X x) { - return {std::tuple{std::forward(x)}}; - } + args_type args; - /** - * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::floor>(&User::rating)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t floor(X x) { - return {std::tuple{std::forward(x)}}; - } + built_in_function_t(args_type&& args_) : args(std::move(args_)) {} + }; - /** - * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ln(200)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t ln(X x) { - return {std::tuple{std::forward(x)}}; - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = + is_base_of_template::value; - /** - * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ln>(200)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t ln(X x) { - return {std::tuple{std::forward(x)}}; - } + template + struct is_built_in_function : polyfill::bool_constant> {}; - /** - * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log(100)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t log(X x) { - return {std::tuple{std::forward(x)}}; - } + template + struct filtered_aggregate_function { + using function_type = F; + using where_expression = W; - /** - * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log>(100)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t log(X x) { - return {std::tuple{std::forward(x)}}; - } + function_type function; + where_expression where; + }; - /** - * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log10(100)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t log10(X x) { - return {std::tuple{std::forward(x)}}; - } + template + struct where_t; - /** - * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log10>(100)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t log10(X x) { - return {std::tuple{std::forward(x)}}; - } + template + struct built_in_aggregate_function_t : built_in_function_t { + using super = built_in_function_t; - /** - * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log(10, 100)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t log(B b, X x) { - return {std::tuple{std::forward(b), std::forward(x)}}; - } + using super::super; - /** - * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log>(10, 100)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t log(B b, X x) { - return {std::tuple{std::forward(b), std::forward(x)}}; - } + template + filtered_aggregate_function, W> filter(where_t wh) { + return {*this, std::move(wh.expression)}; + } + }; - /** - * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log2(64)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t log2(X x) { - return {std::tuple{std::forward(x)}}; - } + struct typeof_string { + serialize_result_type serialize() const { + return "TYPEOF"; + } + }; - /** - * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log2>(64)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t log2(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod - * - * Example: - * - * auto rows = storage.select(sqlite_orm::mod_f(6, 5)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t mod_f(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::mod_f>(6, 5)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t mod_f(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * PI() function https://www.sqlite.org/lang_mathfunc.html#pi - * - * Example: - * - * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector - */ - inline internal::built_in_function_t pi() { - return {{}}; - } - - /** - * PI() function https://www.sqlite.org/lang_mathfunc.html#pi - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, etc. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t pi() { - return {{}}; - } - - /** - * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow - * - * Example: - * - * auto rows = storage.select(sqlite_orm::pow(2, 5)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t pow(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::pow>(2, 5)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t pow(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow - * - * Example: - * - * auto rows = storage.select(sqlite_orm::power(2, 5)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t power(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::power>(2, 5)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t power(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians - * - * Example: - * - * auto rows = storage.select(sqlite_orm::radians(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t radians(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::radians>(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t radians(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sin(&Triangle::cornerA)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t sin(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sin>(&Triangle::cornerA)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t sin(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sinh(&Triangle::cornerA)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t sinh(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t sinh(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t sqrt(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t sqrt(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan - * - * Example: - * - * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t tan(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t tan(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t tanh(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t tanh(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc - * - * Example: - * - * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t trunc(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t trunc(X x) { - return {std::tuple{std::forward(x)}}; - } -#endif // SQLITE_ENABLE_MATH_FUNCTIONS - /** - * TYPEOF(x) function https://sqlite.org/lang_corefunc.html#typeof - */ - template - internal::built_in_function_t typeof_(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * UNICODE(x) function https://sqlite.org/lang_corefunc.html#unicode - */ - template - internal::built_in_function_t unicode(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length - */ - template - internal::built_in_function_t length(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * ABS(x) function https://sqlite.org/lang_corefunc.html#abs - */ - template - internal::built_in_function_t, internal::abs_string, T> abs(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower - */ - template - internal::built_in_function_t lower(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper - */ - template - internal::built_in_function_t upper(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * LAST_INSERT_ROWID(x) function https://www.sqlite.org/lang_corefunc.html#last_insert_rowid - */ - inline internal::built_in_function_t last_insert_rowid() { - return {{}}; - } - - /** - * TOTAL_CHANGES() function https://sqlite.org/lang_corefunc.html#total_changes - */ - inline internal::built_in_function_t total_changes() { - return {{}}; - } - - /** - * CHANGES() function https://sqlite.org/lang_corefunc.html#changes - */ - inline internal::built_in_function_t changes() { - return {{}}; - } - - /** - * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim - */ - template - internal::built_in_function_t trim(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim - */ - template - internal::built_in_function_t trim(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim - */ - template - internal::built_in_function_t ltrim(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim - */ - template - internal::built_in_function_t ltrim(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim - */ - template - internal::built_in_function_t rtrim(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim - */ - template - internal::built_in_function_t rtrim(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * HEX(X) function https://sqlite.org/lang_corefunc.html#hex - */ - template - internal::built_in_function_t hex(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote - */ - template - internal::built_in_function_t quote(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob - */ - template - internal::built_in_function_t, internal::randomblob_string, X> randomblob(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr - */ - template - internal::built_in_function_t instr(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace - */ - template, internal::is_into>::value == 0, bool> = true> - internal::built_in_function_t replace(X x, Y y, Z z) { - return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; - } - - /** - * ROUND(X) function https://sqlite.org/lang_corefunc.html#round - */ - template - internal::built_in_function_t round(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round - */ - template - internal::built_in_function_t round(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - -#if SQLITE_VERSION_NUMBER >= 3007016 - /** - * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char - */ - template - internal::built_in_function_t char_(Args... args) { - return {std::make_tuple(std::forward(args)...)}; - } - - /** - * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random - */ - inline internal::built_in_function_t random() { - return {{}}; - } -#endif - - /** - * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce - */ - template - auto coalesce(Args... args) - -> internal::built_in_function_t::value, - std::common_type...>, - polyfill::type_identity>::type, - internal::coalesce_string, - Args...> { - return {std::make_tuple(std::forward(args)...)}; - } - - /** - * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull - */ - template - auto ifnull(X x, Y y) -> internal::built_in_function_t< - typename mpl::conditional_t< // choose R or common type - std::is_void::value, - std::common_type, internal::field_type_or_type_t>, - polyfill::type_identity>::type, - internal::ifnull_string, - X, - Y> { - return {std::make_tuple(std::move(x), std::move(y))}; - } - - /** - * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif - */ -#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) - /** - * NULLIF(X,Y) using common return type of X and Y - */ - template>, - polyfill::is_detected, - internal::field_type_or_type_t>>, - bool> = true> - auto nullif(X x, Y y) { - if constexpr(std::is_void_v) { - using F = internal::built_in_function_t< - std::optional, internal::field_type_or_type_t>>, - internal::nullif_string, - X, - Y>; - - return F{std::make_tuple(std::move(x), std::move(y))}; - } else { - using F = internal::built_in_function_t; - - return F{std::make_tuple(std::move(x), std::move(y))}; - } - } -#else - template - internal::built_in_function_t nullif(X x, Y y) { - return {std::make_tuple(std::move(x), std::move(y))}; - } -#endif - - /** - * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html - */ - template - internal::built_in_function_t date(Args... args) { - return {std::tuple{std::forward(args)...}}; - } - - /** - * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html - */ - template - internal::built_in_function_t time(Args... args) { - return {std::tuple{std::forward(args)...}}; - } - - /** - * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html - */ - template - internal::built_in_function_t datetime(Args... args) { - return {std::tuple{std::forward(args)...}}; - } - - /** - * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html - */ - template - internal::built_in_function_t julianday(Args... args) { - return {std::tuple{std::forward(args)...}}; - } - - /** - * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html - */ - template - internal::built_in_function_t strftime(Args... args) { - return {std::tuple{std::forward(args)...}}; - } - - /** - * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob - */ - template - internal::built_in_function_t, internal::zeroblob_string, N> zeroblob(N n) { - return {std::tuple{std::forward(n)}}; - } - - /** - * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr - */ - template - internal::built_in_function_t substr(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr - */ - template - internal::built_in_function_t substr(X x, Y y, Z z) { - return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; - } - -#ifdef SQLITE_SOUNDEX - /** - * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex - */ - template - internal::built_in_function_t soundex(X x) { - return {std::tuple{std::forward(x)}}; - } -#endif - - /** - * TOTAL(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t total(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SUM(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t, internal::sum_string, X> sum(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * COUNT(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t count(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * COUNT(*) without FROM function. - */ - inline internal::count_asterisk_without_type count() { - return {}; - } - - /** - * COUNT(*) with FROM function. Specified type T will be serialized as - * a from argument. - */ - template - internal::count_asterisk_t count() { - return {}; - } - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * COUNT(*) with FROM function. Specified recordset will be serialized as - * a from argument. - */ - template - auto count() { - return count>(); - } -#endif - - /** - * AVG(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t avg(X x) { - return {std::tuple{std::forward(x)}}; - } + struct unicode_string { + serialize_result_type serialize() const { + return "UNICODE"; + } + }; - /** - * MAX(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t, internal::max_string, X> max(X x) { - return {std::tuple{std::forward(x)}}; - } + struct length_string { + serialize_result_type serialize() const { + return "LENGTH"; + } + }; - /** - * MIN(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t, internal::min_string, X> min(X x) { - return {std::tuple{std::forward(x)}}; - } + struct abs_string { + serialize_result_type serialize() const { + return "ABS"; + } + }; - /** - * MAX(X, Y, ...) scalar function. - * The return type is the type of the first argument. - */ - template - internal::built_in_function_t, internal::max_string, X, Y, Rest...> - max(X x, Y y, Rest... rest) { - return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; - } + struct lower_string { + serialize_result_type serialize() const { + return "LOWER"; + } + }; - /** - * MIN(X, Y, ...) scalar function. - * The return type is the type of the first argument. - */ - template - internal::built_in_function_t, internal::min_string, X, Y, Rest...> - min(X x, Y y, Rest... rest) { - return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; - } + struct upper_string { + serialize_result_type serialize() const { + return "UPPER"; + } + }; - /** - * GROUP_CONCAT(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t group_concat(X x) { - return {std::tuple{std::forward(x)}}; - } + struct last_insert_rowid_string { + serialize_result_type serialize() const { + return "LAST_INSERT_ROWID"; + } + }; - /** - * GROUP_CONCAT(X, Y) aggregate function. - */ - template - internal::built_in_aggregate_function_t group_concat(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } -#ifdef SQLITE_ENABLE_JSON1 - template - internal::built_in_function_t json(X x) { - return {std::tuple{std::forward(x)}}; - } + struct total_changes_string { + serialize_result_type serialize() const { + return "TOTAL_CHANGES"; + } + }; - template - internal::built_in_function_t json_array(Args... args) { - return {std::tuple{std::forward(args)...}}; - } + struct changes_string { + serialize_result_type serialize() const { + return "CHANGES"; + } + }; - template - internal::built_in_function_t json_array_length(X x) { - return {std::tuple{std::forward(x)}}; - } + struct trim_string { + serialize_result_type serialize() const { + return "TRIM"; + } + }; - template - internal::built_in_function_t json_array_length(X x) { - return {std::tuple{std::forward(x)}}; - } + struct ltrim_string { + serialize_result_type serialize() const { + return "LTRIM"; + } + }; - template - internal::built_in_function_t json_array_length(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + struct rtrim_string { + serialize_result_type serialize() const { + return "RTRIM"; + } + }; - template - internal::built_in_function_t json_array_length(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + struct hex_string { + serialize_result_type serialize() const { + return "HEX"; + } + }; - template - internal::built_in_function_t json_extract(X x, Args... args) { - return {std::tuple{std::forward(x), std::forward(args)...}}; - } + struct quote_string { + serialize_result_type serialize() const { + return "QUOTE"; + } + }; - template - internal::built_in_function_t json_insert(X x, - Args... args) { - static_assert(std::tuple_size>::value % 2 == 0, - "number of arguments in json_insert must be odd"); - return {std::tuple{std::forward(x), std::forward(args)...}}; - } + struct randomblob_string { + serialize_result_type serialize() const { + return "RANDOMBLOB"; + } + }; - template - internal::built_in_function_t json_replace(X x, - Args... args) { - static_assert(std::tuple_size>::value % 2 == 0, - "number of arguments in json_replace must be odd"); - return {std::tuple{std::forward(x), std::forward(args)...}}; - } + struct instr_string { + serialize_result_type serialize() const { + return "INSTR"; + } + }; - template - internal::built_in_function_t json_set(X x, Args... args) { - static_assert(std::tuple_size>::value % 2 == 0, - "number of arguments in json_set must be odd"); - return {std::tuple{std::forward(x), std::forward(args)...}}; - } + struct replace_string { + serialize_result_type serialize() const { + return "REPLACE"; + } + }; - template - internal::built_in_function_t json_object(Args... args) { - static_assert(std::tuple_size>::value % 2 == 0, - "number of arguments in json_object must be even"); - return {std::tuple{std::forward(args)...}}; - } + struct round_string { + serialize_result_type serialize() const { + return "ROUND"; + } + }; - template - internal::built_in_function_t json_patch(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } +#if SQLITE_VERSION_NUMBER >= 3007016 + struct char_string { + serialize_result_type serialize() const { + return "CHAR"; + } + }; - template - internal::built_in_function_t json_remove(X x, - Args... args) { - return {std::tuple{std::forward(x), std::forward(args)...}}; - } + struct random_string { + serialize_result_type serialize() const { + return "RANDOM"; + } + }; - template - internal::built_in_function_t json_remove(X x, Args... args) { - return {std::tuple{std::forward(x), std::forward(args)...}}; - } +#endif - template - internal::built_in_function_t json_type(X x) { - return {std::tuple{std::forward(x)}}; - } + struct coalesce_string { + serialize_result_type serialize() const { + return "COALESCE"; + } + }; - template - internal::built_in_function_t json_type(X x) { - return {std::tuple{std::forward(x)}}; - } + struct ifnull_string { + serialize_result_type serialize() const { + return "IFNULL"; + } + }; - template - internal::built_in_function_t json_type(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + struct nullif_string { + serialize_result_type serialize() const { + return "NULLIF"; + } + }; - template - internal::built_in_function_t json_type(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + struct date_string { + serialize_result_type serialize() const { + return "DATE"; + } + }; - template - internal::built_in_function_t json_valid(X x) { - return {std::tuple{std::forward(x)}}; - } + struct time_string { + serialize_result_type serialize() const { + return "TIME"; + } + }; - template - internal::built_in_function_t json_quote(X x) { - return {std::tuple{std::forward(x)}}; - } + struct datetime_string { + serialize_result_type serialize() const { + return "DATETIME"; + } + }; - template - internal::built_in_function_t json_group_array(X x) { - return {std::tuple{std::forward(x)}}; - } + struct julianday_string { + serialize_result_type serialize() const { + return "JULIANDAY"; + } + }; - template - internal::built_in_function_t json_group_object(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } -#endif // SQLITE_ENABLE_JSON1 + struct strftime_string { + serialize_result_type serialize() const { + return "STRFTIME"; + } + }; + + struct zeroblob_string { + serialize_result_type serialize() const { + return "ZEROBLOB"; + } + }; - // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace - // to facilitate ADL (Argument Dependent Lookup) - namespace internal { - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - add_t, unwrap_expression_t> operator+(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + struct substr_string { + serialize_result_type serialize() const { + return "SUBSTR"; + } + }; +#ifdef SQLITE_SOUNDEX + struct soundex_string { + serialize_result_type serialize() const { + return "SOUNDEX"; + } + }; +#endif + struct total_string { + serialize_result_type serialize() const { + return "TOTAL"; + } + }; - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - sub_t, unwrap_expression_t> operator-(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + struct sum_string { + serialize_result_type serialize() const { + return "SUM"; + } + }; - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - mul_t, unwrap_expression_t> operator*(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + struct count_string { + serialize_result_type serialize() const { + return "COUNT"; + } + }; - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - div_t, unwrap_expression_t> operator/(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + /** + * T is use to specify type explicitly for queries like + * SELECT COUNT(*) FROM table_name; + * T can be omitted with void. + */ + template + struct count_asterisk_t : count_string { + using type = T; - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - mod_t, unwrap_expression_t> operator%(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } - } + template + filtered_aggregate_function, W> filter(where_t wh) { + return {*this, std::move(wh.expression)}; + } + }; - template - internal::highlight_t highlight(X x, Y y, Z z) { - return {std::move(x), std::move(y), std::move(z)}; - } -} -#pragma once + /** + * The same thing as count() but without T arg. + * Is used in cases like this: + * SELECT cust_code, cust_name, cust_city, grade + * FROM customer + * WHERE grade=2 AND EXISTS + * (SELECT COUNT(*) + * FROM customer + * WHERE grade=2 + * GROUP BY grade + * HAVING COUNT(*)>2); + * `c++` + * auto rows = + * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), + * where(is_equal(&Customer::grade, 2) + * and exists(select(count(), + * where(is_equal(&Customer::grade, 2)), + * group_by(&Customer::grade), + * having(greater_than(count(), 2)))))); + */ + struct count_asterisk_without_type : count_string {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include -#endif -#include // std::remove_const -#include // std::string -#include // std::move -#include // std::tuple, std::get, std::tuple_size -// #include "functional/cxx_optional.h" + struct avg_string { + serialize_result_type serialize() const { + return "AVG"; + } + }; -// #include "functional/cxx_universal.h" -// ::size_t -// #include "functional/cxx_type_traits_polyfill.h" + struct max_string { + serialize_result_type serialize() const { + return "MAX"; + } + }; -// #include "is_base_of_template.h" + struct min_string { + serialize_result_type serialize() const { + return "MIN"; + } + }; -// #include "tuple_helper/tuple_traits.h" + struct group_concat_string { + serialize_result_type serialize() const { + return "GROUP_CONCAT"; + } + }; +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + struct acos_string { + serialize_result_type serialize() const { + return "ACOS"; + } + }; -// #include "tuple_helper/tuple_transformer.h" + struct acosh_string { + serialize_result_type serialize() const { + return "ACOSH"; + } + }; -#include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval -#include // std::tuple_size, std::get + struct asin_string { + serialize_result_type serialize() const { + return "ASIN"; + } + }; -// #include "../functional/cxx_universal.h" -// ::size_t -// #include "../functional/cxx_type_traits_polyfill.h" + struct asinh_string { + serialize_result_type serialize() const { + return "ASINH"; + } + }; -// #include "../functional/cxx_functional_polyfill.h" + struct atan_string { + serialize_result_type serialize() const { + return "ATAN"; + } + }; -// #include "../functional/mpl.h" + struct atan2_string { + serialize_result_type serialize() const { + return "ATAN2"; + } + }; -namespace sqlite_orm { - namespace internal { + struct atanh_string { + serialize_result_type serialize() const { + return "ATANH"; + } + }; - template class Op> - struct tuple_transformer; + struct ceil_string { + serialize_result_type serialize() const { + return "CEIL"; + } + }; - template class Pack, class... Types, template class Op> - struct tuple_transformer, Op> { - using type = Pack...>; + struct ceiling_string { + serialize_result_type serialize() const { + return "CEILING"; + } }; - /* - * Transform specified tuple. - * - * `Op` is a metafunction. - */ - template class Op> - using transform_tuple_t = typename tuple_transformer::type; + struct cos_string { + serialize_result_type serialize() const { + return "COS"; + } + }; - // note: applying a combiner like `plus_fold_integrals` needs fold expressions -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) - /* - * Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results. - * - * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. - * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). - * - * @note `project` is called using `std::invoke`, which is `constexpr` since C++20. - */ - template - SQLITE_ORM_CONSTEXPR_CPP20 auto recombine_tuple(CombineOp combine, - const Tpl& tpl, - std::index_sequence, - Projector project, - Init initial) { - return combine(initial, polyfill::invoke(project, std::get(tpl))...); - } + struct cosh_string { + serialize_result_type serialize() const { + return "COSH"; + } + }; + + struct degrees_string { + serialize_result_type serialize() const { + return "DEGREES"; + } + }; - /* - * Apply a projection to a tuple's elements, and combine the results. - * - * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. - * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). - * - * @note `project` is called using `std::invoke`, which is `constexpr` since C++20. - */ - template - SQLITE_ORM_CONSTEXPR_CPP20 auto - recombine_tuple(CombineOp combine, const Tpl& tpl, Projector project, Init initial) { - return recombine_tuple(std::move(combine), - std::forward(tpl), - std::make_index_sequence::value>{}, - std::move(project), - std::move(initial)); - } + struct exp_string { + serialize_result_type serialize() const { + return "EXP"; + } + }; - /* - * Function object that takes integral constants and returns the sum of their values as an integral constant. - * Because it's a "transparent" functor, it must be called with at least one argument, otherwise it cannot deduce the integral constant type. - */ - struct plus_fold_integrals { - template - constexpr auto operator()(const Integrals&...) const { - using integral_type = std::common_type_t; - return std::integral_constant{}; + struct floor_string { + serialize_result_type serialize() const { + return "FLOOR"; } }; - /* - * Function object that takes a type, applies a projection on it, and returns the tuple size of the projected type (as an integral constant). - * The projection is applied on the argument type, not the argument value/object. - */ - template class NestedProject> - struct project_nested_tuple_size { - template - constexpr auto operator()(const T&) const { - return typename std::tuple_size>::type{}; + struct ln_string { + serialize_result_type serialize() const { + return "LN"; } }; - template class NestedProject, class Tpl, class IdxSeq> - using nested_tuple_size_for_t = decltype(recombine_tuple(plus_fold_integrals{}, - std::declval(), - IdxSeq{}, - project_nested_tuple_size{}, - std::integral_constant{})); -#endif + struct log_string { + serialize_result_type serialize() const { + return "LOG"; + } + }; - template - constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { - return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; - } + struct log10_string { + serialize_result_type serialize() const { + return "LOG10"; + } + }; - /* - * Like `std::make_from_tuple`, but using a projection on the tuple elements. - */ - template - constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) { - return create_from_tuple( - std::forward(tpl), - std::make_index_sequence>::value>{}, - std::forward(project)); - } - } -} + struct log2_string { + serialize_result_type serialize() const { + return "LOG2"; + } + }; -// #include "tuple_helper/tuple_iteration.h" + struct mod_string { + serialize_result_type serialize() const { + return "MOD"; + } + }; -#include // std::get, std::tuple_element, std::tuple_size -#include // std::index_sequence, std::make_index_sequence -#include // std::forward, std::move + struct pi_string { + serialize_result_type serialize() const { + return "PI"; + } + }; -// #include "../functional/cxx_universal.h" -// ::size_t + struct pow_string { + serialize_result_type serialize() const { + return "POW"; + } + }; -namespace sqlite_orm { - namespace internal { -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) - template - void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { - if constexpr(reversed) { - // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= - int sink; - // note: `(void)` cast silences warning 'expression result unused' - (void)((lambda(std::get(tpl)), sink) = ... = 0); - } else { - (lambda(std::get(tpl)), ...); + struct power_string { + serialize_result_type serialize() const { + return "POWER"; } - } -#else - template - void iterate_tuple(Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} + }; - template - void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { - if SQLITE_ORM_CONSTEXPR_IF(reversed) { - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - lambda(std::get(tpl)); - } else { - lambda(std::get(tpl)); - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + struct radians_string { + serialize_result_type serialize() const { + return "RADIANS"; } - } -#endif - template - void iterate_tuple(Tpl&& tpl, L&& lambda) { - iterate_tuple(tpl, - std::make_index_sequence>::value>{}, - std::forward(lambda)); - } + }; -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED - template - void iterate_tuple(std::index_sequence, L&& lambda) { - (lambda((std::tuple_element_t*)nullptr), ...); - } -#else - template - void iterate_tuple(std::index_sequence, L&& lambda) { - using Sink = int[sizeof...(Idx)]; - (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; - } -#endif - template - void iterate_tuple(L&& lambda) { - iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); - } + struct sin_string { + serialize_result_type serialize() const { + return "SIN"; + } + }; - template class Base, class L> - struct lambda_as_template_base : L { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} -#endif - template - decltype(auto) operator()(const Base& object) { - return L::operator()(object); + struct sinh_string { + serialize_result_type serialize() const { + return "SINH"; } }; - /* - * This method wraps the specified callable in another function object, - * which in turn implicitly casts its single argument to the specified template base class, - * then passes the converted argument to the lambda. - * - * Note: This method is useful for reducing combinatorial instantiation of template lambdas, - * as long as this library supports compilers that do not implement - * explicit template parameters in generic lambdas [SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED]. - * Unfortunately it doesn't work with user-defined conversion operators in order to extract - * parts of a class. In other words, the destination type must be a direct template base class. - */ - template class Base, class L> - lambda_as_template_base call_as_template_base(L lambda) { - return {std::move(lambda)}; - } - } -} + struct sqrt_string { + serialize_result_type serialize() const { + return "SQRT"; + } + }; -// #include "optional_container.h" + struct tan_string { + serialize_result_type serialize() const { + return "TAN"; + } + }; -// #include "ast/where.h" + struct tanh_string { + serialize_result_type serialize() const { + return "TANH"; + } + }; -#include // std::false_type, std::true_type -#include // std::move + struct trunc_string { + serialize_result_type serialize() const { + return "TRUNC"; + } + }; -// #include "../functional/cxx_universal.h" +#endif // SQLITE_ENABLE_MATH_FUNCTIONS +#ifdef SQLITE_ENABLE_JSON1 + struct json_string { + serialize_result_type serialize() const { + return "JSON"; + } + }; -// #include "../functional/cxx_type_traits_polyfill.h" + struct json_array_string { + serialize_result_type serialize() const { + return "JSON_ARRAY"; + } + }; -// #include "../serialize_result_type.h" + struct json_array_length_string { + serialize_result_type serialize() const { + return "JSON_ARRAY_LENGTH"; + } + }; -namespace sqlite_orm { - namespace internal { + struct json_extract_string { + serialize_result_type serialize() const { + return "JSON_EXTRACT"; + } + }; + + struct json_insert_string { + serialize_result_type serialize() const { + return "JSON_INSERT"; + } + }; + + struct json_replace_string { + serialize_result_type serialize() const { + return "JSON_REPLACE"; + } + }; - struct where_string { + struct json_set_string { serialize_result_type serialize() const { - return "WHERE"; + return "JSON_SET"; } }; - /** - * WHERE argument holder. - * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc - * Don't construct it manually. Call `where(...)` function instead. - */ - template - struct where_t : where_string { - using expression_type = C; + struct json_object_string { + serialize_result_type serialize() const { + return "JSON_OBJECT"; + } + }; - expression_type expression; + struct json_patch_string { + serialize_result_type serialize() const { + return "JSON_PATCH"; + } + }; - where_t(expression_type expression_) : expression(std::move(expression_)) {} + struct json_remove_string { + serialize_result_type serialize() const { + return "JSON_REMOVE"; + } }; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of::value; + struct json_type_string { + serialize_result_type serialize() const { + return "JSON_TYPE"; + } + }; - template - struct is_where : polyfill::bool_constant> {}; - } + struct json_valid_string { + serialize_result_type serialize() const { + return "JSON_VALID"; + } + }; - /** - * WHERE clause. Use it to add WHERE conditions wherever you like. - * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc - * @example - * // SELECT name - * // FROM letters - * // WHERE id > 3 - * auto rows = storage.select(&Letter::name, where(greater_than(&Letter::id, 3))); - */ - template - internal::where_t where(C expression) { - return {std::move(expression)}; - } -} + struct json_quote_string { + serialize_result_type serialize() const { + return "JSON_QUOTE"; + } + }; -// #include "ast/group_by.h" + struct json_group_array_string { + serialize_result_type serialize() const { + return "JSON_GROUP_ARRAY"; + } + }; -#include // std::tuple, std::make_tuple -#include // std::true_type, std::false_type -#include // std::forward, std::move + struct json_group_object_string { + serialize_result_type serialize() const { + return "JSON_GROUP_OBJECT"; + } + }; +#endif // SQLITE_ENABLE_JSON1 -// #include "../functional/cxx_type_traits_polyfill.h" + template + using field_type_or_type_t = polyfill::detected_or_t>; -namespace sqlite_orm { - namespace internal { + template + struct highlight_t { + using table_type = T; + using argument0_type = X; + using argument1_type = Y; + using argument2_type = Z; - template - struct group_by_with_having { - using args_type = std::tuple; - using expression_type = T; + argument0_type argument0; + argument1_type argument1; + argument2_type argument2; - args_type args; - expression_type expression; + highlight_t(argument0_type argument0, argument1_type argument1, argument2_type argument2) : + argument0(std::move(argument0)), argument1(std::move(argument1)), argument2(std::move(argument2)) {} }; + } - /** - * GROUP BY pack holder. - */ - template - struct group_by_t { - using args_type = std::tuple; - - args_type args; - - template - group_by_with_having having(T expression) { - return {std::move(this->args), std::move(expression)}; - } - }; +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS - template - using is_group_by = polyfill::disjunction, - polyfill::is_specialization_of>; + /** + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t acos(X x) { + return {std::tuple{std::forward(x)}}; } /** - * GROUP BY column. - * Example: storage.get_all(group_by(&Employee::name)) + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos>(&Triangle::cornerA)); // decltype(rows) is std::vector> */ - template - internal::group_by_t group_by(Args... args) { - return {{std::forward(args)...}}; + template + internal::built_in_function_t acos(X x) { + return {std::tuple{std::forward(x)}}; } -} - -// #include "core_functions.h" - -// #include "alias_traits.h" - -// #include "cte_moniker.h" - -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include -#include // std::make_index_sequence -#endif -#include // std::enable_if, std::is_member_pointer, std::is_same, std::is_convertible -#include // std::ignore -#include -#endif - -// #include "functional/cxx_universal.h" - -// #include "functional/cstring_literal.h" - -// #include "alias.h" -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -namespace sqlite_orm { + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t acosh(X x) { + return {std::tuple{std::forward(x)}}; + } - namespace internal { - /** - * A special record set alias that is both, a storage lookup type (mapping type) and an alias. - */ - template - struct cte_moniker - : recordset_alias< - cte_moniker /* refer to self, since a moniker is both, an alias and a mapped type */, - A, - X...> { - /** - * Introduce the construction of a common table expression using this moniker. - * - * The list of explicit columns is optional; - * if provided the number of columns must match the number of columns of the subselect. - * The column names will be merged with the subselect: - * 1. column names of subselect - * 2. explicit columns - * 3. fill in empty column names with column index - * - * Example: - * 1_ctealias()(select(&Object::id)); - * 1_ctealias(&Object::name)(select("object")); - * - * @return A `cte_builder` instance. - * @note (internal): Defined in select_constraints.h in order to keep this member function in the same place as the named factory function `cte()`, - * and to keep the actual creation of the builder in one place. - */ -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - requires((is_column_alias_v || std::is_member_pointer_v || - std::same_as> || - std::convertible_to) && - ...) - auto operator()(ExplicitCols... explicitColumns) const; -#else - template, - std::is_member_pointer, - std::is_same>, - std::is_convertible>...>, - bool> = true> - auto operator()(ExplicitCols... explicitColumns) const; -#endif - }; + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t acosh(X x) { + return {std::tuple{std::forward(x)}}; } - inline namespace literals { - /** - * cte_moniker<'n'> from a numeric literal. - * E.g. 1_ctealias, 2_ctealias - */ - template - [[nodiscard]] SQLITE_ORM_CONSTEVAL auto operator"" _ctealias() { - return internal::cte_moniker{}; - } + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t asin(X x) { + return {std::tuple{std::forward(x)}}; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * cte_moniker<'1'[, ...]> from a string literal. - * E.g. "1"_cte, "2"_cte - */ - template - [[nodiscard]] consteval auto operator"" _cte() { - return internal::explode_into(std::make_index_sequence{}); - } -#endif + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t asin(X x) { + return {std::tuple{std::forward(x)}}; } -} -#endif -namespace sqlite_orm { + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t asinh(X x) { + return {std::tuple{std::forward(x)}}; + } - namespace internal { -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct as_optional_t { - using value_type = T; + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t asinh(X x) { + return {std::tuple{std::forward(x)}}; + } - value_type value; - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan(1)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atan(X x) { + return {std::tuple{std::forward(x)}}; + } - struct distinct_string { - operator std::string() const { - return "DISTINCT"; - } - }; + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan>(1)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atan(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * DISCTINCT generic container. - */ - template - struct distinct_t : distinct_string { - using value_type = T; + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2(1, 3)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atan2(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - value_type value; + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2>(1, 3)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atan2(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - distinct_t(value_type value_) : value(std::move(value_)) {} - }; + /** + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh(1)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atanh(X x) { + return {std::tuple{std::forward(x)}}; + } - struct all_string { - operator std::string() const { - return "ALL"; - } - }; + /** + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh>(1)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atanh(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * ALL generic container. - */ - template - struct all_t : all_string { - T value; + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ceil(X x) { + return {std::tuple{std::forward(x)}}; + } - all_t(T value_) : value(std::move(value_)) {} - }; + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ceil(X x) { + return {std::tuple{std::forward(x)}}; + } - template - struct columns_t { - using columns_type = std::tuple; + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ceiling(X x) { + return {std::tuple{std::forward(x)}}; + } - columns_type columns; - bool distinct = false; + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ceiling(X x) { + return {std::tuple{std::forward(x)}}; + } - static constexpr int count = std::tuple_size::value; + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t cos(X x) { + return {std::tuple{std::forward(x)}}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - columns_t(columns_type columns) : columns{std::move(columns)} {} -#endif - }; + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t cos(X x) { + return {std::tuple{std::forward(x)}}; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_columns_v = polyfill::is_specialization_of::value; + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t cosh(X x) { + return {std::tuple{std::forward(x)}}; + } - template - using is_columns = polyfill::bool_constant>; + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t cosh(X x) { + return {std::tuple{std::forward(x)}}; + } - /* - * Captures the type of an aggregate/structure/object and column expressions, such that - * `T` can be constructed in-place as part of a result row. - * `T` must be constructible using direct-list-initialization. - */ - template - struct struct_t { - using columns_type = std::tuple; + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t degrees(X x) { + return {std::tuple{std::forward(x)}}; + } - columns_type columns; - bool distinct = false; + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t degrees(X x) { + return {std::tuple{std::forward(x)}}; + } - static constexpr int count = std::tuple_size::value; + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t exp(X x) { + return {std::tuple{std::forward(x)}}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - struct_t(columns_type columns) : columns{std::move(columns)} {} -#endif - }; + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t exp(X x) { + return {std::tuple{std::forward(x)}}; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_struct_v = polyfill::is_specialization_of::value; + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t floor(X x) { + return {std::tuple{std::forward(x)}}; + } - template - using is_struct = polyfill::bool_constant>; + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t floor(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * Subselect object type. - */ - template - struct select_t { - using return_type = T; - using conditions_type = std::tuple; + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln(200)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ln(X x) { + return {std::tuple{std::forward(x)}}; + } - return_type col; - conditions_type conditions; - bool highest_level = false; + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln>(200)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ln(X x) { + return {std::tuple{std::forward(x)}}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - select_t(return_type col, conditions_type conditions) : - col{std::move(col)}, conditions{std::move(conditions)} {} -#endif - }; + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log(X x) { + return {std::tuple{std::forward(x)}}; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_select_v = polyfill::is_specialization_of::value; + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log>(100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log(X x) { + return {std::tuple{std::forward(x)}}; + } - template - using is_select = polyfill::bool_constant>; + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10(100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log10(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * Base for UNION, UNION ALL, EXCEPT and INTERSECT - */ - template - struct compound_operator { - using expressions_tuple = std::tuple; + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10>(100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log10(X x) { + return {std::tuple{std::forward(x)}}; + } - expressions_tuple compound; + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(10, 100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log(B b, X x) { + return {std::tuple{std::forward(b), std::forward(x)}}; + } - compound_operator(expressions_tuple compound) : compound{std::move(compound)} { - iterate_tuple(this->compound, [](auto& expression) { - expression.highest_level = true; - }); - } - }; + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log>(10, 100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log(B b, X x) { + return {std::tuple{std::forward(b), std::forward(x)}}; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_of_template::value; + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2(64)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log2(X x) { + return {std::tuple{std::forward(x)}}; + } - template - using is_compound_operator = polyfill::bool_constant>; + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2>(64)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log2(X x) { + return {std::tuple{std::forward(x)}}; + } - struct union_base { - bool all = false; + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f(6, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t mod_f(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - union_base(bool all) : all{all} {} -#endif + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f>(6, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t mod_f(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - operator std::string() const { - if(!this->all) { - return "UNION"; - } else { - return "UNION ALL"; - } - } - }; + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector + */ + inline internal::built_in_function_t pi() { + return {{}}; + } - /** - * UNION object type. - */ - template - struct union_t : public compound_operator, union_base { - using typename compound_operator::expressions_tuple; + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, etc. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t pi() { + return {{}}; + } - union_t(expressions_tuple compound, bool all) : - compound_operator{std::move(compound)}, union_base{all} {} - }; + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow(2, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t pow(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - struct except_string { - operator std::string() const { - return "EXCEPT"; - } - }; + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow>(2, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t pow(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - /** - * EXCEPT object type. - */ - template - struct except_t : compound_operator, except_string { - using super = compound_operator; + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power(2, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t power(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - using super::super; - }; + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power>(2, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t power(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - struct intersect_string { - operator std::string() const { - return "INTERSECT"; - } - }; - /** - * INTERSECT object type. - */ - template - struct intersect_t : compound_operator, intersect_string { - using super = compound_operator; + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t radians(X x) { + return {std::tuple{std::forward(x)}}; + } - using super::super; - }; + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians>(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t radians(X x) { + return {std::tuple{std::forward(x)}}; + } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - /* - * Turn explicit columns for a CTE into types that the CTE backend understands - */ - template - struct decay_explicit_column { - using type = T; - }; - template - struct decay_explicit_column> { - using type = alias_holder; - }; - template - struct decay_explicit_column> { - using type = std::string; - }; - template - using decay_explicit_column_t = typename decay_explicit_column::type; + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sin(X x) { + return {std::tuple{std::forward(x)}}; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /* - * Materialization hint to instruct SQLite to materialize the select statement of a CTE into an ephemeral table as an "optimization fence". - */ - struct materialized_t {}; + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t sin(X x) { + return {std::tuple{std::forward(x)}}; + } - /* - * Materialization hint to instruct SQLite to substitute a CTE's select statement as a subquery subject to optimization. - */ - struct not_materialized_t {}; -#endif + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sinh(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * Monikered (aliased) CTE expression. - */ - template - struct common_table_expression { - using cte_moniker_type = Moniker; - using expression_type = Select; - using explicit_colrefs_tuple = ExplicitCols; - using hints_tuple = Hints; - static constexpr size_t explicit_colref_count = std::tuple_size_v; + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t sinh(X x) { + return {std::tuple{std::forward(x)}}; + } - SQLITE_ORM_NOUNIQUEADDRESS hints_tuple hints; - explicit_colrefs_tuple explicitColumns; - expression_type subselect; + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sqrt(X x) { + return {std::tuple{std::forward(x)}}; + } - common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : - explicitColumns{std::move(explicitColumns)}, subselect{std::move(subselect)} { - this->subselect.highest_level = true; - } - }; + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sqrt(X x) { + return {std::tuple{std::forward(x)}}; + } - template - using common_table_expressions = std::tuple; + /** + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tan(X x) { + return {std::tuple{std::forward(x)}}; + } - template - struct cte_builder { - ExplicitCols explicitColumns; + /** + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tan(X x) { + return {std::tuple{std::forward(x)}}; + } -#if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) - template = true> - common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; - } + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tanh(X x) { + return {std::tuple{std::forward(x)}}; + } - template = true> - common_table_expression, select_t> - as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; - } -#else - template = true> - common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; - } + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tanh(X x) { + return {std::tuple{std::forward(x)}}; + } - template = true> - common_table_expression, select_t> as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; - } -#endif - }; + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t trunc(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * WITH object type - expression with prepended CTEs. - */ - template - struct with_t { - using cte_type = common_table_expressions; - using expression_type = E; + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t trunc(X x) { + return {std::tuple{std::forward(x)}}; + } +#endif // SQLITE_ENABLE_MATH_FUNCTIONS + /** + * TYPEOF(x) function https://sqlite.org/lang_corefunc.html#typeof + */ + template + internal::built_in_function_t typeof_(T t) { + return {std::tuple{std::forward(t)}}; + } - bool recursiveIndicated; - cte_type cte; - expression_type expression; + /** + * UNICODE(x) function https://sqlite.org/lang_corefunc.html#unicode + */ + template + internal::built_in_function_t unicode(T t) { + return {std::tuple{std::forward(t)}}; + } - with_t(bool recursiveIndicated, cte_type cte, expression_type expression) : - recursiveIndicated{recursiveIndicated}, cte{std::move(cte)}, expression{std::move(expression)} { - if constexpr(is_select_v) { - this->expression.highest_level = true; - } - } - }; -#endif + /** + * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length + */ + template + internal::built_in_function_t length(T t) { + return {std::tuple{std::forward(t)}}; + } - /** - * Generic way to get DISTINCT value from any type. - */ - template - bool get_distinct(const T&) { - return false; - } + /** + * ABS(x) function https://sqlite.org/lang_corefunc.html#abs + */ + template + internal::built_in_function_t, internal::abs_string, T> abs(T t) { + return {std::tuple{std::forward(t)}}; + } - template - bool get_distinct(const columns_t& cols) { - return cols.distinct; - } + /** + * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower + */ + template + internal::built_in_function_t lower(T t) { + return {std::tuple{std::forward(t)}}; + } - template - bool get_distinct(const struct_t& cols) { - return cols.distinct; - } + /** + * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper + */ + template + internal::built_in_function_t upper(T t) { + return {std::tuple{std::forward(t)}}; + } - template - struct asterisk_t { - using type = T; + /** + * LAST_INSERT_ROWID(x) function https://www.sqlite.org/lang_corefunc.html#last_insert_rowid + */ + inline internal::built_in_function_t last_insert_rowid() { + return {{}}; + } - bool defined_order = false; + /** + * TOTAL_CHANGES() function https://sqlite.org/lang_corefunc.html#total_changes + */ + inline internal::built_in_function_t total_changes() { + return {{}}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - asterisk_t(bool definedOrder) : defined_order{definedOrder} {} -#endif - }; + /** + * CHANGES() function https://sqlite.org/lang_corefunc.html#changes + */ + inline internal::built_in_function_t changes() { + return {{}}; + } - template - struct object_t { - using type = T; + /** + * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim + */ + template + internal::built_in_function_t trim(T t) { + return {std::tuple{std::forward(t)}}; + } - bool defined_order = false; + /** + * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim + */ + template + internal::built_in_function_t trim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - object_t(bool definedOrder) : defined_order{definedOrder} {} -#endif - }; + /** + * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim + */ + template + internal::built_in_function_t ltrim(X x) { + return {std::tuple{std::forward(x)}}; + } - template - struct then_t { - using expression_type = T; + /** + * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim + */ + template + internal::built_in_function_t ltrim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - expression_type expression; - }; + /** + * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim + */ + template + internal::built_in_function_t rtrim(X x) { + return {std::tuple{std::forward(x)}}; + } - template - struct simple_case_t { - using return_type = R; - using case_expression_type = T; - using args_type = std::tuple; - using else_expression_type = E; + /** + * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim + */ + template + internal::built_in_function_t rtrim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - optional_container case_expression; - args_type args; - optional_container else_expression; - }; + /** + * HEX(X) function https://sqlite.org/lang_corefunc.html#hex + */ + template + internal::built_in_function_t hex(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * T is a case expression type - * E is else type (void is ELSE is omitted) - * Args... is a pack of WHEN expressions - */ - template - struct simple_case_builder { - using return_type = R; - using case_expression_type = T; - using args_type = std::tuple; - using else_expression_type = E; + /** + * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote + */ + template + internal::built_in_function_t quote(X x) { + return {std::tuple{std::forward(x)}}; + } - optional_container case_expression; - args_type args; - optional_container else_expression; + /** + * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob + */ + template + internal::built_in_function_t, internal::randomblob_string, X> randomblob(X x) { + return {std::tuple{std::forward(x)}}; + } - template - simple_case_builder> when(W w, then_t t) { - using result_args_type = std::tuple>; - std::pair newPair{std::move(w), std::move(t.expression)}; - result_args_type result_args = std::tuple_cat(std::move(this->args), std::make_tuple(newPair)); - std::get::value - 1>(result_args) = std::move(newPair); - return {std::move(this->case_expression), std::move(result_args), std::move(this->else_expression)}; - } + /** + * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr + */ + template + internal::built_in_function_t instr(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - simple_case_t end() { - return {std::move(this->case_expression), std::move(args), std::move(this->else_expression)}; - } + /** + * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace + */ + template, internal::is_into>::value == 0, bool> = true> + internal::built_in_function_t replace(X x, Y y, Z z) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; + } - template - simple_case_builder else_(El el) { - return {{std::move(this->case_expression)}, std::move(args), {std::move(el)}}; - } - }; + /** + * ROUND(X) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::built_in_function_t round(X x) { + return {std::tuple{std::forward(x)}}; + } - template - void validate_conditions() { - static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); - static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); - static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); - static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 LIMIT blocks"); - static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 FROM blocks"); - } + /** + * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::built_in_function_t round(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - internal::as_optional_t as_optional(T value) { - return {std::move(value)}; +#if SQLITE_VERSION_NUMBER >= 3007016 + /** + * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char + */ + template + internal::built_in_function_t char_(Args... args) { + return {std::make_tuple(std::forward(args)...)}; } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - internal::then_t then(T t) { - return {std::move(t)}; + + /** + * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random + */ + inline internal::built_in_function_t random() { + return {{}}; } +#endif - template - internal::simple_case_builder case_(T t) { - return {{std::move(t)}}; + /** + * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce + */ + template + auto coalesce(Args... args) + ->internal::built_in_function_t::value, + std::common_type...>, + polyfill::type_identity>::type, + internal::coalesce_string, + Args...> { + return {std::make_tuple(std::forward(args)...)}; } - template - internal::simple_case_builder case_() { - return {}; + /** + * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull + */ + template + auto ifnull(X x, Y y) + ->internal::built_in_function_t< + typename mpl::conditional_t< // choose R or common type + std::is_void::value, + std::common_type, internal::field_type_or_type_t>, + polyfill::type_identity>::type, + internal::ifnull_string, + X, + Y> { + return {std::make_tuple(std::move(x), std::move(y))}; } - template - internal::distinct_t distinct(T t) { - return {std::move(t)}; + /** + * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif + */ +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + /** + * NULLIF(X,Y) using common return type of X and Y + */ + template>, + polyfill::is_detected, + internal::field_type_or_type_t>>, + bool> = true> + auto nullif(X x, Y y) { + if constexpr(std::is_void_v) { + using F = internal::built_in_function_t< + std::optional, internal::field_type_or_type_t>>, + internal::nullif_string, + X, + Y>; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } else { + using F = internal::built_in_function_t; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } } - - template - internal::all_t all(T t) { - return {std::move(t)}; +#else + template + internal::built_in_function_t nullif(X x, Y y) { + return {std::make_tuple(std::move(x), std::move(y))}; } +#endif + /** + * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ template - internal::columns_t distinct(internal::columns_t cols) { - cols.distinct = true; - return cols; + internal::built_in_function_t date(Args... args) { + return {std::tuple{std::forward(args)...}}; } - /* - * Combine multiple columns in a tuple. + /** + * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ template - constexpr internal::columns_t columns(Args... args) { - return {std::make_tuple(std::forward(args)...)}; + internal::built_in_function_t time(Args... args) { + return {std::tuple{std::forward(args)...}}; } - /* - * Construct an unmapped structure ad-hoc from multiple columns. - * `T` must be constructible from the column results using direct-list-initialization. + /** + * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ - template - constexpr internal::struct_t struct_(Args... args) { - return {std::make_tuple(std::forward(args)...)}; + template + internal::built_in_function_t datetime(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** - * Public function for subselect query. Is useful in UNION queries. + * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ - template - internal::select_t select(T t, Args... args) { - using args_tuple = std::tuple; - internal::validate_conditions(); - return {std::move(t), {std::forward(args)...}}; + template + internal::built_in_function_t julianday(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** - * Public function for UNION operator. - * Expressions are subselect objects. - * Look through example in examples/union.cpp + * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html */ - template - internal::union_t union_(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); - return {{std::forward(expressions)...}, false}; + template + internal::built_in_function_t strftime(Args... args) { + return {std::tuple{std::forward(args)...}}; } /** - * Public function for UNION ALL operator. - * Expressions are subselect objects. - * Look through example in examples/union.cpp + * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob */ - template - internal::union_t union_all(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); - return {{std::forward(expressions)...}, true}; + template + internal::built_in_function_t, internal::zeroblob_string, N> zeroblob(N n) { + return {std::tuple{std::forward(n)}}; } /** - * Public function for EXCEPT operator. - * Expressions are subselect objects. - * Look through example in examples/except.cpp + * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr */ - template - internal::except_t except(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); - return {{std::forward(expressions)...}}; - } - - template - internal::intersect_t intersect(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); - return {{std::forward(expressions)...}}; + template + internal::built_in_function_t substr(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#if SQLITE_VERSION_NUMBER >= 3035003 -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /* - * Materialization hint to instruct SQLite to materialize the select statement of a CTE into an ephemeral table as an "optimization fence". - * - * Example: - * 1_ctealias().as(select(1)); + /** + * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr */ - inline consteval internal::materialized_t materialized() { - return {}; + template + internal::built_in_function_t substr(X x, Y y, Z z) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; } - /* - * Materialization hint to instruct SQLite to substitute a CTE's select statement as a subquery subject to optimization. - * - * Example: - * 1_ctealias().as(select(1)); +#ifdef SQLITE_SOUNDEX + /** + * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex */ - inline consteval internal::not_materialized_t not_materialized() { - return {}; + template + internal::built_in_function_t soundex(X x) { + return {std::tuple{std::forward(x)}}; } -#endif #endif /** - * Introduce the construction of a common table expression using the specified moniker. - * - * The list of explicit columns is optional; - * if provided the number of columns must match the number of columns of the subselect. - * The column names will be merged with the subselect: - * 1. column names of subselect - * 2. explicit columns - * 3. fill in empty column names with column index - * - * Example: - * using cte_1 = decltype(1_ctealias); - * cte()(select(&Object::id)); - * cte(&Object::name)(select("object")); + * TOTAL(X) aggregate function. */ - template, - std::is_member_pointer, - internal::is_column, - std::is_same>, - std::is_convertible>...>, - bool> = true> - auto cte(ExplicitCols... explicitColumns) { - using namespace ::sqlite_orm::internal; - static_assert(is_cte_moniker_v, "Moniker must be a CTE moniker"); - static_assert((!is_builtin_numeric_column_alias_v && ...), - "Numeric column aliases are reserved for referencing columns locally within a single CTE."); - - using builder_type = - cte_builder, decay_explicit_column_t>>; - return builder_type{{std::move(explicitColumns)...}}; - } - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - requires((internal::is_column_alias_v || std::is_member_pointer_v || - internal::is_column_v || - std::same_as> || - std::convertible_to) && - ...) - auto cte(ExplicitCols... explicitColumns) { - using namespace ::sqlite_orm::internal; - static_assert((!is_builtin_numeric_column_alias_v && ...), - "Numeric column aliases are reserved for referencing columns locally within a single CTE."); - - using builder_type = - cte_builder, decay_explicit_column_t>>; - return builder_type{{std::move(explicitColumns)...}}; - } -#endif - - namespace internal { -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - template - requires((is_column_alias_v || std::is_member_pointer_v || - std::same_as> || - std::convertible_to) && - ...) - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { - return cte>(std::forward(explicitColumns)...); - } -#else - template - template, - std::is_member_pointer, - std::is_same>, - std::is_convertible>...>, - bool>> - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { - return cte>(std::forward(explicitColumns)...); - } -#endif + template + internal::built_in_aggregate_function_t total(X x) { + return {std::tuple{std::forward(x)}}; } - /** - * With-clause for a tuple of ordinary CTEs. - * - * Despite the missing RECURSIVE keyword, the CTEs can be recursive. + /** + * SUM(X) aggregate function. */ - template = true> - internal::with_t with(internal::common_table_expressions ctes, E expression) { - return {false, std::move(ctes), std::move(expression)}; + template + internal::built_in_aggregate_function_t, internal::sum_string, X> sum(X x) { + return {std::tuple{std::forward(x)}}; } - /** - * With-clause for a tuple of ordinary CTEs. - * - * Despite the missing RECURSIVE keyword, the CTEs can be recursive. + /** + * COUNT(X) aggregate function. */ - template = true> - internal::with_t, CTEs...> with(internal::common_table_expressions ctes, - Compound sel) { - return {false, std::move(ctes), sqlite_orm::select(std::move(sel))}; + template + internal::built_in_aggregate_function_t count(X x) { + return {std::tuple{std::forward(x)}}; } - /** - * With-clause for a single ordinary CTE. - * - * Despite the missing `RECURSIVE` keyword, the CTE can be recursive. - * - * Example: - * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; - * with(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); + /** + * COUNT(*) without FROM function. */ - template = true, - internal::satisfies_not = true> - internal::with_t with(CTE cte, E expression) { - return {false, {std::move(cte)}, std::move(expression)}; + inline internal::count_asterisk_without_type count() { + return {}; } - /** - * With-clause for a single ordinary CTE. - * - * Despite the missing `RECURSIVE` keyword, the CTE can be recursive. - * - * Example: - * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; - * with(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); + /** + * COUNT(*) with FROM function. Specified type T will be serialized as + * a from argument. + */ + template + internal::count_asterisk_t count() { + return {}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * COUNT(*) with FROM function. Specified recordset will be serialized as + * a from argument. */ - template = true, - internal::satisfies = true> - internal::with_t, CTE> with(CTE cte, Compound sel) { - return {false, {std::move(cte)}, sqlite_orm::select(std::move(sel))}; + template + auto count() { + return count>(); } +#endif - /** - * With-clause for a tuple of potentially recursive CTEs. - * - * @note The use of RECURSIVE does not force common table expressions to be recursive. + /** + * AVG(X) aggregate function. */ - template = true> - internal::with_t with_recursive(internal::common_table_expressions ctes, E expression) { - return {true, std::move(ctes), std::move(expression)}; + template + internal::built_in_aggregate_function_t avg(X x) { + return {std::tuple{std::forward(x)}}; } - /** - * With-clause for a tuple of potentially recursive CTEs. - * - * @note The use of RECURSIVE does not force common table expressions to be recursive. + /** + * MAX(X) aggregate function. */ - template = true> - internal::with_t, CTEs...> - with_recursive(internal::common_table_expressions ctes, Compound sel) { - return {true, std::move(ctes), sqlite_orm::select(std::move(sel))}; + template + internal::built_in_aggregate_function_t, internal::max_string, X> max(X x) { + return {std::tuple{std::forward(x)}}; } - /** - * With-clause for a single potentially recursive CTE. - * - * @note The use of RECURSIVE does not force common table expressions to be recursive. - * - * Example: - * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; - * with_recursive(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); + /** + * MIN(X) aggregate function. */ - template = true, - internal::satisfies_not = true> - internal::with_t with_recursive(CTE cte, E expression) { - return {true, {std::move(cte)}, std::move(expression)}; + template + internal::built_in_aggregate_function_t, internal::min_string, X> min(X x) { + return {std::tuple{std::forward(x)}}; } - /** - * With-clause for a single potentially recursive CTE. - * - * @note The use of RECURSIVE does not force common table expressions to be recursive. - * - * Example: - * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; - * with_recursive(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); + /** + * MAX(X, Y, ...) scalar function. + * The return type is the type of the first argument. */ - template = true, - internal::satisfies = true> - internal::with_t, CTE> with_recursive(CTE cte, Compound sel) { - return {true, {std::move(cte)}, sqlite_orm::select(std::move(sel))}; + template + internal::built_in_function_t, internal::max_string, X, Y, Rest...> max( + X x, + Y y, + Rest... rest) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; } -#endif /** - * `SELECT * FROM T` expression that fetches results as tuples. - * T is a type mapped to a storage, or an alias of it. - * The `definedOrder` parameter denotes the expected order of result columns. - * The default is the implicit order as returned by SQLite, which may differ from the defined order - * if the schema of a table has been changed. - * By specifying the defined order, the columns are written out in the resulting select SQL string. - * - * In pseudo code: - * select(asterisk(false)) -> SELECT * from User - * select(asterisk(true)) -> SELECT id, name from User - * - * Example: auto rows = storage.select(asterisk()); - * // decltype(rows) is std::vector> - * Example: auto rows = storage.select(asterisk(true)); - * // decltype(rows) is std::vector> - * - * If you need to fetch results as objects instead of tuples please use `object()`. + * MIN(X, Y, ...) scalar function. + * The return type is the type of the first argument. */ - template - internal::asterisk_t asterisk(bool definedOrder = false) { - return {definedOrder}; + template + internal::built_in_function_t, internal::min_string, X, Y, Rest...> min( + X x, + Y y, + Rest... rest) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Example: - * constexpr orm_table_alias auto m = "m"_alias.for_(); - * auto reportingTo = - * storage.select(asterisk(), inner_join(on(m->*&Employee::reportsTo == &Employee::employeeId))); + * GROUP_CONCAT(X) aggregate function. */ - template - auto asterisk(bool definedOrder = false) { - return asterisk>(definedOrder); + template + internal::built_in_aggregate_function_t group_concat(X x) { + return {std::tuple{std::forward(x)}}; } -#endif /** - * `SELECT * FROM T` expression that fetches results as objects of type T. - * T is a type mapped to a storage, or an alias of it. - * - * Example: auto rows = storage.select(object()); - * // decltype(rows) is std::vector, where the User objects are constructed from columns in implicitly stored order - * Example: auto rows = storage.select(object(true)); - * // decltype(rows) is std::vector, where the User objects are constructed from columns in declared make_table order - * - * If you need to fetch results as tuples instead of objects please use `asterisk()`. + * GROUP_CONCAT(X, Y) aggregate function. */ - template - internal::object_t object(bool definedOrder = false) { - return {definedOrder}; + template + internal::built_in_aggregate_function_t group_concat(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } +#ifdef SQLITE_ENABLE_JSON1 + template + internal::built_in_function_t json(X x) { + return {std::tuple{std::forward(x)}}; } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto object(bool definedOrder = false) { - return object>(definedOrder); + template + internal::built_in_function_t json_array(Args... args) { + return {std::tuple{std::forward(args)...}}; } -#endif -} -#pragma once -#include // std::string + template + internal::built_in_function_t json_array_length(X x) { + return {std::tuple{std::forward(x)}}; + } -// #include "functional/cxx_universal.h" + template + internal::built_in_function_t json_array_length(X x) { + return {std::tuple{std::forward(x)}}; + } -namespace sqlite_orm { + template + internal::built_in_function_t json_array_length(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - struct table_info { - int cid = 0; - std::string name; - std::string type; - bool notnull = false; - std::string dflt_value; - int pk = 0; + template + internal::built_in_function_t json_array_length(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } -#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) - table_info(decltype(cid) cid_, - decltype(name) name_, - decltype(type) type_, - decltype(notnull) notnull_, - decltype(dflt_value) dflt_value_, - decltype(pk) pk_) : - cid(cid_), - name(std::move(name_)), type(std::move(type_)), notnull(notnull_), dflt_value(std::move(dflt_value_)), - pk(pk_) {} -#endif - }; + template + internal::built_in_function_t json_extract(X x, Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } - struct table_xinfo { - int cid = 0; - std::string name; - std::string type; - bool notnull = false; - std::string dflt_value; - int pk = 0; - int hidden = 0; // different than 0 => generated_always_as() - TODO verify + template + internal::built_in_function_t json_insert(X x, + Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_insert must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } -#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) - table_xinfo(decltype(cid) cid_, - decltype(name) name_, - decltype(type) type_, - decltype(notnull) notnull_, - decltype(dflt_value) dflt_value_, - decltype(pk) pk_, - decltype(hidden) hidden_) : - cid(cid_), - name(std::move(name_)), type(std::move(type_)), notnull(notnull_), dflt_value(std::move(dflt_value_)), - pk(pk_), hidden{hidden_} {} -#endif - }; -} -#pragma once + template + internal::built_in_function_t json_replace(X x, + Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_replace must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_set(X x, Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_set must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_object(Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_object must be even"); + return {std::tuple{std::forward(args)...}}; + } + + template + internal::built_in_function_t json_patch(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_remove(X x, + Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_remove(X x, Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } -#include -#include -#include -#include + template + internal::built_in_function_t json_type(X x) { + return {std::tuple{std::forward(x)}}; + } -// #include "../functional/cxx_universal.h" + template + internal::built_in_function_t json_type(X x) { + return {std::tuple{std::forward(x)}}; + } -// #include "../optional_container.h" + template + internal::built_in_function_t json_type(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } -// NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? -// (Could be implemented with a normal trigger that insert or update an internal table and then retreive -// the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) -// It could be an interesting feature to bring to sqlite_orm that other libraries don't have ? + template + internal::built_in_function_t json_type(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } -namespace sqlite_orm { - namespace internal { - enum class trigger_timing { trigger_before, trigger_after, trigger_instead_of }; - enum class trigger_type { trigger_delete, trigger_insert, trigger_update }; + template + internal::built_in_function_t json_valid(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * This class is an intermediate SQLite trigger, to be used with - * `make_trigger` to create a full trigger. - * T is the base of the trigger (contains its type, timing and associated table) - * S is the list of trigger statements - */ - template - struct partial_trigger_t { - using statements_type = std::tuple; + template + internal::built_in_function_t json_quote(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * Base of the trigger (contains its type, timing and associated table) - */ - T base; - /** - * Statements of the triggers (to be executed when the trigger fires) - */ - statements_type statements; + template + internal::built_in_function_t json_group_array(X x) { + return {std::tuple{std::forward(x)}}; + } - partial_trigger_t(T trigger_base, S... statements) : - base{std::move(trigger_base)}, statements{std::make_tuple(std::forward(statements)...)} {} + template + internal::built_in_function_t json_group_object(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } +#endif // SQLITE_ENABLE_JSON1 - partial_trigger_t& end() { - return *this; - } - }; + // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace + // to facilitate ADL (Argument Dependent Lookup) + _EXPORT_SQLITE_ORM namespace internal { + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + add_t, unwrap_expression_t> operator+(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct base_trigger { - /** - * Name of the trigger - */ - std::string name; - }; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + sub_t, unwrap_expression_t> operator-(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - /** - * This class represent a SQLite trigger - * T is the base of the trigger (contains its type, timing and associated table) - * S is the list of trigger statments - */ - template - struct trigger_t : base_trigger { - using object_type = void; - using elements_type = typename partial_trigger_t::statements_type; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + mul_t, unwrap_expression_t> operator*(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - /** - * Base of the trigger (contains its type, timing and associated table) - */ - T base; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + div_t, unwrap_expression_t> operator/(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - /** - * Statements of the triggers (to be executed when the trigger fires) - */ - elements_type elements; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + mod_t, unwrap_expression_t> operator%(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + } -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - trigger_t(std::string name, T trigger_base, elements_type statements) : - base_trigger{std::move(name)}, base(std::move(trigger_base)), elements(std::move(statements)) {} -#endif - }; + template + internal::highlight_t highlight(X x, Y y, Z z) { + return {std::move(x), std::move(y), std::move(z)}; + } +} - /** - * Base of a trigger. Contains the trigger type/timming and the table type - * T is the table type - * W is `when` expression type - * Type is the trigger base type (type+timing) - */ - template - struct trigger_base_t { - using table_type = T; - using when_type = W; - using trigger_type_base = Type; +// #include "alias_traits.h" - /** - * Contains the trigger type and timing - */ - trigger_type_base type_base; - /** - * Value used to determine if we execute the trigger on each row or on each statement - * (SQLite doesn't support the FOR EACH STATEMENT syntax yet: https://sqlite.org/lang_createtrigger.html#description - * so this value is more of a placeholder for a later update) - */ - bool do_for_each_row = false; - /** - * When expression (if any) - * If a WHEN expression is specified, the trigger will only execute - * if the expression evaluates to true when the trigger is fired - */ - optional_container container_when; +// #include "cte_moniker.h" - trigger_base_t(trigger_type_base type_base_) : type_base(std::move(type_base_)) {} +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include +#include // std::make_index_sequence +#endif +#include // std::enable_if, std::is_member_pointer, std::is_same, std::is_convertible +#include // std::ignore +#include +#endif - trigger_base_t& for_each_row() { - this->do_for_each_row = true; - return *this; - } +// #include "functional/cxx_universal.h" - template - trigger_base_t when(WW expression) { - trigger_base_t res(this->type_base); - res.container_when.field = std::move(expression); - return res; - } +// #include "functional/cstring_literal.h" - template - partial_trigger_t, S...> begin(S... statements) { - return {*this, std::forward(statements)...}; - } +// #include "alias.h" + +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +_EXPORT_SQLITE_ORM namespace sqlite_orm { + + namespace internal { + /** + * A special record set alias that is both, a storage lookup type (mapping type) and an alias. + */ + template + struct cte_moniker + : recordset_alias< + cte_moniker /* refer to self, since a moniker is both, an alias and a mapped type */, + A, + X...> { + /** + * Introduce the construction of a common table expression using this moniker. + * + * The list of explicit columns is optional; + * if provided the number of columns must match the number of columns of the subselect. + * The column names will be merged with the subselect: + * 1. column names of subselect + * 2. explicit columns + * 3. fill in empty column names with column index + * + * Example: + * 1_ctealias()(select(&Object::id)); + * 1_ctealias(&Object::name)(select("object")); + * + * @return A `cte_builder` instance. + * @note (internal): Defined in select_constraints.h in order to keep this member function in the same place as the named factory function `cte()`, + * and to keep the actual creation of the builder in one place. + */ +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + requires((is_column_alias_v || std::is_member_pointer_v || + std::same_as> || + std::convertible_to) && + ...) + auto operator()(ExplicitCols... explicitColumns) const; +#else + template, + std::is_member_pointer, + std::is_same>, + std::is_convertible>...>, + bool> = true> + auto operator()(ExplicitCols... explicitColumns) const; +#endif }; + } + inline namespace literals { /** - * Contains the trigger type and timing + * cte_moniker<'n'> from a numeric literal. + * E.g. 1_ctealias, 2_ctealias */ - struct trigger_type_base_t { - /** - * Value indicating if the trigger is run BEFORE, AFTER or INSTEAD OF - * the statement that fired it. - */ - trigger_timing timing; - /** - * The type of the statement that would cause the trigger to fire. - * Can be DELETE, INSERT, or UPDATE. - */ - trigger_type type; - - trigger_type_base_t(trigger_timing timing, trigger_type type) : timing(timing), type(type) {} - - template - trigger_base_t on() { - return {*this}; - } - }; + template + [[nodiscard]] SQLITE_ORM_CONSTEVAL auto operator"" _ctealias() { + return internal::cte_moniker{}; + } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Special case for UPDATE OF (columns) - * Contains the trigger type and timing + * cte_moniker<'1'[, ...]> from a string literal. + * E.g. "1"_cte, "2"_cte */ - template - struct trigger_update_type_t : trigger_type_base_t { - using columns_type = std::tuple; - - /** - * Contains the columns the trigger is watching. Will only - * trigger if one of theses columns is updated. - */ - columns_type columns; - - trigger_update_type_t(trigger_timing timing, trigger_type type, Cs... columns) : - trigger_type_base_t(timing, type), columns(std::make_tuple(std::forward(columns)...)) {} - - template - trigger_base_t> on() { - return {*this}; - } - }; + template + [[nodiscard]] consteval auto operator"" _cte() { + return internal::explode_into(std::make_index_sequence{}); + } +#endif + } +} +#endif - struct trigger_timing_t { - trigger_timing timing; +// #include "schema/column.h" - trigger_type_base_t delete_() { - return {timing, trigger_type::trigger_delete}; - } +#include // std::tuple +#include // std::string +#include // std::unique_ptr +#include // std::is_same, std::is_member_object_pointer +#include // std::move - trigger_type_base_t insert() { - return {timing, trigger_type::trigger_insert}; - } +// #include "../functional/cxx_universal.h" - trigger_type_base_t update() { - return {timing, trigger_type::trigger_update}; - } +// #include "../functional/cxx_type_traits_polyfill.h" - template - trigger_update_type_t update_of(Cs... columns) { - return {timing, trigger_type::trigger_update, std::forward(columns)...}; - } - }; +// #include "../tuple_helper/tuple_traits.h" - struct raise_t { - enum class type_t { - ignore, - rollback, - abort, - fail, - }; +// #include "../tuple_helper/tuple_filter.h" - type_t type = type_t::ignore; - std::string message; +// #include "../type_traits.h" -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - raise_t(type_t type, std::string message) : type{type}, message{std::move(message)} {} -#endif - }; +// #include "../member_traits/member_traits.h" - template - struct new_t { - using expression_type = T; +// #include "../type_is_nullable.h" - expression_type expression; - }; +#include // std::false_type, std::true_type, std::enable_if +#include // std::shared_ptr, std::unique_ptr +// #include "functional/cxx_optional.h" - template - struct old_t { - using expression_type = T; +// #include "functional/cxx_type_traits_polyfill.h" - expression_type expression; - }; - } // NAMESPACE internal +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** - * NEW.expression function used within TRIGGER expressions + * This is class that tells `sqlite_orm` that type is nullable. Nullable types + * are mapped to sqlite database as `NULL` and not-nullable are mapped as `NOT NULL`. + * Default nullability status for all types is `NOT NULL`. So if you want to map + * custom type as `NULL` (for example: boost::optional) you have to create a specialization + * of `type_is_nullable` for your type and derive from `std::true_type`. */ - template - internal::new_t new_(T expression) { - return {std::move(expression)}; - } + template + struct type_is_nullable : std::false_type { + bool operator()(const T&) const { + return true; + } + }; /** - * OLD.expression function used within TRIGGER expressions + * This is a specialization for std::shared_ptr, std::unique_ptr, std::optional, which are nullable in sqlite_orm. */ template - internal::old_t old(T expression) { - return {std::move(expression)}; - } + struct type_is_nullable, +#endif + polyfill::is_specialization_of, + polyfill::is_specialization_of>::value>> : std::true_type { + bool operator()(const T& t) const { + return static_cast(t); + } + }; +} - /** - * RAISE(IGNORE) expression used within TRIGGER expressions - */ - inline internal::raise_t raise_ignore() { - return {internal::raise_t::type_t::ignore, {}}; - } +// #include "../constraints.h" - /** - * RAISE(ROLLBACK, %message%) expression used within TRIGGER expressions - */ - inline internal::raise_t raise_rollback(std::string message) { - return {internal::raise_t::type_t::rollback, std::move(message)}; - } +_EXPORT_SQLITE_ORM namespace sqlite_orm { - /** - * RAISE(ABORT, %message%) expression used within TRIGGER expressions - */ - inline internal::raise_t raise_abort(std::string message) { - return {internal::raise_t::type_t::abort, std::move(message)}; - } + namespace internal { - /** - * RAISE(FAIL, %message%) expression used within TRIGGER expressions - */ - inline internal::raise_t raise_fail(std::string message) { - return {internal::raise_t::type_t::fail, std::move(message)}; - } + struct column_identifier { - template - internal::trigger_t make_trigger(std::string name, const internal::partial_trigger_t& part) { - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), std::move(part.base), std::move(part.statements)}); - } + /** + * Column name. + */ + std::string name; + }; - inline internal::trigger_timing_t before() { - return {internal::trigger_timing::trigger_before}; - } + struct empty_setter {}; - inline internal::trigger_timing_t after() { - return {internal::trigger_timing::trigger_after}; - } + /* + * Encapsulates object member pointers that are used as column fields, + * and whose object is mapped to storage. + * + * G is a member object pointer or member function pointer + * S is a member function pointer or `empty_setter` + */ + template + struct column_field { + using member_pointer_t = G; + using setter_type = S; + using object_type = member_object_type_t; + using field_type = member_field_type_t; - inline internal::trigger_timing_t instead_of() { - return {internal::trigger_timing::trigger_instead_of}; - } -} -#pragma once + /** + * Member pointer used to read a field value. + * If it is a object member pointer it is also used to write a field value. + */ + const member_pointer_t member_pointer; -#include -#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence -#include // std::default_delete -#include // std::string, std::wstring -#include // std::vector -#include // ::strncpy, ::strlen -// #include "functional/cxx_string_view.h" + /** + * Setter member function to write a field value + */ + SQLITE_ORM_NOUNIQUEADDRESS + const setter_type setter; + + /** + * Simplified interface for `NOT NULL` constraint + */ + constexpr bool is_not_null() const { + return !type_is_nullable::value; + } + }; + + /* + * Encapsulates a tuple of column constraints. + * + * Op... is a constraints pack, e.g. primary_key_t, unique_t etc + */ + template + struct column_constraints { + using constraints_type = std::tuple; + + SQLITE_ORM_NOUNIQUEADDRESS + constraints_type constraints; + + /** + * Checks whether contraints contain specified type. + */ + template class Trait> + constexpr static bool is() { + return tuple_has::value; + } -#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED -#include // ::wcsncpy, ::wcslen -#endif -#ifndef SQLITE_ORM_OMITS_CODECVT -#include // std::wstring_convert -#include // std::codecvt_utf8_utf16 -#endif + /** + * Simplified interface for `DEFAULT` constraint + * @return string representation of default value if it exists otherwise nullptr + */ + std::unique_ptr default_value() const; + }; -// #include "functional/cxx_universal.h" + /** + * Column definition. + * + * It is a composition of orthogonal information stored in different base classes. + */ + template + struct column_t : column_identifier, column_field, column_constraints { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + column_t(std::string name, G memberPointer, S setter, std::tuple op) : + column_identifier{std::move(name)}, column_field{memberPointer, setter}, + column_constraints{std::move(op)} {} +#endif + }; -// #include "functional/cxx_type_traits_polyfill.h" + template + struct column_field_expression { + using type = void; + }; -// #include "functional/cxx_functional_polyfill.h" + template + struct column_field_expression, void> { + using type = typename column_t::member_pointer_t; + }; -// #include "is_std_ptr.h" + template + using column_field_expression_t = typename column_field_expression::type; -// #include "tuple_helper/tuple_filter.h" + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of::value; -// #include "type_traits.h" + template + using is_column = polyfill::bool_constant>; -// #include "error_code.h" + template + using col_index_sequence_with_field_type = + filter_tuple_sequence_t::template fn, + field_type_t, + filter_tuple_sequence_t>; -// #include "arithmetic_tag.h" + template class TraitFn> + using col_index_sequence_with = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; -#include // std::is_integral + template class TraitFn> + using col_index_sequence_excluding = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; + } -// #include "functional/mpl/conditional.h" + /** + * Factory function for a column definition from a member object pointer of the object to be mapped. + */ + template = true> + internal::column_t make_column(std::string name, + M memberPointer, + Op... constraints) { + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); -namespace sqlite_orm { + // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, + // as this will lead to UB with Clang on MinGW! + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), memberPointer, {}, std::tuple{std::move(constraints)...}}); + } /** - * Helper classes used by statement_binder and row_extractor. + * Factory function for a column definition from "setter" and "getter" member function pointers of the object to be mapped. */ - struct int_or_smaller_tag {}; - struct bigint_tag {}; - struct real_tag {}; + template = true, + internal::satisfies = true> + internal::column_t make_column(std::string name, S setter, G getter, Op... constraints) { + static_assert(std::is_same, internal::getter_field_type_t>::value, + "Getter and setter must get and set same data type"); + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); - template - using arithmetic_tag_t = - mpl::conditional_t::value, - // Integer class - mpl::conditional_t, - // Floating-point class - real_tag>; -} + // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, + // as this will lead to UB with Clang on MinGW! + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), getter, setter, std::tuple{std::move(constraints)...}}); + } -// #include "xdestroy_handling.h" + /** + * Factory function for a column definition from "getter" and "setter" member function pointers of the object to be mapped. + */ + template = true, + internal::satisfies = true> + internal::column_t make_column(std::string name, G getter, S setter, Op... constraints) { + static_assert(std::is_same, internal::getter_field_type_t>::value, + "Getter and setter must get and set same data type"); + static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); -#include // std::integral_constant -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED -#include -#endif + // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, + // as this will lead to UB with Clang on MinGW! + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), getter, setter, std::tuple{std::move(constraints)...}}); + } +} -// #include "functional/cxx_universal.h" +_EXPORT_SQLITE_ORM namespace sqlite_orm { -// #include "functional/cxx_type_traits_polyfill.h" + namespace internal { +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct as_optional_t { + using value_type = T; -namespace sqlite_orm { + value_type value; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - using xdestroy_fn_t = void (*)(void*); - using null_xdestroy_t = std::integral_constant; - SQLITE_ORM_INLINE_VAR constexpr null_xdestroy_t null_xdestroy_f{}; -} + struct distinct_string { + operator std::string() const { + return "DISTINCT"; + } + }; -namespace sqlite_orm { - namespace internal { -#ifdef SQLITE_ORM_CONCEPTS_SUPPORTED /** - * Constrains a deleter to be state-less. + * DISCTINCT generic container. */ - template - concept stateless_deleter = std::is_empty_v && std::is_default_constructible_v; + template + struct distinct_t : distinct_string { + using value_type = T; - /** - * Constrains a deleter to be an integral function constant. - */ - template - concept integral_fp_c = requires { - typename D::value_type; - D::value; - requires std::is_function_v>; + value_type value; + + distinct_t(value_type value_) : value(std::move(value_)) {} }; - /** - * Constrains a deleter to be or to yield a function pointer. - */ - template - concept yields_fp = requires(D d) { - // yielding function pointer by using the plus trick - { +d }; - requires std::is_function_v>; + struct all_string { + operator std::string() const { + return "ALL"; + } }; -#endif -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED /** - * Yield a deleter's function pointer. + * ALL generic container. */ - template - struct yield_fp_of { - using type = decltype(+std::declval()); + template + struct all_t : all_string { + T value; + + all_t(T value_) : value(std::move(value_)) {} }; -#else - template - SQLITE_ORM_INLINE_VAR constexpr bool is_stateless_deleter_v = - std::is_empty::value && std::is_default_constructible::value; + template + struct columns_t { + using columns_type = std::tuple; - template - struct is_integral_fp_c : std::false_type {}; - template - struct is_integral_fp_c< - D, - polyfill::void_t>::value>>> - : std::true_type {}; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_integral_fp_c_v = is_integral_fp_c::value; + columns_type columns; + bool distinct = false; - template - struct can_yield_fp : std::false_type {}; - template - struct can_yield_fp< - D, - polyfill::void_t< - decltype(+std::declval()), - std::enable_if_t())>>::value>>> - : std::true_type {}; - template - SQLITE_ORM_INLINE_VAR constexpr bool can_yield_fp_v = can_yield_fp::value; + static constexpr int count = std::tuple_size::value; - template> - struct yield_fp_of { - using type = void; - }; - template - struct yield_fp_of { - using type = decltype(+std::declval()); +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + columns_t(columns_type columns) : columns{std::move(columns)} {} +#endif }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_columns_v = polyfill::is_specialization_of::value; + + template + using is_columns = polyfill::bool_constant>; + + /* + * Captures the type of an aggregate/structure/object and column expressions, such that + * `T` can be constructed in-place as part of a result row. + * `T` must be constructible using direct-list-initialization. + */ + template + struct struct_t { + using columns_type = std::tuple; + + columns_type columns; + bool distinct = false; + + static constexpr int count = std::tuple_size::value; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + struct_t(columns_type columns) : columns{std::move(columns)} {} #endif - template - using yielded_fn_t = typename yield_fp_of::type; + }; -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - template - concept is_unusable_for_xdestroy = - (!stateless_deleter && (yields_fp && !std::convertible_to, xdestroy_fn_t>)); + template + SQLITE_ORM_INLINE_VAR constexpr bool is_struct_v = polyfill::is_specialization_of::value; + + template + using is_struct = polyfill::bool_constant>; /** - * This concept tests whether a deleter yields a function pointer, which is convertible to an xdestroy function pointer. - * Note: We are using 'is convertible' rather than 'is same' because of any exception specification. + * Subselect object type. */ - template - concept yields_xdestroy = yields_fp && std::convertible_to, xdestroy_fn_t>; + template + struct select_t { + using return_type = T; + using conditions_type = std::tuple; - template - concept needs_xdestroy_proxy = - (stateless_deleter && (!yields_fp || !std::convertible_to, xdestroy_fn_t>)); + return_type col; + conditions_type conditions; + bool highest_level = false; - /** - * xDestroy function that constructs and invokes the stateless deleter. - * - * Requires that the deleter can be called with the q-qualified pointer argument; - * it doesn't check so explicitly, but a compiler error will occur. - */ - template - requires(!integral_fp_c) - void xdestroy_proxy(void* p) noexcept { - // C-casting `void* -> P*` like statement_binder> - auto o = (P*)p; - // ignoring return code - (void)D{}(o); - } +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + select_t(return_type col, conditions_type conditions) : + col{std::move(col)}, conditions{std::move(conditions)} {} +#endif + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_select_v = polyfill::is_specialization_of::value; + + template + using is_select = polyfill::bool_constant>; /** - * xDestroy function that invokes the integral function pointer constant. - * - * Performs a const-cast of the argument pointer in order to allow for C API functions - * that take a non-const parameter, but user code passes a pointer to a const object. + * Base for UNION, UNION ALL, EXCEPT and INTERSECT */ - template - void xdestroy_proxy(void* p) noexcept { - // C-casting `void* -> P*` like statement_binder>, - auto o = (std::remove_cv_t

*)(P*)p; - // ignoring return code - (void)D{}(o); - } -#else - template - SQLITE_ORM_INLINE_VAR constexpr bool is_unusable_for_xdestroy_v = - !is_stateless_deleter_v && - (can_yield_fp_v && !std::is_convertible, xdestroy_fn_t>::value); + template + struct compound_operator { + using expressions_tuple = std::tuple; - template - SQLITE_ORM_INLINE_VAR constexpr bool can_yield_xdestroy_v = - can_yield_fp_v && std::is_convertible, xdestroy_fn_t>::value; + expressions_tuple compound; - template - SQLITE_ORM_INLINE_VAR constexpr bool needs_xdestroy_proxy_v = - is_stateless_deleter_v && - (!can_yield_fp_v || !std::is_convertible, xdestroy_fn_t>::value); + compound_operator(expressions_tuple compound) : compound{std::move(compound)} { + iterate_tuple(this->compound, [](auto& expression) { + expression.highest_level = true; + }); + } + }; - template, bool> = true> - void xdestroy_proxy(void* p) noexcept { - // C-casting `void* -> P*` like statement_binder> - auto o = (P*)p; - // ignoring return code - (void)D{}(o); - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_of_template::value; - template, bool> = true> - void xdestroy_proxy(void* p) noexcept { - // C-casting `void* -> P*` like statement_binder>, - auto o = (std::remove_cv_t

*)(P*)p; - // ignoring return code - (void)D{}(o); - } + template + using is_compound_operator = polyfill::bool_constant>; + + struct union_base { + bool all = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + union_base(bool all) : all{all} {} #endif - } -} -namespace sqlite_orm { + operator std::string() const { + if(!this->all) { + return "UNION"; + } else { + return "UNION ALL"; + } + } + }; -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - /** - * Prohibits using a yielded function pointer, which is not of type xdestroy_fn_t. - * - * Explicitly declared for better error messages. - */ - template - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept - requires(internal::is_unusable_for_xdestroy) - { - static_assert(polyfill::always_false_v, - "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); - return nullptr; - } + /** + * UNION object type. + */ + template + struct union_t : public compound_operator, union_base { + using typename compound_operator::expressions_tuple; - /** - * Obtains a proxy 'xDestroy' function pointer [of type void(*)(void*)] - * for a deleter in a type-safe way. - * - * The deleter can be one of: - * - integral function constant - * - state-less (empty) deleter - * - non-capturing lambda - * - * Type-safety is garanteed by checking whether the deleter or yielded function pointer - * is invocable with the non-q-qualified pointer value. - */ - template - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept - requires(internal::needs_xdestroy_proxy) - { - return internal::xdestroy_proxy; - } + union_t(expressions_tuple compound, bool all) : + compound_operator{std::move(compound)}, union_base{all} {} + }; - /** - * Directly obtains a 'xDestroy' function pointer [of type void(*)(void*)] - * from a deleter in a type-safe way. - * - * The deleter can be one of: - * - function pointer of type xdestroy_fn_t - * - structure holding a function pointer - * - integral function constant - * - non-capturing lambda - * ... and yield a function pointer of type xdestroy_fn_t. - * - * Type-safety is garanteed by checking whether the deleter or yielded function pointer - * is invocable with the non-q-qualified pointer value. - */ - template - constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P* = nullptr) noexcept - requires(internal::yields_xdestroy) - { - return d; - } -#else - template, bool> = true> - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) { - static_assert(polyfill::always_false_v, - "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); - return nullptr; - } + struct except_string { + operator std::string() const { + return "EXCEPT"; + } + }; - template, bool> = true> - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept { - return internal::xdestroy_proxy; - } + /** + * EXCEPT object type. + */ + template + struct except_t : compound_operator, except_string { + using super = compound_operator; - template, bool> = true> - constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P* = nullptr) noexcept { - return d; - } -#endif -} + using super::super; + }; -// #include "pointer_value.h" + struct intersect_string { + operator std::string() const { + return "INTERSECT"; + } + }; + /** + * INTERSECT object type. + */ + template + struct intersect_t : compound_operator, intersect_string { + using super = compound_operator; + + using super::super; + }; + +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + /* + * Turn explicit columns for a CTE into types that the CTE backend understands + */ + template + struct decay_explicit_column { + using type = T; + }; + template + struct decay_explicit_column> { + using type = alias_holder; + }; + template + struct decay_explicit_column> { + using type = std::string; + }; + template + using decay_explicit_column_t = typename decay_explicit_column::type; -#if SQLITE_VERSION_NUMBER >= 3020000 -#include -#include -#include #ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include -#endif + /* + * Materialization hint to instruct SQLite to materialize the select statement of a CTE into an ephemeral table as an "optimization fence". + */ + struct materialized_t {}; + + /* + * Materialization hint to instruct SQLite to substitute a CTE's select statement as a subquery subject to optimization. + */ + struct not_materialized_t {}; #endif -// #include "functional/cstring_literal.h" + /** + * Monikered (aliased) CTE expression. + */ + template + struct common_table_expression { + using cte_moniker_type = Moniker; + using expression_type = Select; + using explicit_colrefs_tuple = ExplicitCols; + using hints_tuple = Hints; + static constexpr size_t explicit_colref_count = std::tuple_size_v; -// #include "xdestroy_handling.h" + SQLITE_ORM_NOUNIQUEADDRESS hints_tuple hints; + explicit_colrefs_tuple explicitColumns; + expression_type subselect; -#if SQLITE_VERSION_NUMBER >= 3020000 -namespace sqlite_orm { -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - namespace internal { - template - struct pointer_type { - using value_type = const char[sizeof...(C) + 1]; - static inline constexpr value_type value = {C..., '\0'}; + common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : + explicitColumns{std::move(explicitColumns)}, subselect{std::move(subselect)} { + this->subselect.highest_level = true; + } }; - } - inline namespace literals { - template - [[nodiscard]] consteval auto operator"" _pointer_type() { - return internal::explode_into(std::make_index_sequence{}); - } - } + template + using common_table_expressions = std::tuple; - /** @short Specifies that a type is an integral constant string usable as a pointer type. - */ - template - concept orm_pointer_type = requires { - typename T::value_type; - { T::value } -> std::convertible_to; - }; -#endif + template + struct cte_builder { + ExplicitCols explicitColumns; - /** - * Wraps a pointer and tags it with a pointer type, - * used for accepting function parameters, - * facilitating the 'pointer-passing interface'. - * - * Template parameters: - * - P: The value type, possibly const-qualified. - * - T: An integral constant string denoting the pointer type, e.g. `"carray"_pointer_type`. - * - */ - template - struct pointer_arg { +#if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) + template = true> + common_table_expression, Select> as(Select sel) && { + return {std::move(this->explicitColumns), std::move(sel)}; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - // note (internal): this is currently a static assertion instead of a type constraint because - // of forward declarations in other places (e.g. function.h) - static_assert(orm_pointer_type, "T must be a pointer type (tag)"); + template = true> + common_table_expression, select_t> + as(Compound sel) && { + return {std::move(this->explicitColumns), {std::move(sel)}}; + } #else - static_assert(std::is_convertible::value, - "The pointer type (tag) must be convertible to `const char*`"); + template = true> + common_table_expression, Select> as(Select sel) && { + return {std::move(this->explicitColumns), std::move(sel)}; + } + + template = true> + common_table_expression, select_t> as(Compound sel) && { + return {std::move(this->explicitColumns), {std::move(sel)}}; + } #endif + }; - using tag = T; - using qualified_type = P; + /** + * WITH object type - expression with prepended CTEs. + */ + template + struct with_t { + using cte_type = common_table_expressions; + using expression_type = E; - P* p_; + bool recursiveIndicated; + cte_type cte; + expression_type expression; - P* ptr() const noexcept { - return p_; + with_t(bool recursiveIndicated, cte_type cte, expression_type expression) : + recursiveIndicated{recursiveIndicated}, cte{std::move(cte)}, expression{std::move(expression)} { + if constexpr(is_select_v) { + this->expression.highest_level = true; + } + } + }; +#endif + + /** + * Generic way to get DISTINCT value from any type. + */ + template + bool get_distinct(const T&) { + return false; } - operator P*() const noexcept { - return p_; + template + bool get_distinct(const columns_t& cols) { + return cols.distinct; } - }; - /** - * Pointer value with associated deleter function, - * used for returning or binding pointer values - * as part of facilitating the 'pointer-passing interface'. - * - * Template parameters: - * - P: The value type, possibly const-qualified. - * - T: An integral constant string denoting the pointer type, e.g. `carray_pointer_type`. - * - D: The deleter for the pointer value; - * can be one of: - * - function pointer - * - integral function pointer constant - * - state-less (empty) deleter - * - non-capturing lambda - * - structure implicitly yielding a function pointer - * - * @note Use one of the factory functions to create a pointer binding, - * e.g. bindable_carray_pointer or statically_bindable_carray_pointer(). - * - * @example - * ``` - * int64 rememberedId; - * storage.select(func(&Object::id, statically_bindable_carray_pointer(&rememberedId))); - * ``` - */ - template - class pointer_binding { + template + bool get_distinct(const struct_t& cols) { + return cols.distinct; + } - P* p_; - SQLITE_ORM_NOUNIQUEADDRESS - D d_; + template + struct asterisk_t { + using type = T; - protected: - // Constructing pointer bindings must go through bind_pointer() - template - friend auto bind_pointer(P2*, D2) noexcept -> pointer_binding; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - // Constructing pointer bindings must go through bind_pointer() - template - friend auto bind_pointer(P2*, D2) noexcept -> pointer_binding; + bool defined_order = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + asterisk_t(bool definedOrder) : defined_order{definedOrder} {} #endif - template - friend B bind_pointer(typename B::qualified_type*, typename B::deleter_type) noexcept; + }; - // Construct from pointer and deleter. - // Transfers ownership of the passed in object. - pointer_binding(P* p, D d = {}) noexcept : p_{p}, d_{std::move(d)} {} + template + struct object_t { + using type = T; - public: - using qualified_type = P; - using tag = T; - using deleter_type = D; + bool defined_order = false; - pointer_binding(const pointer_binding&) = delete; - pointer_binding& operator=(const pointer_binding&) = delete; - pointer_binding& operator=(pointer_binding&&) = delete; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + object_t(bool definedOrder) : defined_order{definedOrder} {} +#endif + }; - pointer_binding(pointer_binding&& other) noexcept : - p_{std::exchange(other.p_, nullptr)}, d_{std::move(other.d_)} {} + template + struct then_t { + using expression_type = T; - ~pointer_binding() { - if(p_) { - if(auto xDestroy = get_xdestroy()) { - // note: C-casting `P* -> void*` like statement_binder> - xDestroy((void*)p_); - } + expression_type expression; + }; + + template + struct simple_case_t { + using return_type = R; + using case_expression_type = T; + using args_type = std::tuple; + using else_expression_type = E; + + optional_container case_expression; + args_type args; + optional_container else_expression; + }; + + /** + * T is a case expression type + * E is else type (void is ELSE is omitted) + * Args... is a pack of WHEN expressions + */ + template + struct simple_case_builder { + using return_type = R; + using case_expression_type = T; + using args_type = std::tuple; + using else_expression_type = E; + + optional_container case_expression; + args_type args; + optional_container else_expression; + + template + simple_case_builder> when(W w, then_t t) { + using result_args_type = std::tuple>; + std::pair newPair{std::move(w), std::move(t.expression)}; + result_args_type result_args = std::tuple_cat(std::move(this->args), std::make_tuple(newPair)); + std::get::value - 1>(result_args) = std::move(newPair); + return {std::move(this->case_expression), std::move(result_args), std::move(this->else_expression)}; + } + + simple_case_t end() { + return {std::move(this->case_expression), std::move(args), std::move(this->else_expression)}; + } + + template + simple_case_builder else_(El el) { + return {{std::move(this->case_expression)}, std::move(args), {std::move(el)}}; } - } + }; - P* ptr() const noexcept { - return p_; + template + void validate_conditions() { + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 LIMIT blocks"); + static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 FROM blocks"); } + } - P* take_ptr() noexcept { - return std::exchange(p_, nullptr); - } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + internal::as_optional_t as_optional(T value) { + return {std::move(value)}; + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + internal::then_t then(T t) { + return {std::move(t)}; + } - xdestroy_fn_t get_xdestroy() const noexcept { - return obtain_xdestroy_for(d_, p_); - } - }; + template + internal::simple_case_builder case_(T t) { + return {{std::move(t)}}; + } - /** - * Alias template for a static pointer value binding. - * 'Static' means that ownership won't be transferred to sqlite, - * sqlite doesn't delete it, and sqlite assumes the object - * pointed to is valid throughout the lifetime of a statement. - */ - template - using static_pointer_binding = pointer_binding; + template + internal::simple_case_builder case_() { + return {}; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - using pointer_arg_t = pointer_arg; + template + internal::distinct_t distinct(T t) { + return {std::move(t)}; + } - template - using pointer_binding_t = pointer_binding; + template + internal::all_t all(T t) { + return {std::move(t)}; + } - /** - * Alias template for a static pointer value binding. - * 'Static' means that ownership won't be transferred to sqlite, - * sqlite doesn't delete it, and sqlite assumes the object - * pointed to is valid throughout the lifetime of a statement. + template + internal::columns_t distinct(internal::columns_t cols) { + cols.distinct = true; + return cols; + } + + /* + * Combine multiple columns in a tuple. */ - template - using static_pointer_binding_t = pointer_binding_t; -#endif -} + template + constexpr internal::columns_t columns(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } -namespace sqlite_orm { - /** - * Wrap a pointer, its type and its deleter function for binding it to a statement. - * - * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object - * is transferred to the pointer binding, which will delete it through - * the deleter when the statement finishes. + /* + * Construct an unmapped structure ad-hoc from multiple columns. + * `T` must be constructible from the column results using direct-list-initialization. */ - template - auto bind_pointer(P* p, D d) noexcept -> pointer_binding { - return {p, std::move(d)}; + template + constexpr internal::struct_t struct_(Args... args) { + return {std::make_tuple(std::forward(args)...)}; } - template - auto bind_pointer(std::unique_ptr p) noexcept -> pointer_binding { - return bind_pointer(p.release(), p.get_deleter()); + /** + * Public function for subselect query. Is useful in UNION queries. + */ + template + internal::select_t select(T t, Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + return {std::move(t), {std::forward(args)...}}; } - template - auto bind_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept -> B { - return B{p, std::move(d)}; + /** + * Public function for UNION operator. + * Expressions are subselect objects. + * Look through example in examples/union.cpp + */ + template + internal::union_t union_(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); + return {{std::forward(expressions)...}, false}; } - template - [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding - bindable_pointer(P* p, D d) noexcept { - return bind_pointer(p, std::move(d)); + /** + * Public function for UNION ALL operator. + * Expressions are subselect objects. + * Look through example in examples/union.cpp + */ + template + internal::union_t union_all(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); + return {{std::forward(expressions)...}, true}; } - template - [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding - bindable_pointer(std::unique_ptr p) noexcept { - return bind_pointer(p.release(), p.get_deleter()); + /** + * Public function for EXCEPT operator. + * Expressions are subselect objects. + * Look through example in examples/except.cpp + */ + template + internal::except_t except(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); + return {{std::forward(expressions)...}}; } - template - [[deprecated("Use the better named function `bind_pointer(...)`")]] B - bindable_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept { - return bind_pointer(p, std::move(d)); + template + internal::intersect_t intersect(E... expressions) { + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); + return {{std::forward(expressions)...}}; } +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if SQLITE_VERSION_NUMBER >= 3035003 #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Wrap a pointer, its type (tag) and its deleter function for binding it to a statement. + /* + * Materialization hint to instruct SQLite to materialize the select statement of a CTE into an ephemeral table as an "optimization fence". * - * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object - * is transferred to the pointer binding, which will delete it through - * the deleter when the statement finishes. + * Example: + * 1_ctealias().as(select(1)); */ - template - auto bind_pointer(P* p, D d) noexcept -> pointer_binding { - return {p, std::move(d)}; + inline consteval internal::materialized_t materialized() { + return {}; } - template - auto bind_pointer(std::unique_ptr p) noexcept -> pointer_binding { - return bind_pointer(p.release(), p.get_deleter()); + /* + * Materialization hint to instruct SQLite to substitute a CTE's select statement as a subquery subject to optimization. + * + * Example: + * 1_ctealias().as(select(1)); + */ + inline consteval internal::not_materialized_t not_materialized() { + return {}; } +#endif #endif /** - * Wrap a pointer and its type for binding it to a statement. + * Introduce the construction of a common table expression using the specified moniker. * - * Note: 'Static' means that ownership of the pointed-to-object won't be transferred - * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + * The list of explicit columns is optional; + * if provided the number of columns must match the number of columns of the subselect. + * The column names will be merged with the subselect: + * 1. column names of subselect + * 2. explicit columns + * 3. fill in empty column names with column index + * + * Example: + * using cte_1 = decltype(1_ctealias); + * cte()(select(&Object::id)); + * cte(&Object::name)(select("object")); */ - template - auto bind_pointer_statically(P* p) noexcept -> static_pointer_binding { - return bind_pointer(p, null_xdestroy_f); + template, + std::is_member_pointer, + internal::is_column, + std::is_same>, + std::is_convertible>...>, + bool> = true> + auto cte(ExplicitCols... explicitColumns) { + using namespace ::sqlite_orm::internal; + static_assert(is_cte_moniker_v, "Moniker must be a CTE moniker"); + static_assert((!is_builtin_numeric_column_alias_v && ...), + "Numeric column aliases are reserved for referencing columns locally within a single CTE."); + + using builder_type = + cte_builder, decay_explicit_column_t>>; + return builder_type{{std::move(explicitColumns)...}}; } - template - B bind_pointer_statically(typename B::qualified_type* p, - typename B::deleter_type* /*exposition*/ = nullptr) noexcept { - return bind_pointer(p); +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + requires((internal::is_column_alias_v || std::is_member_pointer_v || + internal::is_column_v || + std::same_as> || + std::convertible_to) && + ...) + auto cte(ExplicitCols... explicitColumns) { + using namespace ::sqlite_orm::internal; + static_assert((!is_builtin_numeric_column_alias_v && ...), + "Numeric column aliases are reserved for referencing columns locally within a single CTE."); + + using builder_type = + cte_builder, decay_explicit_column_t>>; + return builder_type{{std::move(explicitColumns)...}}; } +#endif - template - [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] static_pointer_binding - statically_bindable_pointer(P* p) noexcept { - return bind_pointer(p, null_xdestroy_f); + namespace internal { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + template + requires((is_column_alias_v || std::is_member_pointer_v || + std::same_as> || + std::convertible_to) && + ...) + auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + return cte>(std::forward(explicitColumns)...); + } +#else + template + template, + std::is_member_pointer, + std::is_same>, + std::is_convertible>...>, + bool>> + auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + return cte>(std::forward(explicitColumns)...); + } +#endif } - template - [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] B - statically_bindable_pointer(typename B::qualified_type* p, - typename B::deleter_type* /*exposition*/ = nullptr) noexcept { - return bind_pointer(p); + /** + * With-clause for a tuple of ordinary CTEs. + * + * Despite the missing RECURSIVE keyword, the CTEs can be recursive. + */ + template = true> + internal::with_t with(internal::common_table_expressions ctes, E expression) { + return {false, std::move(ctes), std::move(expression)}; } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Wrap a pointer and its type (tag) for binding it to a statement. + /** + * With-clause for a tuple of ordinary CTEs. * - * Note: 'Static' means that ownership of the pointed-to-object won't be transferred - * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + * Despite the missing RECURSIVE keyword, the CTEs can be recursive. */ - template - auto bind_pointer_statically(P* p) noexcept -> static_pointer_binding { - return bind_pointer(p, null_xdestroy_f); + template = true> + internal::with_t, CTEs...> with(internal::common_table_expressions ctes, + Compound sel) { + return {false, std::move(ctes), sqlite_orm::select(std::move(sel))}; } -#endif - /** - * Forward a pointer value from an argument. + /** + * With-clause for a single ordinary CTE. + * + * Despite the missing `RECURSIVE` keyword, the CTE can be recursive. + * + * Example: + * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; + * with(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); */ - template - auto rebind_statically(const pointer_arg& pv) noexcept -> static_pointer_binding { - return bind_pointer_statically(pv.ptr()); + template = true, + internal::satisfies_not = true> + internal::with_t with(CTE cte, E expression) { + return {false, {std::move(cte)}, std::move(expression)}; } -} -#endif - -namespace sqlite_orm { - /** - * Helper class used for binding fields to sqlite3 statements. + /** + * With-clause for a single ordinary CTE. + * + * Despite the missing `RECURSIVE` keyword, the CTE can be recursive. + * + * Example: + * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; + * with(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); */ - template - struct statement_binder; - - namespace internal { - /* - * Implementation note: the technique of indirect expression testing is because - * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. - * It must also be a type that differs from those for `is_printable_v`, `is_preparable_v`. - */ - template - struct indirectly_test_bindable; - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_bindable_v = false; - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_bindable_v{})>>> = true; - - template - struct is_bindable : polyfill::bool_constant> {}; + template = true, + internal::satisfies = true> + internal::with_t, CTE> with(CTE cte, Compound sel) { + return {false, {std::move(cte)}, sqlite_orm::select(std::move(sel))}; } -#if SQLITE_VERSION_NUMBER >= 3020000 - /** - * Specialization for pointer bindings (part of the 'pointer-passing interface'). + /** + * With-clause for a tuple of potentially recursive CTEs. + * + * @note The use of RECURSIVE does not force common table expressions to be recursive. */ - template - struct statement_binder, void> { - using V = pointer_binding; - - // ownership of pointed-to-object is left untouched and remains at prepared statement's AST expression - int bind(sqlite3_stmt* stmt, int index, const V& value) const { - // note: C-casting `P* -> void*`, internal::xdestroy_proxy() does the inverse - return sqlite3_bind_pointer(stmt, index, (void*)value.ptr(), T::value, null_xdestroy_f); - } - - // ownership of pointed-to-object is transferred to sqlite - void result(sqlite3_context* context, V& value) const { - // note: C-casting `P* -> void*`, - // row_extractor>::extract() and internal::xdestroy_proxy() do the inverse - sqlite3_result_pointer(context, (void*)value.take_ptr(), T::value, value.get_xdestroy()); - } - }; -#endif + template = true> + internal::with_t with_recursive(internal::common_table_expressions ctes, E expression) { + return {true, std::move(ctes), std::move(expression)}; + } - /** - * Specialization for arithmetic types. + /** + * With-clause for a tuple of potentially recursive CTEs. + * + * @note The use of RECURSIVE does not force common table expressions to be recursive. */ - template - struct statement_binder> { - - int bind(sqlite3_stmt* stmt, int index, const V& value) const { - return this->bind(stmt, index, value, tag()); - } - - void result(sqlite3_context* context, const V& value) const { - this->result(context, value, tag()); - } - - private: - using tag = arithmetic_tag_t; - - int bind(sqlite3_stmt* stmt, int index, const V& value, int_or_smaller_tag) const { - return sqlite3_bind_int(stmt, index, static_cast(value)); - } - - void result(sqlite3_context* context, const V& value, int_or_smaller_tag) const { - sqlite3_result_int(context, static_cast(value)); - } - - int bind(sqlite3_stmt* stmt, int index, const V& value, bigint_tag) const { - return sqlite3_bind_int64(stmt, index, static_cast(value)); - } - - void result(sqlite3_context* context, const V& value, bigint_tag) const { - sqlite3_result_int64(context, static_cast(value)); - } - - int bind(sqlite3_stmt* stmt, int index, const V& value, real_tag) const { - return sqlite3_bind_double(stmt, index, static_cast(value)); - } - - void result(sqlite3_context* context, const V& value, real_tag) const { - sqlite3_result_double(context, static_cast(value)); - } - }; + template = true> + internal::with_t, CTEs...> with_recursive( + internal::common_table_expressions ctes, + Compound sel) { + return {true, std::move(ctes), sqlite_orm::select(std::move(sel))}; + } - /** - * Specialization for std::string and C-string. + /** + * With-clause for a single potentially recursive CTE. + * + * @note The use of RECURSIVE does not force common table expressions to be recursive. + * + * Example: + * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; + * with_recursive(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); */ - template - struct statement_binder, - std::is_same -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - , - std::is_same -#endif - >::value>> { - - int bind(sqlite3_stmt* stmt, int index, const V& value) const { - auto stringData = this->string_data(value); - return sqlite3_bind_text(stmt, index, stringData.first, stringData.second, SQLITE_TRANSIENT); - } - - void result(sqlite3_context* context, const V& value) const { - auto stringData = this->string_data(value); - auto dataCopy = new char[stringData.second + 1]; - constexpr auto deleter = std::default_delete{}; - ::strncpy(dataCopy, stringData.first, stringData.second + 1); - sqlite3_result_text(context, dataCopy, stringData.second, obtain_xdestroy_for(deleter, dataCopy)); - } - - private: -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - std::pair string_data(const std::string_view& s) const { - return {s.data(), int(s.size())}; - } -#else - std::pair string_data(const std::string& s) const { - return {s.c_str(), int(s.size())}; - } - - std::pair string_data(const char* s) const { - return {s, int(::strlen(s))}; - } -#endif - }; - -#ifndef SQLITE_ORM_OMITS_CODECVT - template - struct statement_binder, - std::is_same -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - , - std::is_same -#endif - >::value>> { - - int bind(sqlite3_stmt* stmt, int index, const V& value) const { - auto stringData = this->string_data(value); - std::wstring_convert> converter; - std::string utf8Str = converter.to_bytes(stringData.first, stringData.first + stringData.second); - return statement_binder().bind(stmt, index, utf8Str); - } - - void result(sqlite3_context* context, const V& value) const { - auto stringData = this->string_data(value); - sqlite3_result_text16(context, stringData.first, stringData.second, nullptr); - } - - private: -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - std::pair string_data(const std::wstring_view& s) const { - return {s.data(), int(s.size())}; - } -#else - std::pair string_data(const std::wstring& s) const { - return {s.c_str(), int(s.size())}; - } + template = true, + internal::satisfies_not = true> + internal::with_t with_recursive(CTE cte, E expression) { + return {true, {std::move(cte)}, std::move(expression)}; + } - std::pair string_data(const wchar_t* s) const { - return {s, int(::wcslen(s))}; - } -#endif - }; + /** + * With-clause for a single potentially recursive CTE. + * + * @note The use of RECURSIVE does not force common table expressions to be recursive. + * + * Example: + * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; + * with_recursive(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); + */ + template = true, + internal::satisfies = true> + internal::with_t, CTE> with_recursive(CTE cte, Compound sel) { + return {true, {std::move(cte)}, sqlite_orm::select(std::move(sel))}; + } #endif /** - * Specialization for nullptr_t. + * `SELECT * FROM T` expression that fetches results as tuples. + * T is a type mapped to a storage, or an alias of it. + * The `definedOrder` parameter denotes the expected order of result columns. + * The default is the implicit order as returned by SQLite, which may differ from the defined order + * if the schema of a table has been changed. + * By specifying the defined order, the columns are written out in the resulting select SQL string. + * + * In pseudo code: + * select(asterisk(false)) -> SELECT * from User + * select(asterisk(true)) -> SELECT id, name from User + * + * Example: auto rows = storage.select(asterisk()); + * // decltype(rows) is std::vector> + * Example: auto rows = storage.select(asterisk(true)); + * // decltype(rows) is std::vector> + * + * If you need to fetch results as objects instead of tuples please use `object()`. */ - template<> - struct statement_binder { - int bind(sqlite3_stmt* stmt, int index, const nullptr_t&) const { - return sqlite3_bind_null(stmt, index); - } - - void result(sqlite3_context* context, const nullptr_t&) const { - sqlite3_result_null(context); - } - }; + template + internal::asterisk_t asterisk(bool definedOrder = false) { + return {definedOrder}; + } -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Specialization for std::nullopt_t. + * Example: + * constexpr orm_table_alias auto m = "m"_alias.for_(); + * auto reportingTo = + * storage.select(asterisk(), inner_join(on(m->*&Employee::reportsTo == &Employee::employeeId))); */ - template<> - struct statement_binder { - int bind(sqlite3_stmt* stmt, int index, const std::nullopt_t&) const { - return sqlite3_bind_null(stmt, index); - } - - void result(sqlite3_context* context, const std::nullopt_t&) const { - sqlite3_result_null(context); - } - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - - template - struct statement_binder< - V, - std::enable_if_t::value && - internal::is_bindable>::value>> { - using unqualified_type = std::remove_cv_t; - - int bind(sqlite3_stmt* stmt, int index, const V& value) const { - if(value) { - return statement_binder().bind(stmt, index, *value); - } else { - return statement_binder().bind(stmt, index, nullptr); - } - } - }; + template + auto asterisk(bool definedOrder = false) { + return asterisk>(definedOrder); + } +#endif /** - * Specialization for binary data (std::vector). + * `SELECT * FROM T` expression that fetches results as objects of type T. + * T is a type mapped to a storage, or an alias of it. + * + * Example: auto rows = storage.select(object()); + * // decltype(rows) is std::vector, where the User objects are constructed from columns in implicitly stored order + * Example: auto rows = storage.select(object(true)); + * // decltype(rows) is std::vector, where the User objects are constructed from columns in declared make_table order + * + * If you need to fetch results as tuples instead of objects please use `asterisk()`. */ - template<> - struct statement_binder, void> { - int bind(sqlite3_stmt* stmt, int index, const std::vector& value) const { - if(!value.empty()) { - return sqlite3_bind_blob(stmt, index, (const void*)&value.front(), int(value.size()), SQLITE_TRANSIENT); - } else { - return sqlite3_bind_blob(stmt, index, "", 0, SQLITE_TRANSIENT); - } - } + template + internal::object_t object(bool definedOrder = false) { + return {definedOrder}; + } - void result(sqlite3_context* context, const std::vector& value) const { - if(!value.empty()) { - sqlite3_result_blob(context, (const void*)&value.front(), int(value.size()), nullptr); - } else { - sqlite3_result_blob(context, "", 0, nullptr); - } - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto object(bool definedOrder = false) { + return object>(definedOrder); + } +#endif +} -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct statement_binder && - internal::is_bindable_v>>> { - using unqualified_type = std::remove_cv_t; +// #include "core_functions.h" - int bind(sqlite3_stmt* stmt, int index, const V& value) const { - if(value) { - return statement_binder().bind(stmt, index, *value); - } else { - return statement_binder().bind(stmt, index, std::nullopt); - } - } - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "conditions.h" - namespace internal { +// #include "statement_binder.h" - struct conditional_binder { - sqlite3_stmt* stmt = nullptr; - int index = 1; +#include +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence +#include // std::default_delete +#include // std::string, std::wstring +#include // std::vector +#include // std::strncpy, std::strlen +// #include "functional/cxx_string_view.h" - explicit conditional_binder(sqlite3_stmt* stmt) : stmt{stmt} {} +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // ::wcsncpy, ::wcslen +#endif +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert +#include // std::codecvt_utf8_utf16 +#endif - template = true> - void operator()(const T& t) { - int rc = statement_binder{}.bind(this->stmt, this->index++, t); - if(SQLITE_OK != rc) { - throw_translated_sqlite_error(this->stmt); - } - } +// #include "functional/cxx_universal.h" - template = true> - void operator()(const T&) const {} - }; +// #include "functional/cxx_type_traits_polyfill.h" - struct field_value_binder : conditional_binder { - using conditional_binder::conditional_binder; - using conditional_binder::operator(); +// #include "functional/cxx_functional_polyfill.h" - template = true> - void operator()(const T&) const = delete; +// #include "is_std_ptr.h" - template - void operator()(const T* value) { - if(!value) { - throw std::system_error{orm_error_code::value_is_null}; - } - (*this)(*value); - } - }; +// #include "tuple_helper/tuple_filter.h" - struct tuple_value_binder { - sqlite3_stmt* stmt = nullptr; +// #include "type_traits.h" - explicit tuple_value_binder(sqlite3_stmt* stmt) : stmt{stmt} {} +// #include "error_code.h" - template - void operator()(const Tpl& tpl, Projection project) const { - (*this)(tpl, - std::make_index_sequence::value>{}, - std::forward(project)); - } +// #include "arithmetic_tag.h" - private: -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED - template - void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { - (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); - } -#else - template - void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { - using Sink = int[sizeof...(Idx)]; - (void)Sink{(this->bind(polyfill::invoke(project, std::get(tpl)), Idx), 0)...}; - } -#endif +#include // std::is_integral - template - void bind(const T& t, size_t idx) const { - int rc = statement_binder{}.bind(this->stmt, int(idx + 1), t); - if(SQLITE_OK != rc) { - throw_translated_sqlite_error(this->stmt); - } - } +// #include "functional/mpl/conditional.h" - template - void bind(const T* value, size_t idx) const { - if(!value) { - throw std::system_error{orm_error_code::value_is_null}; - } - (*this)(*value, idx); - } - }; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - template - using bindable_filter_t = filter_tuple_t; - } + /** + * Helper classes used by statement_binder and row_extractor. + */ + struct int_or_smaller_tag {}; + struct bigint_tag {}; + struct real_tag {}; + + template + using arithmetic_tag_t = + mpl::conditional_t::value, + // Integer class + mpl::conditional_t, + // Floating-point class + real_tag>; } -#pragma once -#include -#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if -#include // atof, atoi, atoll -#include // std::system_error -#include // std::string, std::wstring -#ifndef SQLITE_ORM_OMITS_CODECVT -#include // std::wstring_convert -#include // std::codecvt_utf8_utf16 -#endif -#include // std::vector -#include // strlen -#include // std::copy -#include // std::back_inserter -#include // std::tuple, std::tuple_size, std::tuple_element +// #include "xdestroy_handling.h" + +#include // std::integral_constant #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include #endif // #include "functional/cxx_universal.h" -// #include "functional/cxx_functional_polyfill.h" +// #include "functional/cxx_type_traits_polyfill.h" -// #include "functional/static_magic.h" +_EXPORT_SQLITE_ORM namespace sqlite_orm { -#ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#include // std::false_type, std::true_type, std::integral_constant + using xdestroy_fn_t = void (*)(void*); + using null_xdestroy_t = std::integral_constant; + SQLITE_ORM_INLINE_VAR constexpr null_xdestroy_t null_xdestroy_f{}; +} + +_EXPORT_SQLITE_ORM namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_CONCEPTS_SUPPORTED + /** + * Constrains a deleter to be state-less. + */ + template + concept stateless_deleter = std::is_empty_v && std::is_default_constructible_v; + + /** + * Constrains a deleter to be an integral function constant. + */ + template + concept integral_fp_c = requires { + typename D::value_type; + D::value; + requires std::is_function_v>; + }; + + /** + * Constrains a deleter to be or to yield a function pointer. + */ + template + concept yields_fp = requires(D d) { + // yielding function pointer by using the plus trick + { +d }; + requires std::is_function_v>; + }; #endif -#include // std::forward -namespace sqlite_orm { +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + /** + * Yield a deleter's function pointer. + */ + template + struct yield_fp_of { + using type = decltype(+std::declval()); + }; +#else - // got from here - // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co - namespace internal { + template + SQLITE_ORM_INLINE_VAR constexpr bool is_stateless_deleter_v = + std::is_empty::value && std::is_default_constructible::value; - // note: this is a class template accompanied with a variable template because older compilers (e.g. VC 2017) - // cannot handle a static lambda variable inside a template function - template - struct empty_callable_t { - template - R operator()(Args&&...) const { - return R(); - } + template + struct is_integral_fp_c : std::false_type {}; + template + struct is_integral_fp_c< + D, + polyfill::void_t>::value>>> + : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_integral_fp_c_v = is_integral_fp_c::value; + + template + struct can_yield_fp : std::false_type {}; + template + struct can_yield_fp< + D, + polyfill::void_t< + decltype(+std::declval()), + std::enable_if_t())>>::value>>> + : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool can_yield_fp_v = can_yield_fp::value; + + template> + struct yield_fp_of { + using type = void; }; - template - constexpr empty_callable_t empty_callable{}; + template + struct yield_fp_of { + using type = decltype(+std::declval()); + }; +#endif + template + using yielded_fn_t = typename yield_fp_of::type; -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED - template - decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { - if constexpr(B) { - return std::forward(trueFn); - } else { - return std::forward(falseFn); - } - } +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept is_unusable_for_xdestroy = + (!stateless_deleter && (yields_fp && !std::convertible_to, xdestroy_fn_t>)); - template - decltype(auto) static_if([[maybe_unused]] T&& trueFn) { - if constexpr(B) { - return std::forward(trueFn); - } else { - return empty_callable<>; - } + /** + * This concept tests whether a deleter yields a function pointer, which is convertible to an xdestroy function pointer. + * Note: We are using 'is convertible' rather than 'is same' because of any exception specification. + */ + template + concept yields_xdestroy = yields_fp && std::convertible_to, xdestroy_fn_t>; + + template + concept needs_xdestroy_proxy = + (stateless_deleter && (!yields_fp || !std::convertible_to, xdestroy_fn_t>)); + + /** + * xDestroy function that constructs and invokes the stateless deleter. + * + * Requires that the deleter can be called with the q-qualified pointer argument; + * it doesn't check so explicitly, but a compiler error will occur. + */ + template + requires(!integral_fp_c) + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder> + auto o = (P*)p; + // ignoring return code + (void)D{}(o); } - template - void call_if_constexpr([[maybe_unused]] L&& lambda, [[maybe_unused]] Args&&... args) { - if constexpr(B) { - lambda(std::forward(args)...); - } + /** + * xDestroy function that invokes the integral function pointer constant. + * + * Performs a const-cast of the argument pointer in order to allow for C API functions + * that take a non-const parameter, but user code passes a pointer to a const object. + */ + template + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder>, + auto o = (std::remove_cv_t

*)(P*)p; + // ignoring return code + (void)D{}(o); } #else - template - decltype(auto) static_if(std::true_type, T&& trueFn, const F&) { - return std::forward(trueFn); - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_unusable_for_xdestroy_v = + !is_stateless_deleter_v && + (can_yield_fp_v && !std::is_convertible, xdestroy_fn_t>::value); - template - decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { - return std::forward(falseFn); - } + template + SQLITE_ORM_INLINE_VAR constexpr bool can_yield_xdestroy_v = + can_yield_fp_v && std::is_convertible, xdestroy_fn_t>::value; - template - decltype(auto) static_if(T&& trueFn, F&& falseFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); + template + SQLITE_ORM_INLINE_VAR constexpr bool needs_xdestroy_proxy_v = + is_stateless_deleter_v && + (!can_yield_fp_v || !std::is_convertible, xdestroy_fn_t>::value); + + template, bool> = true> + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder> + auto o = (P*)p; + // ignoring return code + (void)D{}(o); } - template - decltype(auto) static_if(T&& trueFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable<>); + template, bool> = true> + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder>, + auto o = (std::remove_cv_t

*)(P*)p; + // ignoring return code + (void)D{}(o); } +#endif + } +} + +_EXPORT_SQLITE_ORM namespace sqlite_orm { + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + /** + * Prohibits using a yielded function pointer, which is not of type xdestroy_fn_t. + * + * Explicitly declared for better error messages. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept + requires(internal::is_unusable_for_xdestroy) + { + static_assert(polyfill::always_false_v, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } + + /** + * Obtains a proxy 'xDestroy' function pointer [of type void(*)(void*)] + * for a deleter in a type-safe way. + * + * The deleter can be one of: + * - integral function constant + * - state-less (empty) deleter + * - non-capturing lambda + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept + requires(internal::needs_xdestroy_proxy) + { + return internal::xdestroy_proxy; + } + + /** + * Directly obtains a 'xDestroy' function pointer [of type void(*)(void*)] + * from a deleter in a type-safe way. + * + * The deleter can be one of: + * - function pointer of type xdestroy_fn_t + * - structure holding a function pointer + * - integral function constant + * - non-capturing lambda + * ... and yield a function pointer of type xdestroy_fn_t. + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P* = nullptr) noexcept + requires(internal::yields_xdestroy) + { + return d; + } +#else + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) { + static_assert(polyfill::always_false_v, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } - template - void call_if_constexpr(L&& lambda, Args&&... args) { - static_if(std::forward(lambda))(std::forward(args)...); - } -#endif + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept { + return internal::xdestroy_proxy; } + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P* = nullptr) noexcept { + return d; + } +#endif } -// #include "tuple_helper/tuple_transformer.h" +// #include "pointer_value.h" -// #include "column_result_proxy.h" +#if SQLITE_VERSION_NUMBER >= 3020000 +#include +#include +#include +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include +#endif +#endif -// #include "type_traits.h" +// #include "functional/cstring_literal.h" -// #include "table_reference.h" +// #include "xdestroy_handling.h" -namespace sqlite_orm { +#if SQLITE_VERSION_NUMBER >= 3020000 +_EXPORT_SQLITE_ORM namespace sqlite_orm { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES namespace internal { - - /* - * Holder for the type of an unmapped aggregate/structure/object to be constructed ad-hoc from column results. - * `T` must be constructible using direct-list-initialization. - */ - template - struct structure { - using type = T; + template + struct pointer_type { + using value_type = const char[sizeof...(C) + 1]; + static inline constexpr value_type value = {C..., '\0'}; }; } -} -namespace sqlite_orm { - namespace internal { + inline namespace literals { + template + [[nodiscard]] consteval auto operator"" _pointer_type() { + return internal::explode_into(std::make_index_sequence{}); + } + } - template - struct column_result_proxy : std::remove_const {}; + /** @short Specifies that a type is an integral constant string usable as a pointer type. + */ + template + concept orm_pointer_type = requires { + typename T::value_type; + { T::value } -> std::convertible_to; + }; +#endif - /* - * Unwrap `table_reference` - */ - template - struct column_result_proxy> : decay_table_ref

{}; + /** + * Wraps a pointer and tags it with a pointer type, + * used for accepting function parameters, + * facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - P: The value type, possibly const-qualified. + * - T: An integral constant string denoting the pointer type, e.g. `"carray"_pointer_type`. + * + */ + template + struct pointer_arg { - /* - * Pass through `structure` - */ - template - struct column_result_proxy> : P {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + // note (internal): this is currently a static assertion instead of a type constraint because + // of forward declarations in other places (e.g. function.h) + static_assert(orm_pointer_type, "T must be a pointer type (tag)"); +#else + static_assert(std::is_convertible::value, + "The pointer type (tag) must be convertible to `const char*`"); +#endif - template - using column_result_proxy_t = typename column_result_proxy::type; - } -} + using tag = T; + using qualified_type = P; -// #include "arithmetic_tag.h" + P* p_; -// #include "pointer_value.h" + P* ptr() const noexcept { + return p_; + } -// #include "journal_mode.h" + operator P*() const noexcept { + return p_; + } + }; -#include // std::back_inserter -#include // std::string -#include // std::unique_ptr -#include // std::array -#include // std::transform -#include // std::toupper + /** + * Pointer value with associated deleter function, + * used for returning or binding pointer values + * as part of facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - P: The value type, possibly const-qualified. + * - T: An integral constant string denoting the pointer type, e.g. `carray_pointer_type`. + * - D: The deleter for the pointer value; + * can be one of: + * - function pointer + * - integral function pointer constant + * - state-less (empty) deleter + * - non-capturing lambda + * - structure implicitly yielding a function pointer + * + * @note Use one of the factory functions to create a pointer binding, + * e.g. bindable_carray_pointer or statically_bindable_carray_pointer(). + * + * @example + * ``` + * int64 rememberedId; + * storage.select(func(&Object::id, statically_bindable_carray_pointer(&rememberedId))); + * ``` + */ + template + class pointer_binding { -#if defined(_WINNT_) -// DELETE is a macro defined in the Windows SDK (winnt.h) -#pragma push_macro("DELETE") -#undef DELETE + P* p_; + SQLITE_ORM_NOUNIQUEADDRESS + D d_; + + protected: + // Constructing pointer bindings must go through bind_pointer() + template + friend auto bind_pointer(P2*, D2) noexcept -> pointer_binding; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + // Constructing pointer bindings must go through bind_pointer() + template + friend auto bind_pointer(P2*, D2) noexcept -> pointer_binding; #endif + template + friend B bind_pointer(typename B::qualified_type*, typename B::deleter_type) noexcept; -namespace sqlite_orm { + // Construct from pointer and deleter. + // Transfers ownership of the passed in object. + pointer_binding(P* p, D d = {}) noexcept : p_{p}, d_{std::move(d)} {} - /** - * Caps case because of: - * 1) delete keyword; - * 2) https://www.sqlite.org/pragma.html#pragma_journal_mode original spelling - */ - enum class journal_mode : signed char { - DELETE = 0, - // An alternate enumeration value when using the Windows SDK that defines DELETE as a macro. - DELETE_ = DELETE, - TRUNCATE = 1, - PERSIST = 2, - MEMORY = 3, - WAL = 4, - OFF = 5, - }; + public: + using qualified_type = P; + using tag = T; + using deleter_type = D; - namespace internal { + pointer_binding(const pointer_binding&) = delete; + pointer_binding& operator=(const pointer_binding&) = delete; + pointer_binding& operator=(pointer_binding&&) = delete; - inline const std::string& to_string(journal_mode j) { - static std::string res[] = { - "DELETE", - "TRUNCATE", - "PERSIST", - "MEMORY", - "WAL", - "OFF", - }; - return res[static_cast(j)]; - } + pointer_binding(pointer_binding&& other) noexcept : + p_{std::exchange(other.p_, nullptr)}, d_{std::move(other.d_)} {} - inline std::unique_ptr journal_mode_from_string(const std::string& str) { - std::string upper_str; - std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) { - return static_cast(std::toupper(static_cast(c))); - }); - static std::array all = {{ - journal_mode::DELETE, - journal_mode::TRUNCATE, - journal_mode::PERSIST, - journal_mode::MEMORY, - journal_mode::WAL, - journal_mode::OFF, - }}; - for(auto j: all) { - if(to_string(j) == upper_str) { - return std::make_unique(j); + ~pointer_binding() { + if(p_) { + if(auto xDestroy = get_xdestroy()) { + // note: C-casting `P* -> void*` like statement_binder> + xDestroy((void*)p_); } } - return {}; } - } -} -#if defined(_WINNT_) -#pragma pop_macro("DELETE") + P* ptr() const noexcept { + return p_; + } + + P* take_ptr() noexcept { + return std::exchange(p_, nullptr); + } + + xdestroy_fn_t get_xdestroy() const noexcept { + return obtain_xdestroy_for(d_, p_); + } + }; + + /** + * Alias template for a static pointer value binding. + * 'Static' means that ownership won't be transferred to sqlite, + * sqlite doesn't delete it, and sqlite assumes the object + * pointed to is valid throughout the lifetime of a statement. + */ + template + using static_pointer_binding = pointer_binding; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using pointer_arg_t = pointer_arg; + + template + using pointer_binding_t = pointer_binding; + + /** + * Alias template for a static pointer value binding. + * 'Static' means that ownership won't be transferred to sqlite, + * sqlite doesn't delete it, and sqlite assumes the object + * pointed to is valid throughout the lifetime of a statement. + */ + template + using static_pointer_binding_t = pointer_binding_t; #endif +} -// #include "error_code.h" +_EXPORT_SQLITE_ORM namespace sqlite_orm { + /** + * Wrap a pointer, its type and its deleter function for binding it to a statement. + * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. + */ + template + auto bind_pointer(P * p, D d) noexcept -> pointer_binding { + return {p, std::move(d)}; + } -// #include "is_std_ptr.h" + template + auto bind_pointer(std::unique_ptr p) noexcept -> pointer_binding { + return bind_pointer(p.release(), p.get_deleter()); + } -// #include "type_traits.h" + template + auto bind_pointer(typename B::qualified_type * p, typename B::deleter_type d = {}) noexcept -> B { + return B{p, std::move(d)}; + } -namespace sqlite_orm { + template + [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding bindable_pointer( + P * p, + D d) noexcept { + return bind_pointer(p, std::move(d)); + } + + template + [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding bindable_pointer( + std::unique_ptr p) noexcept { + return bind_pointer(p.release(), p.get_deleter()); + } + + template + [[deprecated("Use the better named function `bind_pointer(...)`")]] B bindable_pointer( + typename B::qualified_type * p, + typename B::deleter_type d = {}) noexcept { + return bind_pointer(p, std::move(d)); + } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Helper for casting values originating from SQL to C++ typed values, usually from rows of a result set. - * - * sqlite_orm provides specializations for known C++ types, users may define their custom specialization - * of this helper. - * - * @note (internal): Since row extractors are used in certain contexts with only one purpose at a time - * (e.g., converting a row result set but not function values or column text), - * there are factory functions that perform conceptual checking that should be used - * instead of directly creating row extractors. - * + * Wrap a pointer, its type (tag) and its deleter function for binding it to a statement. * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. */ - template - struct row_extractor { - /* - * Called during one-step query execution (one result row) for each column of a result row. - */ - V extract(const char* columnText) const = delete; + template + auto bind_pointer(P * p, D d) noexcept -> pointer_binding { + return {p, std::move(d)}; + } - /* - * Called during multi-step query execution (result set) for each column of a result row. - */ - V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; + template + auto bind_pointer(std::unique_ptr p) noexcept -> pointer_binding { + return bind_pointer(p.release(), p.get_deleter()); + } +#endif - /* - * Called before invocation of user-defined scalar or aggregate functions, - * in order to unbox dynamically typed SQL function values into a tuple of C++ function arguments. - */ - V extract(sqlite3_value* value) const = delete; - }; + /** + * Wrap a pointer and its type for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template + auto bind_pointer_statically(P * p) noexcept -> static_pointer_binding { + return bind_pointer(p, null_xdestroy_f); + } -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - template - concept orm_column_text_extractable = requires(const row_extractor& extractor, const char* columnText) { - { extractor.extract(columnText) } -> std::same_as; - }; + template + B bind_pointer_statically(typename B::qualified_type * p, + typename B::deleter_type* /*exposition*/ = nullptr) noexcept { + return bind_pointer(p); + } - template - concept orm_row_value_extractable = - requires(const row_extractor& extractor, sqlite3_stmt* stmt, int columnIndex) { - { extractor.extract(stmt, columnIndex) } -> std::same_as; - }; + template + [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] static_pointer_binding + statically_bindable_pointer(P * p) noexcept { + return bind_pointer(p, null_xdestroy_f); + } - template - concept orm_boxed_value_extractable = requires(const row_extractor& extractor, sqlite3_value* value) { - { extractor.extract(value) } -> std::same_as; - }; -#endif + template + [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] B statically_bindable_pointer( + typename B::qualified_type * p, + typename B::deleter_type* /*exposition*/ = nullptr) noexcept { + return bind_pointer(p); + } - namespace internal { - /* - * Make a row extractor to be used for casting SQL column text to a C++ typed value. - */ - template -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - requires(orm_column_text_extractable) +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Wrap a pointer and its type (tag) for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template + auto bind_pointer_statically(P * p) noexcept -> static_pointer_binding { + return bind_pointer(p, null_xdestroy_f); + } #endif - row_extractor column_text_extractor() { - return {}; - } - /* - * Make a row extractor to be used for converting a value from a SQL result row set to a C++ typed value. - */ - template -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - requires(orm_row_value_extractable) + /** + * Forward a pointer value from an argument. + */ + template + auto rebind_statically(const pointer_arg& pv) noexcept -> static_pointer_binding { + return bind_pointer_statically(pv.ptr()); + } +} #endif - row_extractor row_value_extractor() { - return {}; - } - /* - * Make a row extractor to be used for unboxing a dynamically typed SQL value to a C++ typed value. +_EXPORT_SQLITE_ORM namespace sqlite_orm { + + /** + * Helper class used for binding fields to sqlite3 statements. + */ + template + struct statement_binder; + + namespace internal { + /* + * Implementation note: the technique of indirect expression testing is because + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + * It must also be a type that differs from those for `is_printable_v`, `is_preparable_v`. */ - template -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - requires(orm_boxed_value_extractable) -#endif - row_extractor boxed_value_extractor() { - return {}; - } - } + template + struct indirectly_test_bindable; - template - int extract_single_value(void* data, int argc, char** argv, char**) { - auto& res = *(R*)data; - if(argc) { - const auto rowExtractor = internal::column_text_extractor(); - res = rowExtractor.extract(argv[0]); - } - return 0; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_bindable_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_bindable_v{})>>> = true; + + template + struct is_bindable : polyfill::bool_constant> {}; } #if SQLITE_VERSION_NUMBER >= 3020000 /** - * Specialization for the 'pointer-passing interface'. - * - * @note The 'pointer-passing' interface doesn't support (and in fact prohibits) - * extracting pointers from columns. + * Specialization for pointer bindings (part of the 'pointer-passing interface'). */ - template - struct row_extractor, void> { - using V = pointer_arg; - - V extract(const char* columnText) const = delete; + template + struct statement_binder, void> { + using V = pointer_binding; - V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; + // ownership of pointed-to-object is left untouched and remains at prepared statement's AST expression + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + // note: C-casting `P* -> void*`, internal::xdestroy_proxy() does the inverse + return sqlite3_bind_pointer(stmt, index, (void*)value.ptr(), T::value, null_xdestroy_f); + } - V extract(sqlite3_value* value) const { - return {(P*)sqlite3_value_pointer(value, T::value)}; + // ownership of pointed-to-object is transferred to sqlite + void result(sqlite3_context* context, V& value) const { + // note: C-casting `P* -> void*`, + // row_extractor>::extract() and internal::xdestroy_proxy() do the inverse + sqlite3_result_pointer(context, (void*)value.take_ptr(), T::value, value.get_xdestroy()); } }; - - /** - * Undefine using pointer_binding<> for querying values - */ - template - struct row_extractor, void>; #endif /** * Specialization for arithmetic types. */ template - struct row_extractor::value>> { - V extract(const char* columnText) const { - return this->extract(columnText, tag()); - } - - V extract(sqlite3_stmt* stmt, int columnIndex) const { - return this->extract(stmt, columnIndex, tag()); - } - - V extract(sqlite3_value* value) const { - return this->extract(value, tag()); - } - - private: - using tag = arithmetic_tag_t; - - V extract(const char* columnText, const int_or_smaller_tag&) const { - return static_cast(atoi(columnText)); - } + struct statement_binder> { - V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { - return static_cast(sqlite3_column_int(stmt, columnIndex)); + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + return this->bind(stmt, index, value, tag()); } - V extract(sqlite3_value* value, const int_or_smaller_tag&) const { - return static_cast(sqlite3_value_int(value)); + void result(sqlite3_context* context, const V& value) const { + this->result(context, value, tag()); } - V extract(const char* columnText, const bigint_tag&) const { - return static_cast(atoll(columnText)); + private: + using tag = arithmetic_tag_t; + + int bind(sqlite3_stmt* stmt, int index, const V& value, int_or_smaller_tag) const { + return sqlite3_bind_int(stmt, index, static_cast(value)); } - V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { - return static_cast(sqlite3_column_int64(stmt, columnIndex)); + void result(sqlite3_context* context, const V& value, int_or_smaller_tag) const { + sqlite3_result_int(context, static_cast(value)); } - V extract(sqlite3_value* value, const bigint_tag&) const { - return static_cast(sqlite3_value_int64(value)); + int bind(sqlite3_stmt* stmt, int index, const V& value, bigint_tag) const { + return sqlite3_bind_int64(stmt, index, static_cast(value)); } - V extract(const char* columnText, const real_tag&) const { - return static_cast(atof(columnText)); + void result(sqlite3_context* context, const V& value, bigint_tag) const { + sqlite3_result_int64(context, static_cast(value)); } - V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { - return static_cast(sqlite3_column_double(stmt, columnIndex)); + int bind(sqlite3_stmt* stmt, int index, const V& value, real_tag) const { + return sqlite3_bind_double(stmt, index, static_cast(value)); } - V extract(sqlite3_value* value, const real_tag&) const { - return static_cast(sqlite3_value_double(value)); + void result(sqlite3_context* context, const V& value, real_tag) const { + sqlite3_result_double(context, static_cast(value)); } }; /** - * Specialization for std::string. + * Specialization for std::string and C-string. */ - template - struct row_extractor::value>> { - T extract(const char* columnText) const { - if(columnText) { - return columnText; - } else { - return {}; - } - } + template + struct statement_binder, + std::is_same +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same +#endif + >::value>> { - T extract(sqlite3_stmt* stmt, int columnIndex) const { - if(auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex)) { - return cStr; - } else { - return {}; - } + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); + return sqlite3_bind_text(stmt, index, stringData.first, stringData.second, SQLITE_TRANSIENT); } - T extract(sqlite3_value* value) const { - if(auto cStr = (const char*)sqlite3_value_text(value)) { - return cStr; - } else { - return {}; - } - } - }; -#ifndef SQLITE_ORM_OMITS_CODECVT - /** - * Specialization for std::wstring. - */ - template<> - struct row_extractor { - std::wstring extract(const char* columnText) const { - if(columnText) { - std::wstring_convert> converter; - return converter.from_bytes(columnText); - } else { - return {}; - } + void result(sqlite3_context* context, const V& value) const { + auto stringData = this->string_data(value); + auto dataCopy = new char[stringData.second + 1]; + constexpr auto deleter = std::default_delete{}; + std::strncpy(dataCopy, stringData.first, stringData.second + 1); + sqlite3_result_text(context, dataCopy, stringData.second, obtain_xdestroy_for(deleter, dataCopy)); } - std::wstring extract(sqlite3_stmt* stmt, int columnIndex) const { - auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); - if(cStr) { - std::wstring_convert> converter; - return converter.from_bytes(cStr); - } else { - return {}; - } + private: +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::pair string_data(const std::string_view& s) const { + return {s.data(), int(s.size())}; + } +#else + std::pair string_data(const std::string& s) const { + return {s.c_str(), int(s.size())}; } - std::wstring extract(sqlite3_value* value) const { - if(auto cStr = (const wchar_t*)sqlite3_value_text16(value)) { - return cStr; - } else { - return {}; - } + std::pair string_data(const char* s) const { + return {s, int(std::strlen(s))}; } +#endif }; -#endif // SQLITE_ORM_OMITS_CODECVT +#ifndef SQLITE_ORM_OMITS_CODECVT template - struct row_extractor::value>> { - using unqualified_type = std::remove_cv_t; - - V extract(const char* columnText) const -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - requires(orm_column_text_extractable) + struct statement_binder, + std::is_same +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same #endif - { - if(columnText) { - const row_extractor rowExtractor{}; - return is_std_ptr::make(rowExtractor.extract(columnText)); - } else { - return {}; - } - } + >::value>> { - V extract(sqlite3_stmt* stmt, int columnIndex) const -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - requires(orm_row_value_extractable) -#endif - { - auto type = sqlite3_column_type(stmt, columnIndex); - if(type != SQLITE_NULL) { - const row_extractor rowExtractor{}; - return is_std_ptr::make(rowExtractor.extract(stmt, columnIndex)); - } else { - return {}; - } + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); + std::wstring_convert> converter; + std::string utf8Str = converter.to_bytes(stringData.first, stringData.first + stringData.second); + return statement_binder().bind(stmt, index, utf8Str); } - V extract(sqlite3_value* value) const -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - requires(orm_boxed_value_extractable) -#endif - { - auto type = sqlite3_value_type(value); - if(type != SQLITE_NULL) { - const row_extractor rowExtractor{}; - return is_std_ptr::make(rowExtractor.extract(value)); - } else { - return {}; - } + void result(sqlite3_context* context, const V& value) const { + auto stringData = this->string_data(value); + sqlite3_result_text16(context, stringData.first, stringData.second, nullptr); } - }; - -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct row_extractor>> { - using unqualified_type = std::remove_cv_t; - V extract(const char* columnText) const -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - requires(orm_column_text_extractable) -#endif - { - if(columnText) { - const row_extractor rowExtractor{}; - return std::make_optional(rowExtractor.extract(columnText)); - } else { - return std::nullopt; - } + private: +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::pair string_data(const std::wstring_view& s) const { + return {s.data(), int(s.size())}; } - - V extract(sqlite3_stmt* stmt, int columnIndex) const -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - requires(orm_row_value_extractable) -#endif - { - auto type = sqlite3_column_type(stmt, columnIndex); - if(type != SQLITE_NULL) { - const row_extractor rowExtractor{}; - return std::make_optional(rowExtractor.extract(stmt, columnIndex)); - } else { - return std::nullopt; - } +#else + std::pair string_data(const std::wstring& s) const { + return {s.c_str(), int(s.size())}; } - V extract(sqlite3_value* value) const -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - requires(orm_boxed_value_extractable) -#endif - { - auto type = sqlite3_value_type(value); - if(type != SQLITE_NULL) { - const row_extractor rowExtractor{}; - return std::make_optional(rowExtractor.extract(value)); - } else { - return std::nullopt; - } + std::pair string_data(const wchar_t* s) const { + return {s, int(::wcslen(s))}; } +#endif }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#endif + /** + * Specialization for nullptr_t. + */ template<> - struct row_extractor { - nullptr_t extract(const char* /*columnText*/) const { - return nullptr; - } - - nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { - return nullptr; + struct statement_binder { + int bind(sqlite3_stmt* stmt, int index, const nullptr_t&) const { + return sqlite3_bind_null(stmt, index); } - nullptr_t extract(sqlite3_value*) const { - return nullptr; + void result(sqlite3_context* context, const nullptr_t&) const { + sqlite3_result_null(context); } }; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED /** - * Specialization for std::vector. + * Specialization for std::nullopt_t. */ template<> - struct row_extractor, void> { - std::vector extract(const char* columnText) const { - return {columnText, columnText + (columnText ? ::strlen(columnText) : 0)}; + struct statement_binder { + int bind(sqlite3_stmt* stmt, int index, const std::nullopt_t&) const { + return sqlite3_bind_null(stmt, index); } - std::vector extract(sqlite3_stmt* stmt, int columnIndex) const { - auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); - auto len = static_cast(sqlite3_column_bytes(stmt, columnIndex)); - return {bytes, bytes + len}; + void result(sqlite3_context* context, const std::nullopt_t&) const { + sqlite3_result_null(context); } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template + struct statement_binder< + V, + std::enable_if_t::value && + internal::is_bindable>::value>> { + using unqualified_type = std::remove_cv_t; - std::vector extract(sqlite3_value* value) const { - auto bytes = static_cast(sqlite3_value_blob(value)); - auto len = static_cast(sqlite3_value_bytes(value)); - return {bytes, bytes + len}; + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + if(value) { + return statement_binder().bind(stmt, index, *value); + } else { + return statement_binder().bind(stmt, index, nullptr); + } } }; /** - * Specialization for journal_mode. + * Specialization for binary data (std::vector). */ template<> - struct row_extractor { - journal_mode extract(const char* columnText) const { - if(columnText) { - if(auto res = internal::journal_mode_from_string(columnText)) { - return std::move(*res); - } else { - throw std::system_error{orm_error_code::incorrect_journal_mode_string}; - } + struct statement_binder, void> { + int bind(sqlite3_stmt* stmt, int index, const std::vector& value) const { + if(!value.empty()) { + return sqlite3_bind_blob(stmt, index, (const void*)&value.front(), int(value.size()), SQLITE_TRANSIENT); } else { - throw std::system_error{orm_error_code::incorrect_journal_mode_string}; + return sqlite3_bind_blob(stmt, index, "", 0, SQLITE_TRANSIENT); } } - journal_mode extract(sqlite3_stmt* stmt, int columnIndex) const { - auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); - return this->extract(cStr); + void result(sqlite3_context* context, const std::vector& value) const { + if(!value.empty()) { + sqlite3_result_blob(context, (const void*)&value.front(), int(value.size()), nullptr); + } else { + sqlite3_result_blob(context, "", 0, nullptr); + } } - - journal_mode extract(sqlite3_value* value) const = delete; }; - namespace internal { - - /* - * Helper to extract a structure from a rowset. - */ - template - struct struct_extractor; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct statement_binder && + internal::is_bindable_v>>> { + using unqualified_type = std::remove_cv_t; -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED - /* - * Returns a value-based row extractor for an unmapped type, - * returns a structure extractor for a table reference, tuple or named struct. - */ - template - auto make_row_extractor([[maybe_unused]] const DBOs& dbObjects) { - if constexpr(polyfill::is_specialization_of_v || - polyfill::is_specialization_of_v || is_table_reference_v) { - return struct_extractor{dbObjects}; + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + if(value) { + return statement_binder().bind(stmt, index, *value); } else { - return row_value_extractor(); + return statement_binder().bind(stmt, index, std::nullopt); } } -#else - /* - * Overload for an unmapped type returns a common row extractor. - */ - template< - class R, - class DBOs, - std::enable_if_t, - polyfill::is_specialization_of, - is_table_reference>>::value, - bool> = true> - auto make_row_extractor(const DBOs& /*dbObjects*/) { - return row_value_extractor(); - } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - /* - * Overload for a table reference, tuple or aggregate of column results returns a structure extractor. - */ - template, - polyfill::is_specialization_of, - is_table_reference>::value, - bool> = true> - struct_extractor make_row_extractor(const DBOs& dbObjects) { - return {dbObjects}; - } -#endif + namespace internal { - /** - * Specialization for a tuple of top-level column results. - */ - template - struct struct_extractor, DBOs> { - const DBOs& db_objects; + struct conditional_binder { + sqlite3_stmt* stmt = nullptr; + int index = 1; - std::tuple extract(const char* columnText) const = delete; + explicit conditional_binder(sqlite3_stmt* stmt) : stmt{stmt} {} - // note: expects to be called only from the top level, and therefore discards the index - std::tuple...> extract(sqlite3_stmt* stmt, - int&& /*nextColumnIndex*/ = 0) const { - int columnIndex = -1; - return {make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + template = true> + void operator()(const T& t) { + int rc = statement_binder{}.bind(this->stmt, this->index++, t); + if(SQLITE_OK != rc) { + throw_translated_sqlite_error(this->stmt); + } } - // unused to date - std::tuple...> extract(sqlite3_stmt* stmt, int& columnIndex) const = delete; + template = true> + void operator()(const T&) const {} + }; - std::tuple extract(sqlite3_value* value) const = delete; + struct field_value_binder : conditional_binder { + using conditional_binder::conditional_binder; + using conditional_binder::operator(); + + template = true> + void operator()(const T&) const = delete; + + template + void operator()(const T* value) { + if(!value) { + throw std::system_error{orm_error_code::value_is_null}; + } + (*this)(*value); + } }; - /** - * Specialization for an unmapped structure to be constructed ad-hoc from column results. - * - * This plays together with `column_result_of_t`, which returns `struct_t` as `structure` - */ - template - struct struct_extractor>, DBOs> { - const DBOs& db_objects; + struct tuple_value_binder { + sqlite3_stmt* stmt = nullptr; - O extract(const char* columnText) const = delete; + explicit tuple_value_binder(sqlite3_stmt* stmt) : stmt{stmt} {} - // note: expects to be called only from the top level, and therefore discards the index; - // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. - // see unit test tests/prepared_statement_tests/select.cpp/TEST_CASE("Prepared select")/SECTION("non-aggregate struct") - template = true> - O extract(sqlite3_stmt* stmt, int&& /*nextColumnIndex*/ = 0) const { - int columnIndex = -1; - return O{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + template + void operator()(const Tpl& tpl, Projection project) const { + (*this)(tpl, + std::make_index_sequence::value>{}, + std::forward(project)); } - template = true> - O extract(sqlite3_stmt* stmt, int&& /*nextColumnIndex*/ = 0) const { - int columnIndex = -1; - // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. - std::tuple t{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; - return create_from_tuple(std::move(t), std::index_sequence_for{}); + private: +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); + } +#else + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + using Sink = int[sizeof...(Idx)]; + (void)Sink{(this->bind(polyfill::invoke(project, std::get(tpl)), Idx), 0)...}; } +#endif - // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. - // see unit test tests/prepared_statement_tests/select.cpp/TEST_CASE("Prepared select")/SECTION("non-aggregate struct") - template = true> - O extract(sqlite3_stmt* stmt, int& columnIndex) const { - --columnIndex; - return O{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + template + void bind(const T& t, size_t idx) const { + int rc = statement_binder{}.bind(this->stmt, int(idx + 1), t); + if(SQLITE_OK != rc) { + throw_translated_sqlite_error(this->stmt); + } } - template = true> - O extract(sqlite3_stmt* stmt, int& columnIndex) const { - --columnIndex; - // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. - std::tuple t{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; - return create_from_tuple(std::move(t), std::index_sequence_for{}); + template + void bind(const T* value, size_t idx) const { + if(!value) { + throw std::system_error{orm_error_code::value_is_null}; + } + (*this)(*value, idx); } + }; - O extract(sqlite3_value* value) const = delete; + template + using bindable_filter_t = filter_tuple_t; + } +} + +// #include "column_result.h" + +#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of +#include // std::reference_wrapper + +// #include "functional/cxx_universal.h" +// ::nullptr_t +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/mpl.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_fy.h" + +#include + +namespace sqlite_orm { + + namespace internal { + + template + struct tuplify { + using type = std::tuple; + }; + template + struct tuplify> { + using type = std::tuple; }; + + template + using tuplify_t = typename tuplify::type; } } -#pragma once -#include +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_transformer.h" + +// #include "tuple_helper/same_or_void.h" + +// #include "type_traits.h" + +// #include "member_traits/member_traits.h" + +// #include "mapped_type_proxy.h" + +#include // std::remove_const -namespace sqlite_orm { +// #include "type_traits.h" - enum class sync_schema_result { +// #include "table_reference.h" - /** - * created new table, table with the same tablename did not exist - */ - new_table_created, +// #include "alias_traits.h" - /** - * table schema is the same as storage, nothing to be done - */ - already_in_sync, +namespace sqlite_orm { - /** - * removed excess columns in table (than storage) without dropping a table - */ - old_columns_removed, + namespace internal { /** - * lacking columns in table (than storage) added without dropping a table + * If T is a table reference or recordset alias then the typename mapped_type_proxy::type is the unqualified aliased type, + * otherwise unqualified T. */ - new_columns_added, + template + struct mapped_type_proxy : std::remove_const {}; - /** - * both old_columns_removed and new_columns_added - */ - new_columns_added_and_old_columns_removed, +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + struct mapped_type_proxy : R {}; +#endif - /** - * old table is dropped and new is recreated. Reasons : - * 1. delete excess columns in the table than storage if preseve = false - * 2. Lacking columns in the table cannot be added due to NULL and DEFAULT constraint - * 3. Reasons 1 and 2 both together - * 4. data_type mismatch between table and storage. - */ - dropped_and_recreated, - }; + template + struct mapped_type_proxy> : std::remove_const> {}; - inline std::ostream& operator<<(std::ostream& os, sync_schema_result value) { - switch(value) { - case sync_schema_result::new_table_created: - return os << "new table created"; - case sync_schema_result::already_in_sync: - return os << "table and storage is already in sync."; - case sync_schema_result::old_columns_removed: - return os << "old excess columns removed"; - case sync_schema_result::new_columns_added: - return os << "new columns added"; - case sync_schema_result::new_columns_added_and_old_columns_removed: - return os << "old excess columns removed and new columns added"; - case sync_schema_result::dropped_and_recreated: - return os << "old table dropped and recreated"; - } - return os; + template + using mapped_type_proxy_t = typename mapped_type_proxy::type; } } -#pragma once -#include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t -#include // std::string -#include // std::forward +// #include "core_functions.h" -// #include "../functional/cxx_universal.h" +// #include "select_constraints.h" -// #include "../tuple_helper/tuple_traits.h" +// #include "operators.h" -// #include "../indexed_column.h" +// #include "rowid.h" -#include // std::string -#include // std::move +// #include "column_result_proxy.h" -// #include "functional/cxx_universal.h" +// #include "type_traits.h" -// #include "ast/where.h" +// #include "table_reference.h" namespace sqlite_orm { - namespace internal { - template - struct indexed_column_t { - using column_type = C; + /* + * Holder for the type of an unmapped aggregate/structure/object to be constructed ad-hoc from column results. + * `T` must be constructible using direct-list-initialization. + */ + template + struct structure { + using type = T; + }; + } +} -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - indexed_column_t(column_type _column_or_expression) : - column_or_expression(std::move(_column_or_expression)) {} -#endif +namespace sqlite_orm { + namespace internal { - column_type column_or_expression; - std::string _collation_name; - int _order = 0; // -1 = desc, 1 = asc, 0 = not specified + template + struct column_result_proxy : std::remove_const {}; - indexed_column_t collate(std::string name) { - auto res = std::move(*this); - res._collation_name = std::move(name); - return res; - } + /* + * Unwrap `table_reference` + */ + template + struct column_result_proxy> : decay_table_ref

{}; - indexed_column_t asc() { - auto res = std::move(*this); - res._order = 1; - return res; - } + /* + * Pass through `structure` + */ + template + struct column_result_proxy> : P {}; - indexed_column_t desc() { - auto res = std::move(*this); - res._order = -1; - return res; - } - }; + template + using column_result_proxy_t = typename column_result_proxy::type; + } +} - template - indexed_column_t make_indexed_column(C col) { - return {std::move(col)}; - } +// #include "alias.h" - template - where_t make_indexed_column(where_t wher) { - return std::move(wher); - } +// #include "cte_types.h" - template - indexed_column_t make_indexed_column(indexed_column_t col) { - return std::move(col); - } - } +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#include +#include +#endif - /** - * Use this function to specify indexed column inside `make_index` function call. - * Example: make_index("index_name", indexed_column(&User::id).asc()) - */ - template - internal::indexed_column_t indexed_column(C column_or_expression) { - return {std::move(column_or_expression)}; - } +// #include "functional/cxx_core_features.h" -} +// #include "functional/cxx_type_traits_polyfill.h" -// #include "../table_type_of.h" +// #include "tuple_helper/tuple_fy.h" -namespace sqlite_orm { +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { - struct index_base { - std::string name; - bool unique = false; + /** + * Aliased column expression mapped into a CTE, stored as a field in a table column. + */ + template + struct aliased_field { + ~aliased_field() = delete; + aliased_field(const aliased_field&) = delete; + void operator=(const aliased_field&) = delete; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} -#endif + F field; }; - template - struct index_t : index_base { - using elements_type = std::tuple; - using object_type = void; - using table_mapped_type = T; - -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - index_t(std::string name_, bool unique_, elements_type elements_) : - index_base{std::move(name_), unique_}, elements(std::move(elements_)) {} -#endif + /** + * This class captures various properties and aspects of a subselect's column expression, + * and is used as a proxy in table_t<>. + */ + template + class subselect_mapper { + public: + subselect_mapper() = delete; - elements_type elements; + // this type name is used to detect the mapping from moniker to object + using cte_moniker_type = Moniker; + using fields_type = std::tuple; + // this type captures the expressions forming the columns in a subselect; + // it is currently unused, however proves to be useful in compilation errors, + // as it simplifies recognizing errors in column expressions + using expressions_tuple = tuplify_t; + // this type captures column reference expressions specified at CTE construction; + // those are: member pointers, alias holders + using explicit_colrefs_tuple = ExplicitColRefs; + // this type captures column reference expressions from the subselect; + // those are: member pointers, alias holders + using subselect_colrefs_tuple = SubselectColRefs; + // this type captures column reference expressions merged from SubselectColRefs and ExplicitColRefs + using final_colrefs_tuple = FinalColRefs; }; } +} +#endif - template - internal::index_t()))...> make_index(std::string name, - Cols... cols) { - using cols_tuple = std::tuple; - static_assert(internal::count_tuple::value <= 1, - "amount of where arguments can be 0 or 1"); - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); - } +// #include "storage_traits.h" - template - internal::index_t>>, - decltype(internal::make_indexed_column(std::declval()))...> - make_index(std::string name, Cols... cols) { - using cols_tuple = std::tuple; - static_assert(internal::count_tuple::value <= 1, - "amount of where arguments can be 0 or 1"); - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); - } +#include // std::tuple + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_transformer.h" + +// #include "type_traits.h" - template - internal::index_t>>, - decltype(internal::make_indexed_column(std::declval()))...> - make_unique_index(std::string name, Cols... cols) { - using cols_tuple = std::tuple; - static_assert(internal::count_tuple::value <= 1, - "amount of where arguments can be 0 or 1"); - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), true, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); - } -} -#pragma once +// #include "storage_lookup.h" -#include // std::string +#include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void +#include +#include // std::index_sequence, std::make_index_sequence -namespace sqlite_orm { +// #include "functional/cxx_universal.h" +// ::size_t +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" +namespace sqlite_orm { namespace internal { - struct rowid_t { - operator std::string() const { - return "rowid"; - } - }; + template + struct storage_t; - struct oid_t { - operator std::string() const { - return "oid"; - } - }; + template + using db_objects_tuple = std::tuple; - struct _rowid_t { - operator std::string() const { - return "_rowid_"; - } - }; + struct basic_table; + struct index_base; + struct base_trigger; template - struct table_rowid_t : public rowid_t { - using type = T; - }; + struct is_storage : std::false_type {}; + + template + struct is_storage> : std::true_type {}; + template + struct is_storage> : std::true_type {}; template - struct table_oid_t : public oid_t { - using type = T; - }; - template - struct table__rowid_t : public _rowid_t { - using type = T; - }; + struct is_db_objects : std::false_type {}; - } + template + struct is_db_objects> : std::true_type {}; + // note: cannot use `db_objects_tuple` alias template because older compilers have problems + // to match `const db_objects_tuple`. + template + struct is_db_objects> : std::true_type {}; - inline internal::rowid_t rowid() { - return {}; - } + /** + * `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. + */ + template + struct object_type_matches : polyfill::conjunction>>, + std::is_same>> {}; - inline internal::oid_t oid() { - return {}; + /** + * `std::true_type` if given lookup type (object or moniker) is mapped, `std::false_type` otherwise. + */ + template + using lookup_type_matches = object_type_matches; } - inline internal::_rowid_t _rowid_() { - return {}; - } + // pick/lookup metafunctions + namespace internal { - template - internal::table_rowid_t rowid() { - return {}; - } + /** + * Indirect enabler for DBO, accepting an index to disambiguate non-unique DBOs + */ + template + struct enable_found_table : std::enable_if::value, DBO> {}; - template - internal::table_oid_t oid() { - return {}; - } + /** + * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * + * Lookup - mapped data type + * Seq - index sequence matching the number of DBOs + * DBOs - db_objects_tuple type + */ + template + struct storage_pick_table; - template - internal::table__rowid_t _rowid_() { - return {}; - } -} -#pragma once + template + struct storage_pick_table, db_objects_tuple> + : enable_found_table... {}; -#include // std::string -#include // std::remove_const, std::is_member_pointer, std::true_type, std::false_type -#include // std::vector -#include // std::tuple_element -#include // std::forward, std::move + /** + * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * + * Lookup - 'table' type, mapped data type + * DBOs - db_objects_tuple type, possibly const-qualified + */ + template + using storage_pick_table_t = typename storage_pick_table::value>, + std::remove_const_t>::type; -// #include "../functional/cxx_universal.h" -// ::size_t -// #include "../functional/cxx_type_traits_polyfill.h" + /** + * Find a table definition (`table_t`) from a tuple of database objects; + * `std::nonesuch` if not found. + * + * DBOs - db_objects_tuple type + * Lookup - mapped data type + */ + template + struct storage_find_table : polyfill::detected {}; -// #include "../functional/cxx_functional_polyfill.h" + /** + * Find a table definition (`table_t`) from a tuple of database objects; + * `std::nonesuch` if not found. + * + * DBOs - db_objects_tuple type, possibly const-qualified + * Lookup - mapped data type + */ + template + using storage_find_table_t = typename storage_find_table>::type; -// #include "../functional/static_magic.h" +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template + struct is_mapped : std::false_type {}; + template + struct is_mapped>> : std::true_type {}; +#else + template> + struct is_mapped : std::true_type {}; + template + struct is_mapped : std::false_type {}; +#endif -// #include "../functional/mpl.h" + template + SQLITE_ORM_INLINE_VAR constexpr bool is_mapped_v = is_mapped::value; + } +} -// #include "../functional/index_sequence_util.h" +// runtime lookup functions +namespace sqlite_orm { + namespace internal { + /** + * Pick the table definition for the specified lookup type from the given tuple of schema objects. + * + * Note: This function requires Lookup to be mapped, otherwise it is removed from the overload resolution set. + */ + template = true> + auto& pick_table(DBOs& dbObjects) { + using table_type = storage_pick_table_t; + return std::get(dbObjects); + } -// #include "../tuple_helper/tuple_filter.h" + /** + * Return passed in DBOs. + */ + template = true> + decltype(auto) db_objects_for_expression(DBOs& dbObjects, const E&) { + return dbObjects; + } -// #include "../tuple_helper/tuple_traits.h" + template = true> + decltype(auto) lookup_table_name(const DBOs& dbObjects); + } +} -// #include "../tuple_helper/tuple_iteration.h" +// #include "schema/column.h" -// #include "../tuple_helper/tuple_transformer.h" +namespace sqlite_orm { + namespace internal { -// #include "../member_traits/member_traits.h" + namespace storage_traits { -// #include "../typed_comparator.h" + /** + * DBO - db object (table) + */ + template + struct storage_mapped_columns_impl + : tuple_transformer, is_column>, field_type_t> {}; -namespace sqlite_orm { + template<> + struct storage_mapped_columns_impl { + using type = std::tuple<>; + }; - namespace internal { + /** + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type + */ + template + struct storage_mapped_columns : storage_mapped_columns_impl> {}; - template - bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { - return false; - } - template - bool compare_any(const O& lhs, const O& rhs) { - return lhs == rhs; + /** + * DBO - db object (table) + */ + template + struct storage_mapped_column_expressions_impl + : tuple_transformer, is_column>, column_field_expression_t> {}; + + template<> + struct storage_mapped_column_expressions_impl { + using type = std::tuple<>; + }; + + /** + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type + */ + template + struct storage_mapped_column_expressions + : storage_mapped_column_expressions_impl> {}; } } } -// #include "../type_traits.h" - -// #include "../alias_traits.h" +// #include "function.h" -// #include "../constraints.h" +#include // std::enable_if, std::is_member_function_pointer, std::is_function, std::remove_const, std::decay, std::is_convertible, std::is_same, std::false_type, std::true_type +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include // std::copy_constructible +#endif +#include // std::tuple, std::tuple_size, std::tuple_element +#include // std::min, std::copy_n +#include // std::move, std::forward -// #include "../table_info.h" +// #include "functional/cxx_universal.h" +// ::size_t, ::nullptr_t +// #include "functional/cxx_type_traits_polyfill.h" -// #include "column.h" +// #include "functional/cstring_literal.h" -namespace sqlite_orm { +// #include "functional/function_traits.h" - namespace internal { +// #include "cxx_type_traits_polyfill.h" - 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>; +// #include "mpl.h" -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - /** - * A subselect mapper's CTE moniker, void otherwise. +namespace sqlite_orm { + namespace internal { + /* + * Define nested typenames: + * - return_type + * - arguments_tuple + * - signature_type */ - template - using moniker_of_or_void_t = polyfill::detected_or_t; + template + struct function_traits; - /** - * 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. + /* + * A function's return type */ - template - using mapped_object_type_for_t = polyfill::detected_or_t; -#endif - - struct basic_table { - - /** - * Table name. - */ - std::string name; - }; + template + using function_return_type_t = typename function_traits::return_type; - /** - * Table definition. + /* + * A function's arguments tuple */ - 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; + template + class Tuple, + template class ProjectOp = polyfill::type_identity_t> + using function_arguments = typename function_traits::template arguments_tuple; - static constexpr bool is_without_rowid_v = WithoutRowId; + /* + * A function's signature + */ + template + using function_signature_type_t = typename function_traits::signature_type; - using is_without_rowid = polyfill::bool_constant; + template + struct function_traits { + using return_type = R; - elements_type elements; + template class Tuple, template class ProjectOp> + using arguments_tuple = Tuple...>; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - table_t(std::string name_, elements_type elements_) : - basic_table{std::move(name_)}, elements{std::move(elements_)} {} -#endif + using signature_type = R(Args...); + }; - table_t without_rowid() const { - return {this->name, this->elements}; - } + // non-exhaustive partial specializations of `function_traits` - /* - * 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()); - } + template + struct function_traits : function_traits { + using signature_type = R(Args...) const; + }; - /* - * 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()); - } +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct function_traits : function_traits { + using signature_type = R(Args...) noexcept; + }; - /* - * 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()); - } + template + struct function_traits : function_traits { + using signature_type = R(Args...) const noexcept; + }; +#endif - /** - * 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); - } + /* + * Pick signature of function pointer + */ + template + struct function_traits : function_traits {}; - 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_any(column.setter, memberPointer)) { - res = &polyfill::invoke(column.member_pointer, object); - } - })); - return res; - } + /* + * Pick signature of function reference + */ + template + struct function_traits : function_traits {}; - const basic_generated_always::storage_type* - find_column_generated_storage_type(const std::string& name) const { - const basic_generated_always::storage_type* result = nullptr; -#if SQLITE_VERSION_NUMBER >= 3031000 - iterate_tuple(this->elements, - col_index_sequence_with{}, - [&result, &name](auto& column) { - if(column.name != name) { - return; - } - using generated_op_index_sequence = - filter_tuple_sequence_t, - is_generated_always>; - constexpr size_t opIndex = index_sequence_value_at<0>(generated_op_index_sequence{}); - result = &std::get(column.constraints).storage; - }); -#else - (void)name; -#endif - return result; - } + /* + * Pick signature of pointer-to-member function + */ + template + struct function_traits : function_traits {}; + } +} - /** - * 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); - } +// #include "type_traits.h" - 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; - } +// #include "tags.h" - std::vector primary_key_column_names() const { - using pkcol_index_sequence = col_index_sequence_with; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - if(pkcol_index_sequence::size() > 0) { - return create_from_tuple>(this->elements, - pkcol_index_sequence{}, - &column_identifier::name); - } else { - return this->composite_key_columns_names(); - } - } + struct arg_values; - 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); - }); - } + // note (internal): forward declare even if `SQLITE_VERSION_NUMBER < 3020000` in order to simplify coding below + template + struct pointer_arg; + // note (internal): forward declare even if `SQLITE_VERSION_NUMBER < 3020000` in order to simplify coding below + template + class pointer_binding; - 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; - } - }); - } + namespace internal { + template + using scalar_call_function_t = decltype(&F::operator()); - /** - * 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 m) const { - const std::string* res = nullptr; - using field_type = member_field_type_t; - iterate_tuple(this->elements, - col_index_sequence_with_field_type{}, - [&res, m](auto& c) { - if(compare_any(c.member_pointer, m) || compare_any(c.setter, m)) { - res = &c.name; - } - }); - return res; - } + template + using aggregate_step_function_t = decltype(&F::step); - /** - * Call passed lambda with all defined foreign keys. - * @param lambda Lambda called for each column. Function signature: `void(auto& column)` - */ - template - void for_each_foreign_key(L&& lambda) const { - using fk_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->elements, fk_index_sequence{}, lambda); - } + template + using aggregate_fin_function_t = decltype(&F::fin); - template - void for_each_foreign_key_to(L&& lambda) const { - using fk_index_sequence = filter_tuple_sequence_t; - using filtered_index_sequence = filter_tuple_sequence_t::template fn, - target_type_t, - fk_index_sequence>; - iterate_tuple(this->elements, filtered_index_sequence{}, lambda); - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v>> = true; - /** - * 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 { - using col_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->elements, col_index_sequence{}, lambda); - } + template + struct is_scalar_udf : polyfill::bool_constant> {}; - /** - * 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); - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_udf_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_udf_v< + F, + polyfill::void_t, + aggregate_fin_function_t, + std::enable_if_t>::value>, + std::enable_if_t>::value>>> = + true; - /** - * 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); - } + template + struct is_aggregate_udf : polyfill::bool_constant> {}; - std::vector get_table_info() const; - }; + template + struct function; + } - template - struct is_table : std::false_type {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** @short Specifies that a type is a function signature (i.e. a function in the C++ type system). + */ + template + concept orm_function_sig = std::is_function_v; - template - struct is_table> : std::true_type {}; + /** @short Specifies that a type is a classic function object. + * + * A classic function object meets the following requirements: + * - defines a single call operator `F::operator()` + * - isn't a traditional sqlite_orm scalar function (having a static `F::name()` function + */ + template + concept orm_classic_function_object = + ((!requires { typename F::is_transparent; }) && (requires { &F::operator(); }) && + /*rule out sqlite_orm scalar function*/ + (!requires { F::name(); })); - 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; + /** @short Specifies that a type is a user-defined scalar function. + * + * `UDF` must meet the following requirements: + * - `UDF::name()` static function + * - `UDF::operator()()` call operator + */ + template + concept orm_scalar_udf = requires { + UDF::name(); + typename internal::scalar_call_function_t; + }; - static constexpr bool is_without_rowid_v = false; - using is_without_rowid = polyfill::bool_constant; + /** @short Specifies that a type is a user-defined aggregate function. + * + * `UDF` must meet the following requirements: + * - `UDF::name()` static function + * - `UDF::step()` member function + * - `UDF::fin()` member function + */ + template + concept orm_aggregate_udf = requires { + UDF::name(); + typename internal::aggregate_step_function_t; + typename internal::aggregate_fin_function_t; + requires std::is_member_function_pointer_v>; + requires std::is_member_function_pointer_v>; + }; - module_details_type module_details; + /** @short Specifies that a type is a framed user-defined scalar function. + */ + template + concept orm_scalar_function = (polyfill::is_specialization_of_v, internal::function> && + orm_scalar_udf); -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - virtual_table_t(std::string name, module_details_type module_details) : - basic_table{std::move(name)}, module_details{std::move(module_details)} {} + /** @short Specifies that a type is a framed user-defined aggregate function. + */ + template + concept orm_aggregate_function = (polyfill::is_specialization_of_v, internal::function> && + orm_aggregate_udf); + + /** @short Specifies that a type is a framed and quoted user-defined scalar function. + */ + template + concept orm_quoted_scalar_function = requires(const Q& quotedF) { + quotedF.name(); + quotedF.callable(); + }; #endif - /** - * 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); - } + namespace internal { + template + struct callable_arguments_impl; - /** - * 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); - } + template + struct callable_arguments_impl> { + using args_tuple = function_arguments, std::tuple, std::decay_t>; + using return_type = function_return_type_t>; + }; - /** - * 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 + struct callable_arguments_impl> { + using args_tuple = function_arguments, std::tuple, std::decay_t>; + using return_type = function_return_type_t>; }; - template - struct is_virtual_table : std::false_type {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + requires(std::is_function_v) + struct callable_arguments_impl { + using args_tuple = function_arguments; + using return_type = std::decay_t>; + }; +#endif - template - struct is_virtual_table> : std::true_type {}; + template + struct callable_arguments : callable_arguments_impl {}; -#if SQLITE_VERSION_NUMBER >= 3009000 - template - struct using_fts5_t { - using object_type = T; - using columns_type = std::tuple; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /* + * Bundle of type and name of a quoted user-defined function. + */ + template + struct udf_holder : private std::string { + using udf_type = UDF; - columns_type columns; + using std::string::basic_string; - using_fts5_t(columns_type columns) : columns(std::move(columns)) {} + const std::string& operator()() const { + return *this; + } + }; +#endif + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /* + * Bundle of type and name of a traditional sqlite_orm user-defined function. + */ + template + requires(requires { UDF::name(); }) + struct udf_holder +#else + /* + * Bundle of type and name of a traditional sqlite_orm user-defined function. + */ + template + struct udf_holder +#endif + { + using udf_type = UDF; - /** - * 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); + template>::value, bool> = true> + decltype(auto) operator()() const { + return UDF::name(); } - /** - * 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); + template::value, bool> = true> + std::string operator()() const { + return std::string{UDF::name()}; } + }; - /** - * 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 { - using col_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->columns, col_index_sequence{}, lambda); - } + /* + * Represents a call of a user-defined function. + */ + template + struct function_call { + using udf_type = UDF; + using args_tuple = std::tuple; + + udf_holder name; + args_tuple callArgs; }; -#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_any(memberPointer, column.member_pointer) || compare_any(memberPointer, column.setter)) { - res = true; - } - }); - }); - return res; - } + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = true; - template - bool exists_in_composite_primary_key(const virtual_table_t& /*virtualTable*/, - const column_field& /*column*/) { + template + struct unpacked_arg { + using type = T; + }; + template + struct unpacked_arg> { + using type = typename callable_arguments::return_type; + }; + template + using unpacked_arg_t = typename unpacked_arg::type; + + template + SQLITE_ORM_CONSTEVAL bool expected_pointer_value() { + static_assert(polyfill::always_false_v, "Expected a pointer value for I-th argument"); return false; } - } -#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"); + template + constexpr bool is_same_pvt_v = expected_pointer_value(); - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); - } + // Always allow binding nullptr to a pointer argument + template + constexpr bool is_same_pvt_v> = true; + // Always allow binding nullptr to a pointer argument + template + constexpr bool is_same_pvt_v, pointer_binding, void> = true; - template - internal::using_fts5_t using_fts5(Cs... columns) { - static_assert(polyfill::conjunction_v...>, - "Incorrect table elements or constraints"); + template + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_data_type() { + constexpr bool valid = std::is_convertible::value; + static_assert(valid, "Pointer data types of I-th argument do not match"); + return valid; + } - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); - } +#if __cplusplus >= 201703L // C++17 or later + template + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_tag() { + constexpr bool valid = Binding == PointerArg; + static_assert(valid, "Pointer types (tags) of I-th argument do not match"); + return valid; + } + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_tag() && + assert_same_pointer_data_type(); +#else + template + constexpr bool assert_same_pointer_tag() { + constexpr bool valid = Binding::value == PointerArg::value; + static_assert(valid, "Pointer types (tags) of I-th argument do not match"); + return valid; + } + + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_tag(); #endif - /** - * 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"); + // not a pointer value, currently leave it unchecked + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::false_type) { + return true; + } - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), std::make_tuple(std::forward(args)...)}); - } + // check the type of pointer values + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::true_type) { + return is_same_pvt_v; + } - /** - * Factory function for a table definition. - * - * The mapped object type is explicitly specified. - */ - template - internal::table_t make_table(std::string name, Cs... args) { - static_assert(polyfill::conjunction_v...>, - "Incorrect table elements or constraints"); + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { + return true; + } + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { + using func_param_type = std::tuple_element_t; + using call_arg_type = unpacked_arg_t>; - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), std::make_tuple(std::forward(args)...)}); - } +#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED + constexpr bool valid = validate_pointer_value_type, + unpacked_arg_t>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v) || + (polyfill::is_specialization_of_v) > {}); -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Factory function for a table definition. - * - * 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)...); - } + return validate_pointer_value_types(polyfill::index_constant{}) && valid; +#else + return validate_pointer_value_types(polyfill::index_constant{}) && + validate_pointer_value_type, + unpacked_arg_t>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v) || + (polyfill::is_specialization_of_v) > {}); #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)}); - } -} -#pragma once + /* + * Note: Currently the number of call arguments is checked and whether the types of pointer values match, + * but other call argument types are not checked against the parameter types of the function. + */ + template +#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED + SQLITE_ORM_CONSTEVAL void check_function_call() { +#else + void check_function_call() { +#endif + using call_args_tuple = std::tuple; + using function_params_tuple = typename callable_arguments::args_tuple; + constexpr size_t callArgsCount = std::tuple_size::value; + constexpr size_t functionParamsCount = std::tuple_size::value; + static_assert(std::is_same>::value || + (callArgsCount == functionParamsCount && + validate_pointer_value_types( + polyfill::index_constant{})), + "Check the number and types of the function call arguments"); + } -#include // std::string + /* + * Generator of a user-defined function call in a sql query expression. + * + * Use the variable template `func<>` to instantiate. + * + * Calling the function captures the parameters in a `function_call` node. + */ + template + struct function { + using udf_type = UDF; + using callable_type = UDF; -// #include "functional/cxx_universal.h" -// ::size_t -// #include "functional/static_magic.h" + /* + * Generates the SQL function call. + */ + template + function_call operator()(CallArgs... callArgs) const { + check_function_call(); + return {this->udf_holder(), {std::forward(callArgs)...}}; + } -// #include "functional/index_sequence_util.h" + constexpr auto udf_holder() const { + return internal::udf_holder{}; + } -// #include "tuple_helper/tuple_traits.h" + // returns a character range + constexpr auto name() const { + return this->udf_holder()(); + } + }; -// #include "tuple_helper/tuple_filter.h" +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /* + * Generator of a user-defined function call in a sql query expression. + * + * Use the string literal operator template `""_scalar.quote()` to quote + * a freestanding function, stateless lambda or function object. + * + * Calling the function captures the parameters in a `function_call` node. + * + * Internal note: + * 1. Captures and represents a function [pointer or object], especially one without side effects. + * If `F` is a stateless function object, `quoted_scalar_function::callable()` returns the original function object, + * otherwise it is assumed to have possibe side-effects and `quoted_scalar_function::callable()` returns a copy. + * 2. The nested `udf_type` typename is deliberately chosen to be the function signature, + * and will be the abstracted version of the user-defined function. + */ + template + struct quoted_scalar_function { + using udf_type = Sig; + using callable_type = F; -// #include "tuple_helper/tuple_iteration.h" + /* + * Generates the SQL function call. + */ + template + function_call operator()(CallArgs... callArgs) const { + check_function_call(); + return {this->udf_holder(), {std::forward(callArgs)...}}; + } -// #include "type_traits.h" + /* + * Return original `udf` if stateless or a copy of it otherwise + */ + constexpr decltype(auto) callable() const { + if constexpr(stateless) { + return (this->udf); + } else { + // non-const copy + return F(this->udf); + } + } -// #include "select_constraints.h" + constexpr auto udf_holder() const { + return internal::udf_holder{this->name()}; + } -// #include "cte_types.h" + constexpr auto name() const { + return this->nme; + } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include -#include -#endif + template + consteval quoted_scalar_function(const char (&name)[N], Args&&... constructorArgs) : + udf(std::forward(constructorArgs)...) { + std::copy_n(name, N, this->nme); + } -// #include "functional/cxx_core_features.h" + F udf; + char nme[N]; + }; -// #include "functional/cxx_type_traits_polyfill.h" + template + struct quoted_function_builder : cstring_literal { + using cstring_literal::cstring_literal; -// #include "tuple_helper/tuple_fy.h" + /* + * From a freestanding function, possibly overloaded. + */ + template + [[nodiscard]] consteval auto quote(F* callable) const { + return quoted_scalar_function{this->cstr, std::move(callable)}; + } -#include + /* + * From a classic function object instance. + */ + template + requires(orm_classic_function_object && (stateless || std::copy_constructible)) + [[nodiscard]] consteval auto quote(F callable) const { + using Sig = function_signature_type_t; + // detect whether overloaded call operator can be picked using `Sig` + using call_operator_type = decltype(static_cast(&F::operator())); + return quoted_scalar_function{this->cstr, std::move(callable)}; + } -namespace sqlite_orm { + /* + * From a function object instance, picking the overloaded call operator. + */ + template + requires((stateless || std::copy_constructible)) + [[nodiscard]] consteval auto quote(F callable) const { + // detect whether overloaded call operator can be picked using `Sig` + using call_operator_type = decltype(static_cast(&F::operator())); + return quoted_scalar_function{this->cstr, std::move(callable)}; + } - namespace internal { + /* + * From a classic function object type. + */ + template + requires(stateless || std::copy_constructible) + [[nodiscard]] consteval auto quote(Args&&... constructorArgs) const { + using Sig = function_signature_type_t; + return quoted_scalar_function{this->cstr, std::forward(constructorArgs)...}; + } - template - struct tuplify { - using type = std::tuple; - }; - template - struct tuplify> { - using type = std::tuple; + /* + * From a function object type, picking the overloaded call operator. + */ + template + requires((stateless || std::copy_constructible)) + [[nodiscard]] consteval auto quote(Args&&... constructorArgs) const { + // detect whether overloaded call operator can be picked using `Sig` + using call_operator_type = decltype(static_cast(&F::operator())); + return quoted_scalar_function{this->cstr, std::forward(constructorArgs)...}; + } }; +#endif + } - template - using tuplify_t = typename tuplify::type; + /** @short Call a user-defined function. + * + * Note: Currently the number of call arguments is checked and whether the types of pointer values match, + * but other call argument types are not checked against the parameter types of the function. + * + * Example: + * struct IdFunc { int oeprator(int arg)() const { return arg; } }; + * // inline: + * select(func(42)); + * // As this is a variable template, you can frame the user-defined function and define a variable for syntactic sugar and legibility: + * inline constexpr orm_scalar_function auto idfunc = func; + * select(idfunc(42)); + * + */ + template +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + requires(orm_scalar_udf || orm_aggregate_udf) +#endif + SQLITE_ORM_INLINE_VAR constexpr internal::function func{}; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + inline namespace literals { + /* @short Create a scalar function from a freestanding function, stateless lambda or function object, + * and call such a user-defined function. + * + * If you need to pick a function or method from an overload set, or pick a template function you can + * specify an explicit function signature in the call to `from()`. + * + * Examples: + * // freestanding function from a library + * constexpr orm_quoted_scalar_function auto clamp_int_f = "clamp_int"_scalar.quote(std::clamp); + * // stateless lambda + * constexpr orm_quoted_scalar_function auto is_fatal_error_f = "IS_FATAL_ERROR"_scalar.quote([](unsigned long errcode) { + * return errcode != 0; + * }); + * // function object instance + * constexpr orm_quoted_scalar_function auto equal_to_int_f = "equal_to"_scalar.quote(std::equal_to{}); + * // function object + * constexpr orm_quoted_scalar_function auto equal_to_int_2_f = "equal_to"_scalar.quote>(); + * // pick function object's template call operator + * constexpr orm_quoted_scalar_function auto equal_to_int_3_f = "equal_to"_scalar.quote(std::equal_to{}); + * + * storage.create_scalar_function(); + * storage.create_scalar_function(); + * storage.create_scalar_function(); + * storage.create_scalar_function(); + * storage.create_scalar_function(); + * + * auto rows = storage.select(clamp_int_f(0, 1, 1)); + * auto rows = storage.select(is_fatal_error_f(1)); + * auto rows = storage.select(equal_to_int_f(1, 1)); + * auto rows = storage.select(equal_to_int_2_f(1, 1)); + * auto rows = storage.select(equal_to_int_3_f(1, 1)); + */ + template + [[nodiscard]] consteval auto operator"" _scalar() { + return builder; + } } +#endif } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -namespace sqlite_orm { +// #include "ast/special_keywords.h" +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { + struct current_time_t {}; + struct current_date_t {}; + struct current_timestamp_t {}; + } - /** - * Aliased column expression mapped into a CTE, stored as a field in a table column. - */ - template - struct aliased_field { - ~aliased_field() = delete; - aliased_field(const aliased_field&) = delete; - void operator=(const aliased_field&) = delete; - - F field; - }; + inline internal::current_time_t current_time() { + return {}; + } - /** - * This class captures various properties and aspects of a subselect's column expression, - * and is used as a proxy in table_t<>. - */ - template - class subselect_mapper { - public: - subselect_mapper() = delete; + inline internal::current_date_t current_date() { + return {}; + } - // this type name is used to detect the mapping from moniker to object - using cte_moniker_type = Moniker; - using fields_type = std::tuple; - // this type captures the expressions forming the columns in a subselect; - // it is currently unused, however proves to be useful in compilation errors, - // as it simplifies recognizing errors in column expressions - using expressions_tuple = tuplify_t; - // this type captures column reference expressions specified at CTE construction; - // those are: member pointers, alias holders - using explicit_colrefs_tuple = ExplicitColRefs; - // this type captures column reference expressions from the subselect; - // those are: member pointers, alias holders - using subselect_colrefs_tuple = SubselectColRefs; - // this type captures column reference expressions merged from SubselectColRefs and ExplicitColRefs - using final_colrefs_tuple = FinalColRefs; - }; + inline internal::current_timestamp_t current_timestamp() { + return {}; } } + +namespace sqlite_orm { + + namespace internal { + + /** + * Obtains the result type of expressions that form the columns of a select statement. + * + * This is a proxy class used to define what type must have result type depending on select + * arguments (member pointer, aggregate functions, etc). Below you can see specializations + * for different types. E.g. specialization for internal::length_t has `type` int cause + * LENGTH returns INTEGER in sqlite. Every column_result_t must have `type` type that equals + * c++ SELECT return type for T + * DBOs - db_objects_tuple type + * T - C++ type + * SFINAE - sfinae argument + */ + template + struct column_result_t { +#ifdef __FUNCTION__ + // produce an error message that reveals `T` and `DBOs` + static constexpr bool reveal() { + static_assert(polyfill::always_false_v, "T not found in DBOs - " __FUNCTION__); + } + static constexpr bool trigger = reveal(); #endif + }; -// #include "storage_lookup.h" + template + using column_result_of_t = typename column_result_t::type; -#include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void -#include -#include // std::index_sequence, std::make_index_sequence + template + using column_result_for_tuple_t = + transform_tuple_t::template fn>; -// #include "functional/cxx_universal.h" -// ::size_t -// #include "functional/cxx_type_traits_polyfill.h" +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct column_result_t, void> { + using type = std::optional>; + }; -// #include "type_traits.h" + template + struct column_result_t, void> { + using type = std::optional; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED -namespace sqlite_orm { - namespace internal { + template + struct column_result_t, void> { + using type = bool; + }; - template - struct storage_t; + template + struct column_result_t, void> { + using type = bool; + }; - template - using db_objects_tuple = std::tuple; + template + struct column_result_t { + using type = std::string; + }; - struct basic_table; - struct index_base; - struct base_trigger; + template + struct column_result_t { + using type = std::string; + }; - template - struct is_storage : std::false_type {}; + template + struct column_result_t { + using type = std::string; + }; - template - struct is_storage> : std::true_type {}; - template - struct is_storage> : std::true_type {}; + template + struct column_result_t> : member_field_type {}; - template - struct is_db_objects : std::false_type {}; + template + struct column_result_t, void> { + using type = R; + }; - template - struct is_db_objects> : std::true_type {}; - // note: cannot use `db_objects_tuple` alias template because older compilers have problems - // to match `const db_objects_tuple`. - template - struct is_db_objects> : std::true_type {}; + template + struct column_result_t, void> { + using type = R; + }; - /** - * `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. - */ - template - struct object_type_matches : polyfill::conjunction>>, - std::is_same>> {}; + template + struct column_result_t, void> { + using type = typename callable_arguments::return_type; + }; - /** - * `std::true_type` if given lookup type (object or moniker) is mapped, `std::false_type` otherwise. - */ - template - using lookup_type_matches = object_type_matches; - } + template + struct column_result_t, S, X, Rest...>, void> { + using type = std::unique_ptr>; + }; - // pick/lookup metafunctions - namespace internal { + template + struct column_result_t, S, X>, void> { + using type = std::unique_ptr>; + }; - /** - * Indirect enabler for DBO, accepting an index to disambiguate non-unique DBOs - */ - template - struct enable_found_table : std::enable_if::value, DBO> {}; + template + struct column_result_t, void> { + using type = int; + }; - /** - * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. - * - * Lookup - mapped data type - * Seq - index sequence matching the number of DBOs - * DBOs - db_objects_tuple type - */ - template - struct storage_pick_table; + template + struct column_result_t { + using type = nullptr_t; + }; - template - struct storage_pick_table, db_objects_tuple> - : enable_found_table... {}; + template + struct column_result_t { + using type = int; + }; - /** - * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. - * - * Lookup - 'table' type, mapped data type - * DBOs - db_objects_tuple type, possibly const-qualified - */ - template - using storage_pick_table_t = typename storage_pick_table::value>, - std::remove_const_t>::type; + template + struct column_result_t, void> : column_result_t {}; - /** - * Find a table definition (`table_t`) from a tuple of database objects; - * `std::nonesuch` if not found. - * - * DBOs - db_objects_tuple type - * Lookup - mapped data type - */ - template - struct storage_find_table : polyfill::detected {}; + template + struct column_result_t, void> : column_result_t {}; - /** - * Find a table definition (`table_t`) from a tuple of database objects; - * `std::nonesuch` if not found. - * - * DBOs - db_objects_tuple type, possibly const-qualified - * Lookup - mapped data type - */ - template - using storage_find_table_t = typename storage_find_table>::type; + template + struct column_result_t, void> { + using type = std::string; + }; -#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION - template - struct is_mapped : std::false_type {}; - template - struct is_mapped>> : std::true_type {}; -#else - template> - struct is_mapped : std::true_type {}; - template - struct is_mapped : std::false_type {}; -#endif + template + struct column_result_t, void> { + using type = double; + }; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_mapped_v = is_mapped::value; - } -} + template + struct column_result_t, void> { + using type = double; + }; -// runtime lookup functions -namespace sqlite_orm { - namespace internal { - /** - * Pick the table definition for the specified lookup type from the given tuple of schema objects. - * - * Note: This function requires Lookup to be mapped, otherwise it is removed from the overload resolution set. - */ - template = true> - auto& pick_table(DBOs& dbObjects) { - using table_type = storage_pick_table_t; - return std::get(dbObjects); - } + template + struct column_result_t, void> { + using type = double; + }; - /** - * Return passed in DBOs. - */ - template = true> - decltype(auto) db_objects_for_expression(DBOs& dbObjects, const E&) { - return dbObjects; - } + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = double; + }; + + template + struct column_result_t, void> { + using type = int; + }; - template = true> - decltype(auto) lookup_table_name(const DBOs& dbObjects); - } -} + template + struct column_result_t, void> { + using type = int; + }; -// interface functions -namespace sqlite_orm { - namespace internal { + template + struct column_result_t, void> { + using type = int; + }; - template - using tables_index_sequence = filter_tuple_sequence_t; + template + struct column_result_t, void> { + using type = int; + }; - template = true> - int foreign_keys_count(const DBOs& dbObjects) { - int res = 0; - iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { - res += table.template count_of(); - }); - return res; - } + template + struct column_result_t, void> { + using type = int; + }; - template> - decltype(auto) lookup_table_name(const DBOs& dbObjects) { - return static_if::value>( - [](const auto& dbObjects) -> const std::string& { - return pick_table(dbObjects).name; - }, - empty_callable)(dbObjects); - } + template + struct column_result_t { + using type = int64; + }; - /** - * Find column name by its type and member pointer. - */ - template = true> - const std::string* find_column_name(const DBOs& dbObjects, F O::*field) { - return pick_table(dbObjects).find_column_name(field); - } + template + struct column_result_t { + using type = int64; + }; - /** - * Materialize column pointer: - * 1. by explicit object type and member pointer. - * 2. by moniker and member pointer. - */ - template = true> - constexpr decltype(auto) materialize_column_pointer(const DBOs&, const column_pointer& cp) { - return cp.field; - } + template + struct column_result_t { + using type = int64; + }; -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - /** - * 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()` - */ - template = true> - constexpr decltype(auto) materialize_column_pointer(const DBOs&, - const column_pointer>&) { - using table_type = storage_pick_table_t; - using cte_mapper_type = cte_mapper_type_t; + template + struct column_result_t, void> { + using type = int64; + }; - // lookup ColAlias in the final column references - using colalias_index = - find_tuple_type>; - static_assert(colalias_index::value < std::tuple_size_v, - "No such column mapped into the CTE."); + template + struct column_result_t, void> { + using type = int64; + }; - return &aliased_field< - ColAlias, - std::tuple_element_t>::field; - } -#endif + template + struct column_result_t, void> { + using type = int64; + }; - /** - * Find column name by: - * 1. by explicit object type and member pointer. - * 2. by moniker and member pointer. - */ - template = true> - const std::string* find_column_name(const DBOs& dbObjects, const column_pointer& cp) { - auto field = materialize_column_pointer(dbObjects, cp); - return pick_table(dbObjects).find_column_name(field); - } + template + struct column_result_t, void> : column_result_t {}; + + template + struct column_result_t, void> : column_result_t {}; #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - /** - * Find column name by: - * 3. by moniker and alias_holder<>. - */ - template = true> - constexpr decltype(auto) find_column_name(const DBOs& dboObjects, - const column_pointer>&) { + template + struct column_result_t>, void> { using table_type = storage_pick_table_t; using cte_mapper_type = cte_mapper_type_t; - using column_index_sequence = filter_tuple_sequence_t, is_column>; - // note: even though the columns contain the [`aliased_field<>::*`] we perform the lookup using the column references. // lookup ColAlias in the final column references using colalias_index = find_tuple_type>; 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; - // however we have the column index already. - // lookup column in table_t<>'s elements - constexpr size_t ColIdx = index_sequence_value_at(column_index_sequence{}); - auto& table = pick_table(dboObjects); - return &std::get(table.elements).name; - } + using type = std::tuple_element_t; + }; #endif - } -} -#pragma once - -#include // std::string -// #include "constraints.h" + template + struct column_result_t, void> + : conc_tuple>>...> {}; -// #include "serializer_context.h" + template + struct column_result_t, void> { + using type = structure>>...>>; + }; -// #include "storage_lookup.h" + template + struct column_result_t> : column_result_t {}; -namespace sqlite_orm { + template + struct column_result_t> { + using type = + polyfill::detected_t>; + static_assert(!std::is_same::value, + "Compound select statements must return a common type"); + }; - namespace internal { + template + struct column_result_t> { + using type = typename T::result_type; + }; - template - auto serialize(const T& t, const C& context); + template + struct column_result_t, void> { + using type = std::string; + }; /** - * Serialize default value of a column's default valu + * Result for the most simple queries like `SELECT 1` */ - template - std::string serialize_default_value(const default_t& dft) { - db_objects_tuple<> dbObjects; - serializer_context> context{dbObjects}; - return serialize(dft.value, context); - } - - } - -} -#pragma once - -#include -#include // std::unique_ptr/shared_ptr, std::make_unique/std::make_shared -#include // std::system_error -#include // std::string -#include // std::remove_reference, std::is_base_of, std::decay, std::false_type, std::true_type -#include // std::identity -#include // std::stringstream -#include // std::map -#include // std::vector -#include // std::tuple_size, std::tuple, std::make_tuple, std::tie -#include // std::forward, std::pair -#include // std::for_each, std::ranges::for_each -// #include "functional/cxx_optional.h" - -// #include "functional/cxx_universal.h" - -// #include "functional/cxx_functional_polyfill.h" - -// #include "functional/static_magic.h" + template + struct column_result_t> { + using type = T; + }; -// #include "functional/mpl.h" + /** + * Result for the most simple queries like `SELECT 'ototo'` + */ + template + struct column_result_t { + using type = std::string; + }; -// #include "tuple_helper/tuple_traits.h" + template + struct column_result_t { + using type = std::string; + }; -// #include "tuple_helper/tuple_filter.h" + template + struct column_result_t, void> : column_result_t> {}; -// #include "tuple_helper/tuple_transformer.h" + template + struct column_result_t, void> + : storage_traits::storage_mapped_columns> {}; -// #include "tuple_helper/tuple_iteration.h" + template + struct column_result_t, void> { + using type = table_reference; + }; -// #include "type_traits.h" + template + struct column_result_t, void> { + using type = T; + }; -// #include "alias.h" + template + struct column_result_t, void> { + using type = R; + }; -// #include "error_code.h" + template + struct column_result_t, void> { + using type = bool; + }; -// #include "type_printer.h" + template + struct column_result_t, void> { + using type = bool; + }; -// #include "constraints.h" + template + struct column_result_t, void> { + using type = bool; + }; -// #include "field_printer.h" + template + struct column_result_t, void> : column_result_t {}; + } +} -#include // std::string -#include // std::stringstream -#include // std::vector -#include // std::shared_ptr, std::unique_ptr -#ifndef SQLITE_ORM_OMITS_CODECVT -#include // std::wstring_convert -#include // std::codecvt_utf8_utf16 -#endif -// #include "functional/cxx_optional.h" +// #include "mapped_type_proxy.h" -// #include "functional/cxx_universal.h" +// #include "sync_schema_result.h" -// #include "functional/cxx_type_traits_polyfill.h" +#include -// #include "is_std_ptr.h" +_EXPORT_SQLITE_ORM namespace sqlite_orm { -// #include "type_traits.h" + enum class sync_schema_result { -namespace sqlite_orm { + /** + * created new table, table with the same tablename did not exist + */ + new_table_created, - /** - * Is used to print members mapped to objects in storage_t::dump member function. - * Other developers can create own specialization to map custom types - */ - template - struct field_printer; + /** + * table schema is the same as storage, nothing to be done + */ + already_in_sync, - namespace internal { - /* - * Implementation note: the technique of indirect expression testing is because - * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. - * It must also be a type that differs from those for `is_preparable_v`, `is_bindable_v`. + /** + * removed excess columns in table (than storage) without dropping a table */ - template - struct indirectly_test_printable; + old_columns_removed, - template - SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v = false; - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_printable_v{})>>> = true; + /** + * lacking columns in table (than storage) added without dropping a table + */ + new_columns_added, - template - struct is_printable : polyfill::bool_constant> {}; - } + /** + * both old_columns_removed and new_columns_added + */ + new_columns_added_and_old_columns_removed, - template - struct field_printer> { - std::string operator()(const T& t) const { - std::stringstream ss; - ss << t; - return ss.str(); - } + /** + * old table is dropped and new is recreated. Reasons : + * 1. delete excess columns in the table than storage if preseve = false + * 2. Lacking columns in the table cannot be added due to NULL and DEFAULT constraint + * 3. Reasons 1 and 2 both together + * 4. data_type mismatch between table and storage. + */ + dropped_and_recreated, }; - /** - * Upgrade to integer is required when using unsigned char(uint8_t) - */ - template<> - struct field_printer { - std::string operator()(const unsigned char& t) const { - std::stringstream ss; - ss << +t; - return ss.str(); + inline std::ostream& operator<<(std::ostream& os, sync_schema_result value) { + switch(value) { + case sync_schema_result::new_table_created: + return os << "new table created"; + case sync_schema_result::already_in_sync: + return os << "table and storage is already in sync."; + case sync_schema_result::old_columns_removed: + return os << "old excess columns removed"; + case sync_schema_result::new_columns_added: + return os << "new columns added"; + case sync_schema_result::new_columns_added_and_old_columns_removed: + return os << "old excess columns removed and new columns added"; + case sync_schema_result::dropped_and_recreated: + return os << "old table dropped and recreated"; } - }; + return os; + } +} - /** - * Upgrade to integer is required when using signed char(int8_t) - */ - template<> - struct field_printer { - std::string operator()(const signed char& t) const { - std::stringstream ss; - ss << +t; - return ss.str(); - } - }; +// #include "table_info.h" - /** - * char is neither signed char nor unsigned char so it has its own specialization - */ - template<> - struct field_printer { - std::string operator()(const char& t) const { - std::stringstream ss; - ss << +t; - return ss.str(); - } - }; +#include // std::string - template - struct field_printer> { - std::string operator()(std::string string) const { - return string; - } - }; +// #include "functional/cxx_universal.h" - template<> - struct field_printer, void> { - std::string operator()(const std::vector& t) const { - std::stringstream ss; - ss << std::hex; - for(auto c: t) { - ss << c; - } - return ss.str(); - } - }; -#ifndef SQLITE_ORM_OMITS_CODECVT - /** - * Specialization for std::wstring (UTF-16 assumed). - */ - template - struct field_printer> { - std::string operator()(const std::wstring& wideString) const { - std::wstring_convert> converter; - return converter.to_bytes(wideString); - } - }; -#endif // SQLITE_ORM_OMITS_CODECVT - template<> - struct field_printer { - std::string operator()(const nullptr_t&) const { - return "NULL"; - } - }; -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template<> - struct field_printer { - std::string operator()(const std::nullopt_t&) const { - return "NULL"; - } - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct field_printer, - internal::is_printable>>::value>> { - using unqualified_type = std::remove_cv_t; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - std::string operator()(const T& t) const { - if(t) { - return field_printer()(*t); - } else { - return field_printer{}(nullptr); - } - } + struct table_info { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; + +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) + table_info(decltype(cid) cid_, + decltype(name) name_, + decltype(type) type_, + decltype(notnull) notnull_, + decltype(dflt_value) dflt_value_, + decltype(pk) pk_) : + cid(cid_), + name(std::move(name_)), type(std::move(type_)), notnull(notnull_), dflt_value(std::move(dflt_value_)), + pk(pk_) {} +#endif }; -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct field_printer< - T, - std::enable_if_t, - internal::is_printable>>>> { - using unqualified_type = std::remove_cv_t; + struct table_xinfo { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; + int hidden = 0; // different than 0 => generated_always_as() - TODO verify - std::string operator()(const T& t) const { - if(t.has_value()) { - return field_printer()(*t); - } else { - return field_printer{}(std::nullopt); - } - } +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) + table_xinfo(decltype(cid) cid_, + decltype(name) name_, + decltype(type) type_, + decltype(notnull) notnull_, + decltype(dflt_value) dflt_value_, + decltype(pk) pk_, + decltype(hidden) hidden_) : + cid(cid_), + name(std::move(name_)), type(std::move(type_)), notnull(notnull_), dflt_value(std::move(dflt_value_)), + pk(pk_), hidden{hidden_} {} +#endif }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED } -// #include "rowid.h" +// #include "storage_impl.h" -// #include "operators.h" +#include // std::string + +// #include "functional/cxx_universal.h" +// ::size_t +// #include "functional/static_magic.h" + +// #include "functional/index_sequence_util.h" -// #include "select_constraints.h" +// #include "tuple_helper/tuple_traits.h" -// #include "core_functions.h" +// #include "tuple_helper/tuple_filter.h" -// #include "conditions.h" +// #include "tuple_helper/tuple_iteration.h" -// #include "statement_binder.h" +// #include "type_traits.h" -// #include "column_result.h" +// #include "select_constraints.h" -#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of -#include // std::reference_wrapper +// #include "cte_types.h" -// #include "functional/cxx_universal.h" -// ::nullptr_t -// #include "functional/cxx_type_traits_polyfill.h" +// #include "schema/column.h" -// #include "functional/mpl.h" +// #include "schema/table.h" -// #include "tuple_helper/tuple_traits.h" +#include // std::string +#include // std::remove_const, std::is_member_pointer, std::true_type, std::false_type +#include // std::vector +#include // std::tuple_element +#include // std::forward, std::move -// #include "tuple_helper/tuple_fy.h" +// #include "../functional/cxx_universal.h" +// ::size_t +// #include "../functional/cxx_type_traits_polyfill.h" -// #include "tuple_helper/tuple_filter.h" +// #include "../functional/cxx_functional_polyfill.h" -// #include "tuple_helper/tuple_transformer.h" +// #include "../functional/static_magic.h" -// #include "tuple_helper/same_or_void.h" +// #include "../functional/mpl.h" -// #include "type_traits.h" +// #include "../functional/index_sequence_util.h" -// #include "member_traits/member_traits.h" +// #include "../tuple_helper/tuple_filter.h" -// #include "mapped_type_proxy.h" +// #include "../tuple_helper/tuple_traits.h" -#include // std::remove_const +// #include "../tuple_helper/tuple_iteration.h" -// #include "type_traits.h" +// #include "../tuple_helper/tuple_transformer.h" -// #include "table_reference.h" +// #include "../member_traits/member_traits.h" -// #include "alias_traits.h" +// #include "../typed_comparator.h" namespace sqlite_orm { namespace internal { - /** - * If T is a table reference or recordset alias then the typename mapped_type_proxy::type is the unqualified aliased type, - * otherwise unqualified T. - */ - template - struct mapped_type_proxy : std::remove_const {}; - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - struct mapped_type_proxy : R {}; -#endif - - template - struct mapped_type_proxy> : std::remove_const> {}; - - template - using mapped_type_proxy_t = typename mapped_type_proxy::type; + template + bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { + return false; + } + template + bool compare_any(const O& lhs, const O& rhs) { + return lhs == rhs; + } } } -// #include "core_functions.h" +// #include "../type_traits.h" -// #include "select_constraints.h" +// #include "../alias_traits.h" -// #include "operators.h" +// #include "../constraints.h" -// #include "rowid.h" +// #include "../table_info.h" -// #include "column_result_proxy.h" +// #include "index.h" -// #include "alias.h" +#include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t +#include // std::string +#include // std::forward -// #include "cte_types.h" +// #include "../functional/cxx_universal.h" -// #include "storage_traits.h" +// #include "../tuple_helper/tuple_traits.h" -#include // std::tuple +// #include "../indexed_column.h" -// #include "functional/cxx_type_traits_polyfill.h" +#include // std::string +#include // std::move -// #include "tuple_helper/tuple_filter.h" +// #include "functional/cxx_universal.h" -// #include "tuple_helper/tuple_transformer.h" +// #include "ast/where.h" -// #include "type_traits.h" +_EXPORT_SQLITE_ORM namespace sqlite_orm { -// #include "storage_lookup.h" + namespace internal { -// #include "schema/column.h" + template + struct indexed_column_t { + using column_type = C; -namespace sqlite_orm { - namespace internal { +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + indexed_column_t(column_type _column_or_expression) : + column_or_expression(std::move(_column_or_expression)) {} +#endif - namespace storage_traits { + column_type column_or_expression; + std::string _collation_name; + int _order = 0; // -1 = desc, 1 = asc, 0 = not specified - /** - * DBO - db object (table) - */ - template - struct storage_mapped_columns_impl - : tuple_transformer, is_column>, field_type_t> {}; + indexed_column_t collate(std::string name) { + auto res = std::move(*this); + res._collation_name = std::move(name); + return res; + } - template<> - struct storage_mapped_columns_impl { - using type = std::tuple<>; - }; + indexed_column_t asc() { + auto res = std::move(*this); + res._order = 1; + return res; + } - /** - * DBOs - db_objects_tuple type - * Lookup - mapped or unmapped data type - */ - template - struct storage_mapped_columns : storage_mapped_columns_impl> {}; + indexed_column_t desc() { + auto res = std::move(*this); + res._order = -1; + return res; + } + }; - /** - * DBO - db object (table) - */ - template - struct storage_mapped_column_expressions_impl - : tuple_transformer, is_column>, column_field_expression_t> {}; + template + indexed_column_t make_indexed_column(C col) { + return {std::move(col)}; + } - template<> - struct storage_mapped_column_expressions_impl { - using type = std::tuple<>; - }; + template + where_t make_indexed_column(where_t wher) { + return std::move(wher); + } - /** - * DBOs - db_objects_tuple type - * Lookup - mapped or unmapped data type - */ - template - struct storage_mapped_column_expressions - : storage_mapped_column_expressions_impl> {}; + template + indexed_column_t make_indexed_column(indexed_column_t col) { + return std::move(col); } } + + /** + * Use this function to specify indexed column inside `make_index` function call. + * Example: make_index("index_name", indexed_column(&User::id).asc()) + */ + template + internal::indexed_column_t indexed_column(C column_or_expression) { + return {std::move(column_or_expression)}; + } } -// #include "function.h" +// #include "../table_type_of.h" -#include // std::enable_if, std::is_member_function_pointer, std::is_function, std::remove_const, std::decay, std::is_convertible, std::is_same, std::false_type, std::true_type -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED -#include // std::copy_constructible -#endif -#include // std::tuple, std::tuple_size, std::tuple_element -#include // std::min, std::copy_n -#include // std::move, std::forward +_EXPORT_SQLITE_ORM namespace sqlite_orm { -// #include "functional/cxx_universal.h" -// ::size_t, ::nullptr_t -// #include "functional/cxx_type_traits_polyfill.h" + namespace internal { -// #include "functional/cstring_literal.h" + struct index_base { + std::string name; + bool unique = false; -// #include "functional/function_traits.h" +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} +#endif + }; -// #include "cxx_type_traits_polyfill.h" + template + struct index_t : index_base { + using elements_type = std::tuple; + using object_type = void; + using table_mapped_type = T; -// #include "mpl.h" +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + index_t(std::string name_, bool unique_, elements_type elements_) : + index_base{std::move(name_), unique_}, elements(std::move(elements_)) {} +#endif -namespace sqlite_orm { - namespace internal { - /* - * Define nested typenames: - * - return_type - * - arguments_tuple - * - signature_type - */ - template - struct function_traits; + elements_type elements; + }; + } - /* - * A function's return type - */ - template - using function_return_type_t = typename function_traits::return_type; + template + internal::index_t()))...> make_index(std::string name, + Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } + + template + internal::index_t>>, + decltype(internal::make_indexed_column(std::declval()))...> + make_index(std::string name, Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } - /* - * A function's arguments tuple - */ - template - class Tuple, - template class ProjectOp = polyfill::type_identity_t> - using function_arguments = typename function_traits::template arguments_tuple; + template + internal::index_t>>, + decltype(internal::make_indexed_column(std::declval()))...> + make_unique_index(std::string name, Cols... cols) { + using cols_tuple = std::tuple; + static_assert(internal::count_tuple::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), true, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); + } +} - /* - * A function's signature - */ - template - using function_signature_type_t = typename function_traits::signature_type; +// #include "column.h" - template - struct function_traits { - using return_type = R; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - template class Tuple, template class ProjectOp> - using arguments_tuple = Tuple...>; + namespace internal { - using signature_type = R(Args...); - }; + 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>; - // non-exhaustive partial specializations of `function_traits` +#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; - template - struct function_traits : function_traits { - using signature_type = R(Args...) const; - }; + /** + * 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 -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct function_traits : function_traits { - using signature_type = R(Args...) noexcept; - }; + struct basic_table { - template - struct function_traits : function_traits { - using signature_type = R(Args...) const noexcept; + /** + * Table name. + */ + std::string name; }; -#endif - - /* - * Pick signature of function pointer - */ - template - struct function_traits : function_traits {}; - /* - * Pick signature of function reference + /** + * Table definition. */ - template - struct function_traits : function_traits {}; + 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; - /* - * Pick signature of pointer-to-member function - */ - template - struct function_traits : function_traits {}; - } -} + static constexpr bool is_without_rowid_v = WithoutRowId; -// #include "type_traits.h" + using is_without_rowid = polyfill::bool_constant; -// #include "tags.h" + elements_type elements; -namespace sqlite_orm { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + table_t(std::string name_, elements_type elements_) : + basic_table{std::move(name_)}, elements{std::move(elements_)} {} +#endif - struct arg_values; + table_t without_rowid() const { + return {this->name, this->elements}; + } - // note (internal): forward declare even if `SQLITE_VERSION_NUMBER < 3020000` in order to simplify coding below - template - struct pointer_arg; - // note (internal): forward declare even if `SQLITE_VERSION_NUMBER < 3020000` in order to simplify coding below - template - class pointer_binding; + /* + * 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()); + } - namespace internal { - template - using scalar_call_function_t = decltype(&F::operator()); + /* + * 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()); + } - template - using aggregate_step_function_t = decltype(&F::step); + /* + * 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()); + } - template - using aggregate_fin_function_t = decltype(&F::fin); + /** + * 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 - SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v = false; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v>> = true; + 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_any(column.setter, memberPointer)) { + res = &polyfill::invoke(column.member_pointer, object); + } + })); + return res; + } - template - struct is_scalar_udf : polyfill::bool_constant> {}; + const basic_generated_always::storage_type* + find_column_generated_storage_type(const std::string& name) const { + const basic_generated_always::storage_type* result = nullptr; +#if SQLITE_VERSION_NUMBER >= 3031000 + iterate_tuple(this->elements, + col_index_sequence_with{}, + [&result, &name](auto& column) { + if(column.name != name) { + return; + } + using generated_op_index_sequence = + filter_tuple_sequence_t, + is_generated_always>; + constexpr size_t opIndex = index_sequence_value_at<0>(generated_op_index_sequence{}); + result = &std::get(column.constraints).storage; + }); +#else + (void)name; +#endif + return result; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_udf_v = false; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_udf_v< - F, - polyfill::void_t, - aggregate_fin_function_t, - std::enable_if_t>::value>, - std::enable_if_t>::value>>> = - true; + /** + * 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); + } - template - struct is_aggregate_udf : polyfill::bool_constant> {}; + 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; + } - template - struct function; - } + std::vector primary_key_column_names() const { + using pkcol_index_sequence = col_index_sequence_with; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** @short Specifies that a type is a function signature (i.e. a function in the C++ type system). - */ - template - concept orm_function_sig = std::is_function_v; + if(pkcol_index_sequence::size() > 0) { + return create_from_tuple>(this->elements, + pkcol_index_sequence{}, + &column_identifier::name); + } else { + return this->composite_key_columns_names(); + } + } - /** @short Specifies that a type is a classic function object. - * - * A classic function object meets the following requirements: - * - defines a single call operator `F::operator()` - * - isn't a traditional sqlite_orm scalar function (having a static `F::name()` function - */ - template - concept orm_classic_function_object = - ((!requires { typename F::is_transparent; }) && (requires { &F::operator(); }) && - /*rule out sqlite_orm scalar function*/ - (!requires { F::name(); })); + 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); + }); + } - /** @short Specifies that a type is a user-defined scalar function. - * - * `UDF` must meet the following requirements: - * - `UDF::name()` static function - * - `UDF::operator()()` call operator - */ - template - concept orm_scalar_udf = requires { - UDF::name(); - typename internal::scalar_call_function_t; - }; + 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; + } + }); + } - /** @short Specifies that a type is a user-defined aggregate function. - * - * `UDF` must meet the following requirements: - * - `UDF::name()` static function - * - `UDF::step()` member function - * - `UDF::fin()` member function - */ - template - concept orm_aggregate_udf = requires { - UDF::name(); - typename internal::aggregate_step_function_t; - typename internal::aggregate_fin_function_t; - requires std::is_member_function_pointer_v>; - requires std::is_member_function_pointer_v>; - }; + /** + * 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 m) const { + const std::string* res = nullptr; + using field_type = member_field_type_t; + iterate_tuple(this->elements, + col_index_sequence_with_field_type{}, + [&res, m](auto& c) { + if(compare_any(c.member_pointer, m) || compare_any(c.setter, m)) { + res = &c.name; + } + }); + return res; + } - /** @short Specifies that a type is a framed user-defined scalar function. - */ - template - concept orm_scalar_function = (polyfill::is_specialization_of_v, internal::function> && - orm_scalar_udf); + /** + * Call passed lambda with all defined foreign keys. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_foreign_key(L&& lambda) const { + using fk_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, fk_index_sequence{}, lambda); + } - /** @short Specifies that a type is a framed user-defined aggregate function. - */ - template - concept orm_aggregate_function = (polyfill::is_specialization_of_v, internal::function> && - orm_aggregate_udf); + template + void for_each_foreign_key_to(L&& lambda) const { + using fk_index_sequence = filter_tuple_sequence_t; + using filtered_index_sequence = filter_tuple_sequence_t::template fn, + target_type_t, + fk_index_sequence>; + iterate_tuple(this->elements, filtered_index_sequence{}, lambda); + } - /** @short Specifies that a type is a framed and quoted user-defined scalar function. - */ - template - concept orm_quoted_scalar_function = requires(const Q& quotedF) { - quotedF.name(); - quotedF.callable(); - }; -#endif + /** + * 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 { + using col_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->elements, col_index_sequence{}, lambda); + } - namespace internal { - template - struct callable_arguments_impl; + /** + * 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); + } - template - struct callable_arguments_impl> { - using args_tuple = function_arguments, std::tuple, std::decay_t>; - using return_type = function_return_type_t>; - }; + /** + * 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); + } - template - struct callable_arguments_impl> { - using args_tuple = function_arguments, std::tuple, std::decay_t>; - using return_type = function_return_type_t>; + std::vector get_table_info() const; }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - requires(std::is_function_v) - struct callable_arguments_impl { - using args_tuple = function_arguments; - using return_type = std::decay_t>; - }; -#endif + template + struct is_table : std::false_type {}; - template - struct callable_arguments : callable_arguments_impl {}; + template + struct is_table> : std::true_type {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /* - * Bundle of type and name of a quoted user-defined function. - */ - template - struct udf_holder : private std::string { - using udf_type = UDF; + 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; - using std::string::basic_string; + static constexpr bool is_without_rowid_v = false; + using is_without_rowid = polyfill::bool_constant; - const std::string& operator()() const { - return *this; - } - }; -#endif + module_details_type module_details; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /* - * Bundle of type and name of a traditional sqlite_orm user-defined function. - */ - template - requires(requires { UDF::name(); }) - struct udf_holder -#else - /* - * Bundle of type and name of a traditional sqlite_orm user-defined function. - */ - template - struct udf_holder +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + virtual_table_t(std::string name, module_details_type module_details) : + basic_table{std::move(name)}, module_details{std::move(module_details)} {} #endif - { - using udf_type = UDF; - template>::value, bool> = true> - decltype(auto) operator()() const { - return UDF::name(); + /** + * 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); } - template::value, bool> = true> - std::string operator()() const { - return std::string{UDF::name()}; + /** + * 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); } - }; - - /* - * Represents a call of a user-defined function. - */ - template - struct function_call { - using udf_type = UDF; - using args_tuple = std::tuple; - udf_holder name; - args_tuple callArgs; + /** + * 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 - SQLITE_ORM_INLINE_VAR constexpr bool - is_operator_argument_v::value>> = true; - - template - struct unpacked_arg { - using type = T; - }; - template - struct unpacked_arg> { - using type = typename callable_arguments::return_type; - }; - template - using unpacked_arg_t = typename unpacked_arg::type; + struct is_virtual_table : std::false_type {}; - template - SQLITE_ORM_CONSTEVAL bool expected_pointer_value() { - static_assert(polyfill::always_false_v, "Expected a pointer value for I-th argument"); - return false; - } + template + struct is_virtual_table> : std::true_type {}; - template - constexpr bool is_same_pvt_v = expected_pointer_value(); +#if SQLITE_VERSION_NUMBER >= 3009000 + template + struct using_fts5_t { + using object_type = T; + using columns_type = std::tuple; - // Always allow binding nullptr to a pointer argument - template - constexpr bool is_same_pvt_v> = true; - // Always allow binding nullptr to a pointer argument - template - constexpr bool is_same_pvt_v, pointer_binding, void> = true; + columns_type columns; - template - SQLITE_ORM_CONSTEVAL bool assert_same_pointer_data_type() { - constexpr bool valid = std::is_convertible::value; - static_assert(valid, "Pointer data types of I-th argument do not match"); - return valid; - } + using_fts5_t(columns_type columns) : columns(std::move(columns)) {} -#if __cplusplus >= 201703L // C++17 or later - template - SQLITE_ORM_CONSTEVAL bool assert_same_pointer_tag() { - constexpr bool valid = Binding == PointerArg; - static_assert(valid, "Pointer types (tags) of I-th argument do not match"); - return valid; - } - template - constexpr bool - is_same_pvt_v> = - assert_same_pointer_tag() && - assert_same_pointer_data_type(); -#else - template - constexpr bool assert_same_pointer_tag() { - constexpr bool valid = Binding::value == PointerArg::value; - static_assert(valid, "Pointer types (tags) of I-th argument do not match"); - return valid; - } + /** + * 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); + } - template - constexpr bool - is_same_pvt_v> = - assert_same_pointer_tag(); + /** + * 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 { + using col_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->columns, col_index_sequence{}, lambda); + } + }; #endif - // not a pointer value, currently leave it unchecked - template - SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::false_type) { - return true; + 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_any(memberPointer, column.member_pointer) || compare_any(memberPointer, column.setter)) { + res = true; + } + }); + }); + return res; } - // check the type of pointer values - template - SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::true_type) { - return is_same_pvt_v; + template + bool exists_in_composite_primary_key(const virtual_table_t& /*virtualTable*/, + const column_field& /*column*/) { + return false; } + } - template - SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { - return true; - } - template - SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant) { - using func_param_type = std::tuple_element_t; - using call_arg_type = unpacked_arg_t>; +#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"); -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - constexpr bool valid = validate_pointer_value_type, - unpacked_arg_t>>( - polyfill::bool_constant < (polyfill::is_specialization_of_v) || - (polyfill::is_specialization_of_v) > {}); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); + } - return validate_pointer_value_types(polyfill::index_constant{}) && valid; -#else - return validate_pointer_value_types(polyfill::index_constant{}) && - validate_pointer_value_type, - unpacked_arg_t>>( - polyfill::bool_constant < (polyfill::is_specialization_of_v) || - (polyfill::is_specialization_of_v) > {}); -#endif - } + template + internal::using_fts5_t using_fts5(Cs... columns) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); - /* - * Note: Currently the number of call arguments is checked and whether the types of pointer values match, - * but other call argument types are not checked against the parameter types of the function. - */ - template -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - SQLITE_ORM_CONSTEVAL void check_function_call() { -#else - void check_function_call() { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); + } #endif - using call_args_tuple = std::tuple; - using function_params_tuple = typename callable_arguments::args_tuple; - constexpr size_t callArgsCount = std::tuple_size::value; - constexpr size_t functionParamsCount = std::tuple_size::value; - static_assert(std::is_same>::value || - (callArgsCount == functionParamsCount && - validate_pointer_value_types( - polyfill::index_constant{})), - "Check the number and types of the function call arguments"); - } - /* - * Generator of a user-defined function call in a sql query expression. - * - * Use the variable template `func<>` to instantiate. - * - * Calling the function captures the parameters in a `function_call` node. - */ - template - struct function { - using udf_type = UDF; - using callable_type = UDF; + /** + * 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"); - /* - * Generates the SQL function call. - */ - template - function_call operator()(CallArgs... callArgs) const { - check_function_call(); - return {this->udf_holder(), {std::forward(callArgs)...}}; - } + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } - constexpr auto udf_holder() const { - return internal::udf_holder{}; - } + /** + * Factory function for a table definition. + * + * The mapped object type is explicitly specified. + */ + template + internal::table_t make_table(std::string name, Cs... args) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); - // returns a character range - constexpr auto name() const { - return this->udf_holder()(); - } - }; + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /* - * Generator of a user-defined function call in a sql query expression. - * - * Use the string literal operator template `""_scalar.quote()` to quote - * a freestanding function, stateless lambda or function object. - * - * Calling the function captures the parameters in a `function_call` node. - * - * Internal note: - * 1. Captures and represents a function [pointer or object], especially one without side effects. - * If `F` is a stateless function object, `quoted_scalar_function::callable()` returns the original function object, - * otherwise it is assumed to have possibe side-effects and `quoted_scalar_function::callable()` returns a copy. - * 2. The nested `udf_type` typename is deliberately chosen to be the function signature, - * and will be the abstracted version of the user-defined function. - */ - template - struct quoted_scalar_function { - using udf_type = Sig; - using callable_type = F; + /** + * Factory function for a table definition. + * + * 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)...); + } +#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" - /* - * Generates the SQL function call. - */ - template - function_call operator()(CallArgs... callArgs) const { - check_function_call(); - return {this->udf_holder(), {std::forward(callArgs)...}}; - } +// interface functions +namespace sqlite_orm { + namespace internal { - /* - * Return original `udf` if stateless or a copy of it otherwise - */ - constexpr decltype(auto) callable() const { - if constexpr(stateless) { - return (this->udf); - } else { - // non-const copy - return F(this->udf); - } - } + template + using tables_index_sequence = filter_tuple_sequence_t; - constexpr auto udf_holder() const { - return internal::udf_holder{this->name()}; - } + template = true> + int foreign_keys_count(const DBOs& dbObjects) { + int res = 0; + iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { + res += table.template count_of(); + }); + return res; + } - constexpr auto name() const { - return this->nme; - } + template> + decltype(auto) lookup_table_name(const DBOs& dbObjects) { + return static_if::value>( + [](const auto& dbObjects) -> const std::string& { + return pick_table(dbObjects).name; + }, + empty_callable)(dbObjects); + } - template - consteval quoted_scalar_function(const char (&name)[N], Args&&... constructorArgs) : - udf(std::forward(constructorArgs)...) { - std::copy_n(name, N, this->nme); - } + /** + * Find column name by its type and member pointer. + */ + template = true> + const std::string* find_column_name(const DBOs& dbObjects, F O::*field) { + return pick_table(dbObjects).find_column_name(field); + } - F udf; - char nme[N]; - }; + /** + * Materialize column pointer: + * 1. by explicit object type and member pointer. + * 2. by moniker and member pointer. + */ + template = true> + constexpr decltype(auto) materialize_column_pointer(const DBOs&, const column_pointer& cp) { + return cp.field; + } - template - struct quoted_function_builder : cstring_literal { - using cstring_literal::cstring_literal; +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + /** + * 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()` + */ + template = true> + constexpr decltype(auto) materialize_column_pointer(const DBOs&, + const column_pointer>&) { + using table_type = storage_pick_table_t; + using cte_mapper_type = cte_mapper_type_t; - /* - * From a freestanding function, possibly overloaded. - */ - template - [[nodiscard]] consteval auto quote(F* callable) const { - return quoted_scalar_function{this->cstr, std::move(callable)}; - } + // lookup ColAlias in the final column references + using colalias_index = + find_tuple_type>; + static_assert(colalias_index::value < std::tuple_size_v, + "No such column mapped into the CTE."); - /* - * From a classic function object instance. - */ - template - requires(orm_classic_function_object && (stateless || std::copy_constructible)) - [[nodiscard]] consteval auto quote(F callable) const { - using Sig = function_signature_type_t; - // detect whether overloaded call operator can be picked using `Sig` - using call_operator_type = decltype(static_cast(&F::operator())); - return quoted_scalar_function{this->cstr, std::move(callable)}; - } + return &aliased_field< + ColAlias, + std::tuple_element_t>::field; + } +#endif - /* - * From a function object instance, picking the overloaded call operator. - */ - template - requires((stateless || std::copy_constructible)) - [[nodiscard]] consteval auto quote(F callable) const { - // detect whether overloaded call operator can be picked using `Sig` - using call_operator_type = decltype(static_cast(&F::operator())); - return quoted_scalar_function{this->cstr, std::move(callable)}; - } + /** + * Find column name by: + * 1. by explicit object type and member pointer. + * 2. by moniker and member pointer. + */ + template = true> + const std::string* find_column_name(const DBOs& dbObjects, const column_pointer& cp) { + auto field = materialize_column_pointer(dbObjects, cp); + return pick_table(dbObjects).find_column_name(field); + } - /* - * From a classic function object type. - */ - template - requires(stateless || std::copy_constructible) - [[nodiscard]] consteval auto quote(Args&&... constructorArgs) const { - using Sig = function_signature_type_t; - return quoted_scalar_function{this->cstr, std::forward(constructorArgs)...}; - } +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + /** + * Find column name by: + * 3. by moniker and alias_holder<>. + */ + template = true> + constexpr decltype(auto) find_column_name(const DBOs& dboObjects, + const column_pointer>&) { + using table_type = storage_pick_table_t; + using cte_mapper_type = cte_mapper_type_t; + using column_index_sequence = filter_tuple_sequence_t, is_column>; - /* - * From a function object type, picking the overloaded call operator. - */ - template - requires((stateless || std::copy_constructible)) - [[nodiscard]] consteval auto quote(Args&&... constructorArgs) const { - // detect whether overloaded call operator can be picked using `Sig` - using call_operator_type = decltype(static_cast(&F::operator())); - return quoted_scalar_function{this->cstr, std::forward(constructorArgs)...}; - } - }; + // note: even though the columns contain the [`aliased_field<>::*`] we perform the lookup using the column references. + // lookup ColAlias in the final column references + using colalias_index = + find_tuple_type>; + 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; + // however we have the column index already. + // lookup column in table_t<>'s elements + constexpr size_t ColIdx = index_sequence_value_at(column_index_sequence{}); + auto& table = pick_table(dboObjects); + return &std::get(table.elements).name; + } #endif } +} - /** @short Call a user-defined function. - * - * Note: Currently the number of call arguments is checked and whether the types of pointer values match, - * but other call argument types are not checked against the parameter types of the function. - * - * Example: - * struct IdFunc { int oeprator(int arg)() const { return arg; } }; - * // inline: - * select(func(42)); - * // As this is a variable template, you can frame the user-defined function and define a variable for syntactic sugar and legibility: - * inline constexpr orm_scalar_function auto idfunc = func; - * select(idfunc(42)); - * - */ - template -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - requires(orm_scalar_udf || orm_aggregate_udf) +// #include "journal_mode.h" + +#include // std::back_inserter +#include // std::string +#include // std::unique_ptr +#include // std::array +#include // std::transform +#include // std::toupper + +#if defined(_WINNT_) +// DELETE is a macro defined in the Windows SDK (winnt.h) +#pragma push_macro("DELETE") +#undef DELETE #endif - SQLITE_ORM_INLINE_VAR constexpr internal::function func{}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - inline namespace literals { - /* @short Create a scalar function from a freestanding function, stateless lambda or function object, - * and call such a user-defined function. - * - * If you need to pick a function or method from an overload set, or pick a template function you can - * specify an explicit function signature in the call to `from()`. - * - * Examples: - * // freestanding function from a library - * constexpr orm_quoted_scalar_function auto clamp_int_f = "clamp_int"_scalar.quote(std::clamp); - * // stateless lambda - * constexpr orm_quoted_scalar_function auto is_fatal_error_f = "IS_FATAL_ERROR"_scalar.quote([](unsigned long errcode) { - * return errcode != 0; - * }); - * // function object instance - * constexpr orm_quoted_scalar_function auto equal_to_int_f = "equal_to"_scalar.quote(std::equal_to{}); - * // function object - * constexpr orm_quoted_scalar_function auto equal_to_int_2_f = "equal_to"_scalar.quote>(); - * // pick function object's template call operator - * constexpr orm_quoted_scalar_function auto equal_to_int_3_f = "equal_to"_scalar.quote(std::equal_to{}); - * - * storage.create_scalar_function(); - * storage.create_scalar_function(); - * storage.create_scalar_function(); - * storage.create_scalar_function(); - * storage.create_scalar_function(); - * - * auto rows = storage.select(clamp_int_f(0, 1, 1)); - * auto rows = storage.select(is_fatal_error_f(1)); - * auto rows = storage.select(equal_to_int_f(1, 1)); - * auto rows = storage.select(equal_to_int_2_f(1, 1)); - * auto rows = storage.select(equal_to_int_3_f(1, 1)); - */ - template - [[nodiscard]] consteval auto operator"" _scalar() { - return builder; +_EXPORT_SQLITE_ORM namespace sqlite_orm { + + /** + * Caps case because of: + * 1) delete keyword; + * 2) https://www.sqlite.org/pragma.html#pragma_journal_mode original spelling + */ + enum class journal_mode : signed char { + DELETE = 0, + // An alternate enumeration value when using the Windows SDK that defines DELETE as a macro. + DELETE_ = DELETE, + TRUNCATE = 1, + PERSIST = 2, + MEMORY = 3, + WAL = 4, + OFF = 5, + }; + + namespace internal { + + inline const std::string& to_string(journal_mode j) { + static std::string res[] = { + "DELETE", + "TRUNCATE", + "PERSIST", + "MEMORY", + "WAL", + "OFF", + }; + return res[static_cast(j)]; + } + + inline std::unique_ptr journal_mode_from_string(const std::string& str) { + std::string upper_str; + std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) { + return static_cast(std::toupper(static_cast(c))); + }); + static std::array all = {{ + journal_mode::DELETE, + journal_mode::TRUNCATE, + journal_mode::PERSIST, + journal_mode::MEMORY, + journal_mode::WAL, + journal_mode::OFF, + }}; + for(auto j: all) { + if(to_string(j) == upper_str) { + return std::make_unique(j); + } + } + return {}; } } -#endif } -// #include "ast/special_keywords.h" +#if defined(_WINNT_) +#pragma pop_macro("DELETE") +#endif -namespace sqlite_orm { - namespace internal { - struct current_time_t {}; - struct current_date_t {}; - struct current_timestamp_t {}; - } +// #include "mapped_view.h" - inline internal::current_time_t current_time() { - return {}; - } +#include +#include // std::forward, std::move - inline internal::current_date_t current_date() { - return {}; - } +// #include "row_extractor.h" - inline internal::current_timestamp_t current_timestamp() { - return {}; - } -} +#include +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if +#include // std::atof, std::atoi, std::atoll +#include // std::strlen +#include // std::system_error +#include // std::string, std::wstring +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert +#include // std::codecvt_utf8_utf16 +#endif +#include // std::vector +#include // std::copy +#include // std::back_inserter +#include // std::tuple, std::tuple_size, std::tuple_element +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include +#endif -namespace sqlite_orm { +// #include "functional/cxx_universal.h" +// ::nullptr_t, ::size_t +// #include "functional/cxx_functional_polyfill.h" - namespace internal { +// #include "functional/static_magic.h" - /** - * Obtains the result type of expressions that form the columns of a select statement. - * - * This is a proxy class used to define what type must have result type depending on select - * arguments (member pointer, aggregate functions, etc). Below you can see specializations - * for different types. E.g. specialization for internal::length_t has `type` int cause - * LENGTH returns INTEGER in sqlite. Every column_result_t must have `type` type that equals - * c++ SELECT return type for T - * DBOs - db_objects_tuple type - * T - C++ type - * SFINAE - sfinae argument - */ - template - struct column_result_t { -#ifdef __FUNCTION__ - // produce an error message that reveals `T` and `DBOs` - static constexpr bool reveal() { - static_assert(polyfill::always_false_v, "T not found in DBOs - " __FUNCTION__); - } - static constexpr bool trigger = reveal(); -#endif - }; +// #include "tuple_helper/tuple_transformer.h" - template - using column_result_of_t = typename column_result_t::type; +// #include "column_result_proxy.h" - template - using column_result_for_tuple_t = - transform_tuple_t::template fn>; +// #include "arithmetic_tag.h" -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct column_result_t, void> { - using type = std::optional>; - }; +// #include "pointer_value.h" - template - struct column_result_t, void> { - using type = std::optional; - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "journal_mode.h" - template - struct column_result_t, void> { - using type = bool; - }; +// #include "error_code.h" - template - struct column_result_t, void> { - using type = bool; - }; +// #include "is_std_ptr.h" - template - struct column_result_t { - using type = std::string; - }; +// #include "type_traits.h" - template - struct column_result_t { - using type = std::string; - }; +_EXPORT_SQLITE_ORM namespace sqlite_orm { - template - struct column_result_t { - using type = std::string; - }; + /** + * Helper for casting values originating from SQL to C++ typed values, usually from rows of a result set. + * + * sqlite_orm provides specializations for known C++ types, users may define their custom specialization + * of this helper. + * + * @note (internal): Since row extractors are used in certain contexts with only one purpose at a time + * (e.g., converting a row result set but not function values or column text), + * there are factory functions that perform conceptual checking that should be used + * instead of directly creating row extractors. + * + * + */ + template + struct row_extractor { + /* + * Called during one-step query execution (one result row) for each column of a result row. + */ + V extract(const char* columnText) const = delete; - template - struct column_result_t> : member_field_type {}; + /* + * Called during multi-step query execution (result set) for each column of a result row. + */ + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; - template - struct column_result_t, void> { - using type = R; + /* + * Called before invocation of user-defined scalar or aggregate functions, + * in order to unbox dynamically typed SQL function values into a tuple of C++ function arguments. + */ + V extract(sqlite3_value* value) const = delete; + }; + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept orm_column_text_extractable = requires(const row_extractor& extractor, const char* columnText) { + { extractor.extract(columnText) } -> std::same_as; + }; + + template + concept orm_row_value_extractable = + requires(const row_extractor& extractor, sqlite3_stmt* stmt, int columnIndex) { + { extractor.extract(stmt, columnIndex) } -> std::same_as; }; - template - struct column_result_t, void> { - using type = R; - }; + template + concept orm_boxed_value_extractable = requires(const row_extractor& extractor, sqlite3_value* value) { + { extractor.extract(value) } -> std::same_as; + }; +#endif + + namespace internal { + /* + * Make a row extractor to be used for casting SQL column text to a C++ typed value. + */ + template +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_column_text_extractable) +#endif + row_extractor column_text_extractor() { + return {}; + } + + /* + * Make a row extractor to be used for converting a value from a SQL result row set to a C++ typed value. + */ + template +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_row_value_extractable) +#endif + row_extractor row_value_extractor() { + return {}; + } + + /* + * Make a row extractor to be used for unboxing a dynamically typed SQL value to a C++ typed value. + */ + template +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_boxed_value_extractable) +#endif + row_extractor boxed_value_extractor() { + return {}; + } + } + + template + int extract_single_value(void* data, int argc, char** argv, char**) { + auto& res = *(R*)data; + if(argc) { + const auto rowExtractor = internal::column_text_extractor(); + res = rowExtractor.extract(argv[0]); + } + return 0; + } + +#if SQLITE_VERSION_NUMBER >= 3020000 + /** + * Specialization for the 'pointer-passing interface'. + * + * @note The 'pointer-passing' interface doesn't support (and in fact prohibits) + * extracting pointers from columns. + */ + template + struct row_extractor, void> { + using V = pointer_arg; + + V extract(const char* columnText) const = delete; - template - struct column_result_t, void> { - using type = typename callable_arguments::return_type; - }; + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; - template - struct column_result_t, S, X, Rest...>, void> { - using type = std::unique_ptr>; - }; + V extract(sqlite3_value* value) const { + return {(P*)sqlite3_value_pointer(value, T::value)}; + } + }; - template - struct column_result_t, S, X>, void> { - using type = std::unique_ptr>; - }; + /** + * Undefine using pointer_binding<> for querying values + */ + template + struct row_extractor, void>; +#endif - template - struct column_result_t, void> { - using type = int; - }; + /** + * Specialization for arithmetic types. + */ + template + struct row_extractor::value>> { + V extract(const char* columnText) const { + return this->extract(columnText, tag()); + } - template - struct column_result_t { - using type = nullptr_t; - }; + V extract(sqlite3_stmt* stmt, int columnIndex) const { + return this->extract(stmt, columnIndex, tag()); + } - template - struct column_result_t { - using type = int; - }; + V extract(sqlite3_value* value) const { + return this->extract(value, tag()); + } - template - struct column_result_t, void> : column_result_t {}; + private: + using tag = arithmetic_tag_t; - template - struct column_result_t, void> : column_result_t {}; + V extract(const char* columnText, const int_or_smaller_tag&) const { + return static_cast(std::atoi(columnText)); + } - template - struct column_result_t, void> { - using type = std::string; - }; + V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { + return static_cast(sqlite3_column_int(stmt, columnIndex)); + } - template - struct column_result_t, void> { - using type = double; - }; + V extract(sqlite3_value* value, const int_or_smaller_tag&) const { + return static_cast(sqlite3_value_int(value)); + } - template - struct column_result_t, void> { - using type = double; - }; + V extract(const char* columnText, const bigint_tag&) const { + return static_cast(std::atoll(columnText)); + } - template - struct column_result_t, void> { - using type = double; - }; + V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { + return static_cast(sqlite3_column_int64(stmt, columnIndex)); + } - template - struct column_result_t, void> { - using type = double; - }; + V extract(sqlite3_value* value, const bigint_tag&) const { + return static_cast(sqlite3_value_int64(value)); + } - template - struct column_result_t, void> { - using type = double; - }; + V extract(const char* columnText, const real_tag&) const { + return static_cast(std::atof(columnText)); + } - template - struct column_result_t, void> { - using type = int; - }; + V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { + return static_cast(sqlite3_column_double(stmt, columnIndex)); + } - template - struct column_result_t, void> { - using type = int; - }; + V extract(sqlite3_value* value, const real_tag&) const { + return static_cast(sqlite3_value_double(value)); + } + }; - template - struct column_result_t, void> { - using type = int; - }; + /** + * Specialization for std::string. + */ + template + struct row_extractor::value>> { + T extract(const char* columnText) const { + if(columnText) { + return columnText; + } else { + return {}; + } + } - template - struct column_result_t, void> { - using type = int; - }; + T extract(sqlite3_stmt* stmt, int columnIndex) const { + if(auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex)) { + return cStr; + } else { + return {}; + } + } - template - struct column_result_t, void> { - using type = int; - }; + T extract(sqlite3_value* value) const { + if(auto cStr = (const char*)sqlite3_value_text(value)) { + return cStr; + } else { + return {}; + } + } + }; +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring. + */ + template<> + struct row_extractor { + std::wstring extract(const char* columnText) const { + if(columnText) { + std::wstring_convert> converter; + return converter.from_bytes(columnText); + } else { + return {}; + } + } - template - struct column_result_t { - using type = int64; - }; + std::wstring extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); + if(cStr) { + std::wstring_convert> converter; + return converter.from_bytes(cStr); + } else { + return {}; + } + } - template - struct column_result_t { - using type = int64; - }; + std::wstring extract(sqlite3_value* value) const { + if(auto cStr = (const wchar_t*)sqlite3_value_text16(value)) { + return cStr; + } else { + return {}; + } + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT - template - struct column_result_t { - using type = int64; - }; + template + struct row_extractor::value>> { + using unqualified_type = std::remove_cv_t; - template - struct column_result_t, void> { - using type = int64; - }; + V extract(const char* columnText) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_column_text_extractable) +#endif + { + if(columnText) { + const row_extractor rowExtractor{}; + return is_std_ptr::make(rowExtractor.extract(columnText)); + } else { + return {}; + } + } - template - struct column_result_t, void> { - using type = int64; - }; + V extract(sqlite3_stmt* stmt, int columnIndex) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_row_value_extractable) +#endif + { + auto type = sqlite3_column_type(stmt, columnIndex); + if(type != SQLITE_NULL) { + const row_extractor rowExtractor{}; + return is_std_ptr::make(rowExtractor.extract(stmt, columnIndex)); + } else { + return {}; + } + } - template - struct column_result_t, void> { - using type = int64; - }; + V extract(sqlite3_value* value) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_boxed_value_extractable) +#endif + { + auto type = sqlite3_value_type(value); + if(type != SQLITE_NULL) { + const row_extractor rowExtractor{}; + return is_std_ptr::make(rowExtractor.extract(value)); + } else { + return {}; + } + } + }; - template - struct column_result_t, void> : column_result_t {}; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct row_extractor>> { + using unqualified_type = std::remove_cv_t; - template - struct column_result_t, void> : column_result_t {}; + V extract(const char* columnText) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_column_text_extractable) +#endif + { + if(columnText) { + const row_extractor rowExtractor{}; + return std::make_optional(rowExtractor.extract(columnText)); + } else { + return std::nullopt; + } + } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template - struct column_result_t>, void> { - using table_type = storage_pick_table_t; - using cte_mapper_type = cte_mapper_type_t; + V extract(sqlite3_stmt* stmt, int columnIndex) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_row_value_extractable) +#endif + { + auto type = sqlite3_column_type(stmt, columnIndex); + if(type != SQLITE_NULL) { + const row_extractor rowExtractor{}; + return std::make_optional(rowExtractor.extract(stmt, columnIndex)); + } else { + return std::nullopt; + } + } - // lookup ColAlias in the final column references - using colalias_index = - find_tuple_type>; - static_assert(colalias_index::value < std::tuple_size_v, - "No such column mapped into the CTE."); - using type = std::tuple_element_t; - }; + V extract(sqlite3_value* value) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_boxed_value_extractable) #endif + { + auto type = sqlite3_value_type(value); + if(type != SQLITE_NULL) { + const row_extractor rowExtractor{}; + return std::make_optional(rowExtractor.extract(value)); + } else { + return std::nullopt; + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct column_result_t, void> - : conc_tuple>>...> {}; + template<> + struct row_extractor { + nullptr_t extract(const char* /*columnText*/) const { + return nullptr; + } - template - struct column_result_t, void> { - using type = structure>>...>>; - }; + nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { + return nullptr; + } - template - struct column_result_t> : column_result_t {}; + nullptr_t extract(sqlite3_value*) const { + return nullptr; + } + }; + /** + * Specialization for std::vector. + */ + template<> + struct row_extractor, void> { + std::vector extract(const char* columnText) const { + return {columnText, columnText + (columnText ? std::strlen(columnText) : 0)}; + } - template - struct column_result_t> { - using type = - polyfill::detected_t>; - static_assert(!std::is_same::value, - "Compound select statements must return a common type"); - }; + std::vector extract(sqlite3_stmt* stmt, int columnIndex) const { + auto bytes = static_cast(sqlite3_column_blob(stmt, columnIndex)); + auto len = static_cast(sqlite3_column_bytes(stmt, columnIndex)); + return {bytes, bytes + len}; + } - template - struct column_result_t> { - using type = typename T::result_type; - }; + std::vector extract(sqlite3_value* value) const { + auto bytes = static_cast(sqlite3_value_blob(value)); + auto len = static_cast(sqlite3_value_bytes(value)); + return {bytes, bytes + len}; + } + }; - template - struct column_result_t, void> { - using type = std::string; - }; + /** + * Specialization for journal_mode. + */ + template<> + struct row_extractor { + journal_mode extract(const char* columnText) const { + if(columnText) { + if(auto res = internal::journal_mode_from_string(columnText)) { + return std::move(*res); + } else { + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; + } + } else { + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; + } + } - /** - * Result for the most simple queries like `SELECT 1` - */ - template - struct column_result_t> { - using type = T; - }; + journal_mode extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); + return this->extract(cStr); + } - /** - * Result for the most simple queries like `SELECT 'ototo'` - */ - template - struct column_result_t { - using type = std::string; - }; + journal_mode extract(sqlite3_value* value) const = delete; + }; - template - struct column_result_t { - using type = std::string; - }; + namespace internal { - template - struct column_result_t, void> : column_result_t> {}; + /* + * Helper to extract a structure from a rowset. + */ + template + struct struct_extractor; - template - struct column_result_t, void> - : storage_traits::storage_mapped_columns> {}; +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + /* + * Returns a value-based row extractor for an unmapped type, + * returns a structure extractor for a table reference, tuple or named struct. + */ + template + auto make_row_extractor([[maybe_unused]] const DBOs& dbObjects) { + if constexpr(polyfill::is_specialization_of_v || + polyfill::is_specialization_of_v || is_table_reference_v) { + return struct_extractor{dbObjects}; + } else { + return row_value_extractor(); + } + } +#else + /* + * Overload for an unmapped type returns a common row extractor. + */ + template< + class R, + class DBOs, + std::enable_if_t, + polyfill::is_specialization_of, + is_table_reference>>::value, + bool> = true> + auto make_row_extractor(const DBOs& /*dbObjects*/) { + return row_value_extractor(); + } - template - struct column_result_t, void> { - using type = table_reference; - }; + /* + * Overload for a table reference, tuple or aggregate of column results returns a structure extractor. + */ + template, + polyfill::is_specialization_of, + is_table_reference>::value, + bool> = true> + struct_extractor make_row_extractor(const DBOs& dbObjects) { + return {dbObjects}; + } +#endif - template - struct column_result_t, void> { - using type = T; - }; + /** + * Specialization for a tuple of top-level column results. + */ + template + struct struct_extractor, DBOs> { + const DBOs& db_objects; - template - struct column_result_t, void> { - using type = R; - }; + std::tuple extract(const char* columnText) const = delete; - template - struct column_result_t, void> { - using type = bool; - }; + // note: expects to be called only from the top level, and therefore discards the index + std::tuple...> extract(sqlite3_stmt* stmt, + int&& /*nextColumnIndex*/ = 0) const { + int columnIndex = -1; + return {make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + } - template - struct column_result_t, void> { - using type = bool; - }; + // unused to date + std::tuple...> extract(sqlite3_stmt* stmt, int& columnIndex) const = delete; - template - struct column_result_t, void> { - using type = bool; + std::tuple extract(sqlite3_value* value) const = delete; }; - template - struct column_result_t, void> : column_result_t {}; - } -} - -// #include "mapped_type_proxy.h" - -// #include "sync_schema_result.h" + /** + * Specialization for an unmapped structure to be constructed ad-hoc from column results. + * + * This plays together with `column_result_of_t`, which returns `struct_t` as `structure` + */ + template + struct struct_extractor>, DBOs> { + const DBOs& db_objects; -// #include "table_info.h" + O extract(const char* columnText) const = delete; -// #include "storage_impl.h" + // note: expects to be called only from the top level, and therefore discards the index; + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + // see unit test tests/prepared_statement_tests/select.cpp/TEST_CASE("Prepared select")/SECTION("non-aggregate struct") + template = true> + O extract(sqlite3_stmt* stmt, int&& /*nextColumnIndex*/ = 0) const { + int columnIndex = -1; + return O{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + } -// #include "journal_mode.h" + template = true> + O extract(sqlite3_stmt* stmt, int&& /*nextColumnIndex*/ = 0) const { + int columnIndex = -1; + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + std::tuple t{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + return create_from_tuple(std::move(t), std::index_sequence_for{}); + } -// #include "mapped_view.h" + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + // see unit test tests/prepared_statement_tests/select.cpp/TEST_CASE("Prepared select")/SECTION("non-aggregate struct") + template = true> + O extract(sqlite3_stmt* stmt, int& columnIndex) const { + --columnIndex; + return O{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + } -#include -#include // std::forward, std::move + template = true> + O extract(sqlite3_stmt* stmt, int& columnIndex) const { + --columnIndex; + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + std::tuple t{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + return create_from_tuple(std::move(t), std::index_sequence_for{}); + } -// #include "row_extractor.h" + O extract(sqlite3_value* value) const = delete; + }; + } +} // #include "mapped_iterator.h" @@ -13551,7 +13237,7 @@ namespace sqlite_orm { #include // std::unique_ptr #include // std::integral_constant -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * Guard class which finalizes `sqlite3_stmt` in dtor @@ -13580,7 +13266,7 @@ namespace sqlite_orm { // #include "storage_lookup.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { @@ -13661,7 +13347,7 @@ namespace sqlite_orm { // #include "error_code.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** * Escape the provided character in the given string by doubling it. @@ -14020,7 +13706,7 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { @@ -14053,7 +13739,6 @@ namespace sqlite_orm { internal::dynamic_values_t values(std::vector vector) { return {{std::move(vector)}}; } - } // #include "table_reference.h" @@ -14069,7 +13754,7 @@ namespace sqlite_orm { // #include "../functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { #if SQLITE_VERSION_NUMBER >= 3024000 template @@ -14254,7 +13939,7 @@ namespace sqlite_orm { } -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { @@ -14353,319 +14038,317 @@ namespace sqlite_orm { } } -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm{ - namespace internal { + namespace internal{ - struct prepared_statement_base { - sqlite3_stmt* stmt = nullptr; - connection_ref con; + struct prepared_statement_base{sqlite3_stmt* stmt = nullptr; +connection_ref con; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} +prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} #endif - ~prepared_statement_base() { - sqlite3_finalize(this->stmt); - } +~prepared_statement_base() { + sqlite3_finalize(this->stmt); +} - std::string sql() const { - // note: sqlite3 internally checks for null before calling - // sqlite3_normalized_sql() or sqlite3_expanded_sql(), so check here, too, even if superfluous - if(const char* sql = sqlite3_sql(this->stmt)) { - return sql; - } else { - return {}; - } - } +std::string sql() const { + // note: sqlite3 internally checks for null before calling + // sqlite3_normalized_sql() or sqlite3_expanded_sql(), so check here, too, even if superfluous + if(const char* sql = sqlite3_sql(this->stmt)) { + return sql; + } else { + return {}; + } +} #if SQLITE_VERSION_NUMBER >= 3014000 - std::string expanded_sql() const { - // note: must check return value due to SQLITE_OMIT_TRACE - using char_ptr = std::unique_ptr>; - if(char_ptr sql{sqlite3_expanded_sql(this->stmt)}) { - return sql.get(); - } else { - return {}; - } - } +std::string expanded_sql() const { + // note: must check return value due to SQLITE_OMIT_TRACE + using char_ptr = std::unique_ptr>; + if(char_ptr sql{sqlite3_expanded_sql(this->stmt)}) { + return sql.get(); + } else { + return {}; + } +} #endif #if SQLITE_VERSION_NUMBER >= 3026000 and defined(SQLITE_ENABLE_NORMALIZE) - std::string normalized_sql() const { - if(const char* sql = sqlite3_normalized_sql(this->stmt)) { - return sql; - } else { - return {}; - } - } +std::string normalized_sql() const { + if(const char* sql = sqlite3_normalized_sql(this->stmt)) { + return sql; + } else { + return {}; + } +} #endif #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - std::string_view column_name(int index) const { - return sqlite3_column_name(stmt, index); - } +std::string_view column_name(int index) const { + return sqlite3_column_name(stmt, index); +} #endif - }; +} +; - template - struct prepared_statement_t : prepared_statement_base { - using expression_type = T; +template +struct prepared_statement_t : prepared_statement_base { + using expression_type = T; - expression_type expression; + expression_type expression; - prepared_statement_t(T expression_, sqlite3_stmt* stmt_, connection_ref con_) : - prepared_statement_base{stmt_, std::move(con_)}, expression(std::move(expression_)) {} + prepared_statement_t(T expression_, sqlite3_stmt* stmt_, connection_ref con_) : + prepared_statement_base{stmt_, std::move(con_)}, expression(std::move(expression_)) {} - prepared_statement_t(prepared_statement_t&& prepared_stmt) : - prepared_statement_base{prepared_stmt.stmt, std::move(prepared_stmt.con)}, - expression(std::move(prepared_stmt.expression)) { - prepared_stmt.stmt = nullptr; - } - }; + prepared_statement_t(prepared_statement_t&& prepared_stmt) : + prepared_statement_base{prepared_stmt.stmt, std::move(prepared_stmt.con)}, + expression(std::move(prepared_stmt.expression)) { + prepared_stmt.stmt = nullptr; + } +}; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_prepared_statement_v = - polyfill::is_specialization_of::value; +template +SQLITE_ORM_INLINE_VAR constexpr bool is_prepared_statement_v = + polyfill::is_specialization_of::value; - template - struct is_prepared_statement : polyfill::bool_constant> {}; +template +struct is_prepared_statement : polyfill::bool_constant> {}; - /** +/** * T - type of object to obtain from a database */ - template - struct get_all_t { - using type = T; - using return_type = R; +template +struct get_all_t { + using type = T; + using return_type = R; - using conditions_type = std::tuple; + using conditions_type = std::tuple; - conditions_type conditions; - }; + conditions_type conditions; +}; - template - struct get_all_pointer_t { - using type = T; - using return_type = R; +template +struct get_all_pointer_t { + using type = T; + using return_type = R; - using conditions_type = std::tuple; + using conditions_type = std::tuple; - conditions_type conditions; - }; + conditions_type conditions; +}; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct get_all_optional_t { - using type = T; - using return_type = R; +template +struct get_all_optional_t { + using type = T; + using return_type = R; - using conditions_type = std::tuple; + using conditions_type = std::tuple; - conditions_type conditions; - }; + conditions_type conditions; +}; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct update_all_t { - using set_type = S; - using conditions_type = std::tuple; +template +struct update_all_t { + using set_type = S; + using conditions_type = std::tuple; - static_assert(is_set::value, "update_all_t must have set or dynamic set as the first argument"); + static_assert(is_set::value, "update_all_t must have set or dynamic set as the first argument"); - set_type set; - conditions_type conditions; - }; + set_type set; + conditions_type conditions; +}; - template - struct remove_all_t { - using type = T; - using conditions_type = std::tuple; +template +struct remove_all_t { + using type = T; + using conditions_type = std::tuple; - conditions_type conditions; - }; + conditions_type conditions; +}; - template - struct get_t { - using type = T; - using ids_type = std::tuple; +template +struct get_t { + using type = T; + using ids_type = std::tuple; - ids_type ids; - }; + ids_type ids; +}; - template - struct get_pointer_t { - using type = T; - using ids_type = std::tuple; +template +struct get_pointer_t { + using type = T; + using ids_type = std::tuple; - ids_type ids; - }; + ids_type ids; +}; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct get_optional_t { - using type = T; - using ids_type = std::tuple; +template +struct get_optional_t { + using type = T; + using ids_type = std::tuple; - ids_type ids; - }; + ids_type ids; +}; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct update_t { - using type = T; +template +struct update_t { + using type = T; - type object; - }; + type object; +}; - template - struct remove_t { - using type = T; - using ids_type = std::tuple; +template +struct remove_t { + using type = T; + using ids_type = std::tuple; - ids_type ids; - }; + ids_type ids; +}; - template - struct insert_t { - using type = T; +template +struct insert_t { + using type = T; - type object; - }; + type object; +}; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_insert_v = polyfill::is_specialization_of::value; +template +SQLITE_ORM_INLINE_VAR constexpr bool is_insert_v = polyfill::is_specialization_of::value; - template - struct is_insert : polyfill::bool_constant> {}; +template +struct is_insert : polyfill::bool_constant> {}; - template - struct insert_explicit { - using type = T; - using columns_type = columns_t; +template +struct insert_explicit { + using type = T; + using columns_type = columns_t; - type obj; - columns_type columns; - }; + type obj; + columns_type columns; +}; - template - struct replace_t { - using type = T; +template +struct replace_t { + using type = T; - type object; - }; + type object; +}; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_replace_v = polyfill::is_specialization_of::value; +template +SQLITE_ORM_INLINE_VAR constexpr bool is_replace_v = polyfill::is_specialization_of::value; - template - struct is_replace : polyfill::bool_constant> {}; +template +struct is_replace : polyfill::bool_constant> {}; - template - struct insert_range_t { - using iterator_type = It; - using transformer_type = Projection; - using object_type = O; +template +struct insert_range_t { + using iterator_type = It; + using transformer_type = Projection; + using object_type = O; - std::pair range; - transformer_type transformer; - }; + std::pair range; + transformer_type transformer; +}; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_insert_range_v = - polyfill::is_specialization_of::value; +template +SQLITE_ORM_INLINE_VAR constexpr bool is_insert_range_v = polyfill::is_specialization_of::value; - template - struct is_insert_range : polyfill::bool_constant> {}; +template +struct is_insert_range : polyfill::bool_constant> {}; - template - struct replace_range_t { - using iterator_type = It; - using transformer_type = Projection; - using object_type = O; +template +struct replace_range_t { + using iterator_type = It; + using transformer_type = Projection; + using object_type = O; - std::pair range; - transformer_type transformer; - }; + std::pair range; + transformer_type transformer; +}; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_replace_range_v = - polyfill::is_specialization_of::value; +template +SQLITE_ORM_INLINE_VAR constexpr bool is_replace_range_v = polyfill::is_specialization_of::value; - template - struct is_replace_range : polyfill::bool_constant> {}; +template +struct is_replace_range : polyfill::bool_constant> {}; - template - struct insert_raw_t { - using args_tuple = std::tuple; +template +struct insert_raw_t { + using args_tuple = std::tuple; - args_tuple args; - }; + args_tuple args; +}; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_insert_raw_v = polyfill::is_specialization_of::value; +template +SQLITE_ORM_INLINE_VAR constexpr bool is_insert_raw_v = polyfill::is_specialization_of::value; - template - struct is_insert_raw : polyfill::bool_constant> {}; +template +struct is_insert_raw : polyfill::bool_constant> {}; - template - struct replace_raw_t { - using args_tuple = std::tuple; +template +struct replace_raw_t { + using args_tuple = std::tuple; - args_tuple args; - }; + args_tuple args; +}; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_replace_raw_v = polyfill::is_specialization_of::value; +template +SQLITE_ORM_INLINE_VAR constexpr bool is_replace_raw_v = polyfill::is_specialization_of::value; - template - struct is_replace_raw : polyfill::bool_constant> {}; +template +struct is_replace_raw : polyfill::bool_constant> {}; - struct default_values_t {}; +struct default_values_t {}; - template - using is_default_values = std::is_same; +template +using is_default_values = std::is_same; - enum class conflict_action { - abort, - fail, - ignore, - replace, - rollback, - }; +enum class conflict_action { + abort, + fail, + ignore, + replace, + rollback, +}; - struct insert_constraint { - conflict_action action = conflict_action::abort; +struct insert_constraint { + conflict_action action = conflict_action::abort; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - insert_constraint(conflict_action action) : action{action} {} + insert_constraint(conflict_action action) : action{action} {} #endif - }; +}; - template - using is_insert_constraint = std::is_same; - } +template +using is_insert_constraint = std::is_same; +} - inline internal::insert_constraint or_rollback() { - return {internal::conflict_action::rollback}; - } +inline internal::insert_constraint or_rollback() { + return {internal::conflict_action::rollback}; +} - inline internal::insert_constraint or_replace() { - return {internal::conflict_action::replace}; - } +inline internal::insert_constraint or_replace() { + return {internal::conflict_action::replace}; +} - inline internal::insert_constraint or_ignore() { - return {internal::conflict_action::ignore}; - } +inline internal::insert_constraint or_ignore() { + return {internal::conflict_action::ignore}; +} - inline internal::insert_constraint or_fail() { - return {internal::conflict_action::fail}; - } +inline internal::insert_constraint or_fail() { + return {internal::conflict_action::fail}; +} - inline internal::insert_constraint or_abort() { - return {internal::conflict_action::abort}; - } +inline internal::insert_constraint or_abort() { + return {internal::conflict_action::abort}; +} - /** +/** * Use this function to add `DEFAULT VALUES` modifier to raw `INSERT`. * * @example @@ -14673,11 +14356,11 @@ namespace sqlite_orm { * storage.insert(into(), default_values()); * ``` */ - inline internal::default_values_t default_values() { - return {}; - } +inline internal::default_values_t default_values() { + return {}; +} - /** +/** * Raw insert statement creation routine. Use this if `insert` with object does not fit you. This insert is designed to be able * to call any type of `INSERT` query with no limitations. * @example @@ -14714,48 +14397,48 @@ namespace sqlite_orm { * auto statement3 = storage.prepare(insert(or_abort(), into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); * ``` */ - template - internal::insert_raw_t insert(Args... args) { - using args_tuple = std::tuple; - using internal::count_tuple; - using internal::is_columns; - using internal::is_insert_constraint; - using internal::is_into; - using internal::is_select; - using internal::is_upsert_clause; - using internal::is_values; +template +internal::insert_raw_t insert(Args... args) { + using args_tuple = std::tuple; + using internal::count_tuple; + using internal::is_columns; + using internal::is_insert_constraint; + using internal::is_into; + using internal::is_select; + using internal::is_upsert_clause; + using internal::is_values; - constexpr int orArgsCount = count_tuple::value; - static_assert(orArgsCount < 2, "Raw insert must have only one OR... argument"); + constexpr int orArgsCount = count_tuple::value; + static_assert(orArgsCount < 2, "Raw insert must have only one OR... argument"); - constexpr int intoArgsCount = count_tuple::value; - static_assert(intoArgsCount != 0, "Raw insert must have into argument"); - static_assert(intoArgsCount < 2, "Raw insert must have only one into argument"); + constexpr int intoArgsCount = count_tuple::value; + static_assert(intoArgsCount != 0, "Raw insert must have into argument"); + static_assert(intoArgsCount < 2, "Raw insert must have only one into argument"); - constexpr int columnsArgsCount = count_tuple::value; - static_assert(columnsArgsCount < 2, "Raw insert must have only one columns(...) argument"); + constexpr int columnsArgsCount = count_tuple::value; + static_assert(columnsArgsCount < 2, "Raw insert must have only one columns(...) argument"); - constexpr int valuesArgsCount = count_tuple::value; - static_assert(valuesArgsCount < 2, "Raw insert must have only one values(...) argument"); + constexpr int valuesArgsCount = count_tuple::value; + static_assert(valuesArgsCount < 2, "Raw insert must have only one values(...) argument"); - constexpr int defaultValuesCount = count_tuple::value; - static_assert(defaultValuesCount < 2, "Raw insert must have only one default_values() argument"); + constexpr int defaultValuesCount = count_tuple::value; + static_assert(defaultValuesCount < 2, "Raw insert must have only one default_values() argument"); - constexpr int selectsArgsCount = count_tuple::value; - static_assert(selectsArgsCount < 2, "Raw insert must have only one select(...) argument"); + constexpr int selectsArgsCount = count_tuple::value; + static_assert(selectsArgsCount < 2, "Raw insert must have only one select(...) argument"); - constexpr int upsertClausesCount = count_tuple::value; - static_assert(upsertClausesCount <= 2, "Raw insert can contain 2 instances of upsert clause maximum"); + constexpr int upsertClausesCount = count_tuple::value; + static_assert(upsertClausesCount <= 2, "Raw insert can contain 2 instances of upsert clause maximum"); - constexpr int argsCount = int(std::tuple_size::value); - static_assert(argsCount == intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + - selectsArgsCount + orArgsCount + upsertClausesCount, - "Raw insert has invalid arguments"); + constexpr int argsCount = int(std::tuple_size::value); + static_assert(argsCount == intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + + selectsArgsCount + orArgsCount + upsertClausesCount, + "Raw insert has invalid arguments"); - return {{std::forward(args)...}}; - } + return {{std::forward(args)...}}; +} - /** +/** * Raw replace statement creation routine. Use this if `replace` with object does not fit you. This replace is designed to be able * to call any type of `REPLACE` query with no limitations. Actually this is the same query as raw insert except `OR...` option existance. * @example @@ -14786,39 +14469,39 @@ namespace sqlite_orm { * storage.execute(statement)); * ``` */ - template - internal::replace_raw_t replace(Args... args) { - using args_tuple = std::tuple; - using internal::count_tuple; - using internal::is_columns; - using internal::is_into; - using internal::is_values; +template +internal::replace_raw_t replace(Args... args) { + using args_tuple = std::tuple; + using internal::count_tuple; + using internal::is_columns; + using internal::is_into; + using internal::is_values; - constexpr int intoArgsCount = count_tuple::value; - static_assert(intoArgsCount != 0, "Raw replace must have into argument"); - static_assert(intoArgsCount < 2, "Raw replace must have only one into argument"); + constexpr int intoArgsCount = count_tuple::value; + static_assert(intoArgsCount != 0, "Raw replace must have into argument"); + static_assert(intoArgsCount < 2, "Raw replace must have only one into argument"); - constexpr int columnsArgsCount = count_tuple::value; - static_assert(columnsArgsCount < 2, "Raw replace must have only one columns(...) argument"); + constexpr int columnsArgsCount = count_tuple::value; + static_assert(columnsArgsCount < 2, "Raw replace must have only one columns(...) argument"); - constexpr int valuesArgsCount = count_tuple::value; - static_assert(valuesArgsCount < 2, "Raw replace must have only one values(...) argument"); + constexpr int valuesArgsCount = count_tuple::value; + static_assert(valuesArgsCount < 2, "Raw replace must have only one values(...) argument"); - constexpr int defaultValuesCount = count_tuple::value; - static_assert(defaultValuesCount < 2, "Raw replace must have only one default_values() argument"); + constexpr int defaultValuesCount = count_tuple::value; + static_assert(defaultValuesCount < 2, "Raw replace must have only one default_values() argument"); - constexpr int selectsArgsCount = count_tuple::value; - static_assert(selectsArgsCount < 2, "Raw replace must have only one select(...) argument"); + constexpr int selectsArgsCount = count_tuple::value; + static_assert(selectsArgsCount < 2, "Raw replace must have only one select(...) argument"); - constexpr int argsCount = int(std::tuple_size::value); - static_assert(argsCount == - intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + selectsArgsCount, - "Raw replace has invalid arguments"); + constexpr int argsCount = int(std::tuple_size::value); + static_assert(argsCount == + intoArgsCount + columnsArgsCount + valuesArgsCount + defaultValuesCount + selectsArgsCount, + "Raw replace has invalid arguments"); - return {{std::forward(args)...}}; - } + return {{std::forward(args)...}}; +} - /** +/** * Create a replace range statement. * The objects in the range are transformed using the specified projection, which defaults to identity projection. * @@ -14837,22 +14520,22 @@ namespace sqlite_orm { * storage.execute(statement); * ``` */ - template - auto replace_range(It from, It to, Projection project = {}) { - using O = std::decay_t(), *std::declval()))>; - return internal::replace_range_t{{std::move(from), std::move(to)}, std::move(project)}; - } +template +auto replace_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + return internal::replace_range_t{{std::move(from), std::move(to)}, std::move(project)}; +} - /* +/* * Create a replace range statement. * Overload of `replace_range(It, It, Projection)` with explicit object type template parameter. */ - template - internal::replace_range_t replace_range(It from, It to, Projection project = {}) { - return {{std::move(from), std::move(to)}, std::move(project)}; - } +template +internal::replace_range_t replace_range(It from, It to, Projection project = {}) { + return {{std::move(from), std::move(to)}, std::move(project)}; +} - /** +/** * Create an insert range statement. * The objects in the range are transformed using the specified projection, which defaults to identity projection. * @@ -14871,46 +14554,46 @@ namespace sqlite_orm { * storage.execute(statement); * ``` */ - template - auto insert_range(It from, It to, Projection project = {}) { - using O = std::decay_t(), *std::declval()))>; - return internal::insert_range_t{{std::move(from), std::move(to)}, std::move(project)}; - } +template +auto insert_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + return internal::insert_range_t{{std::move(from), std::move(to)}, std::move(project)}; +} - /* +/* * Create an insert range statement. * Overload of `insert_range(It, It, Projection)` with explicit object type template parameter. */ - template - internal::insert_range_t insert_range(It from, It to, Projection project = {}) { - return {{std::move(from), std::move(to)}, std::move(project)}; - } +template +internal::insert_range_t insert_range(It from, It to, Projection project = {}) { + return {{std::move(from), std::move(to)}, std::move(project)}; +} - /** +/** * Create a replace statement. * T is an object type mapped to a storage. * Usage: storage.replace(myUserInstance); * Parameter obj is accepted by value. If you want to accept it by ref * please use std::ref function: storage.replace(std::ref(myUserInstance)); */ - template - internal::replace_t replace(T obj) { - return {std::move(obj)}; - } +template +internal::replace_t replace(T obj) { + return {std::move(obj)}; +} - /** +/** * Create an insert statement. * T is an object type mapped to a storage. * Usage: storage.insert(myUserInstance); * Parameter obj is accepted by value. If you want to accept it by ref * please use std::ref function: storage.insert(std::ref(myUserInstance)); */ - template - internal::insert_t insert(T obj) { - return {std::move(obj)}; - } +template +internal::insert_t insert(T obj) { + return {std::move(obj)}; +} - /** +/** * Create an explicit insert statement. * T is an object type mapped to a storage. * Cols is columns types aparameter pack. Must contain member pointers @@ -14918,233 +14601,229 @@ namespace sqlite_orm { * Parameter obj is accepted by value. If you want to accept it by ref * please use std::ref function: storage.insert(std::ref(myUserInstance), columns(&User::id, &User::name)); */ - template - internal::insert_explicit insert(T obj, internal::columns_t cols) { - return {std::move(obj), std::move(cols)}; - } +template +internal::insert_explicit insert(T obj, internal::columns_t cols) { + return {std::move(obj), std::move(cols)}; +} - /** +/** * Create a remove statement * T is an object type mapped to a storage. * Usage: remove(5); */ - template - internal::remove_t remove(Ids... ids) { - return {{std::forward(ids)...}}; - } +template +internal::remove_t remove(Ids... ids) { + return {{std::forward(ids)...}}; +} #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** +/** * Create a remove statement * `table` is an explicitly specified table reference of a mapped object to be extracted. * Usage: remove(5); */ - template - auto remove(Ids... ids) { - return remove>(std::forward(ids)...); - } +template +auto remove(Ids... ids) { + return remove>(std::forward(ids)...); +} #endif - /** +/** * Create an update statement. * T is an object type mapped to a storage. * Usage: storage.update(myUserInstance); * Parameter obj is accepted by value. If you want to accept it by ref * please use std::ref function: storage.update(std::ref(myUserInstance)); */ - template - internal::update_t update(T obj) { - return {std::move(obj)}; - } +template +internal::update_t update(T obj) { + return {std::move(obj)}; +} - /** +/** * Create a get statement. * T is an object type mapped to a storage. * Usage: get(5); */ - template - internal::get_t get(Ids... ids) { - return {{std::forward(ids)...}}; - } +template +internal::get_t get(Ids... ids) { + return {{std::forward(ids)...}}; +} #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** +/** * Create a get statement. * `table` is an explicitly specified table reference of a mapped object to be extracted. * Usage: get(5); */ - template - auto get(Ids... ids) { - return get>(std::forward(ids)...); - } +template +auto get(Ids... ids) { + return get>(std::forward(ids)...); +} #endif - /** +/** * Create a get pointer statement. * T is an object type mapped to a storage. * Usage: get_pointer(5); */ - template - internal::get_pointer_t get_pointer(Ids... ids) { - return {{std::forward(ids)...}}; - } +template +internal::get_pointer_t get_pointer(Ids... ids) { + return {{std::forward(ids)...}}; +} #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** +/** * Create a get pointer statement. * `table` is an explicitly specified table reference of a mapped object to be extracted. * Usage: get_pointer(5); */ - template - auto get_pointer(Ids... ids) { - return get_pointer>(std::forward(ids)...); - } +template +auto get_pointer(Ids... ids) { + return get_pointer>(std::forward(ids)...); +} #endif #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - /** +/** * Create a get optional statement. * T is an object type mapped to a storage. * Usage: get_optional(5); */ - template - internal::get_optional_t get_optional(Ids... ids) { - return {{std::forward(ids)...}}; - } +template +internal::get_optional_t get_optional(Ids... ids) { + return {{std::forward(ids)...}}; +} #endif // SQLITE_ORM_OPTIONAL_SUPPORTED #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** +/** * Create a get optional statement. * `table` is an explicitly specified table reference of a mapped object to be extracted. * Usage: get_optional(5); */ - template - auto get_optional(Ids... ids) { - return get_optional>(std::forward(ids)...); - } +template +auto get_optional(Ids... ids) { + return get_optional>(std::forward(ids)...); +} #endif - /** +/** * Create a remove all statement. * T is an object type mapped to a storage. * Usage: storage.remove_all(...); */ - template - internal::remove_all_t remove_all(Args... args) { - using args_tuple = std::tuple; - internal::validate_conditions(); - return {{std::forward(args)...}}; - } +template +internal::remove_all_t remove_all(Args... args) { + using args_tuple = std::tuple; + internal::validate_conditions(); + return {{std::forward(args)...}}; +} #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** +/** * Create a remove all statement. * `table` is an explicitly specified table reference of a mapped object to be extracted. * Usage: storage.remove_all(...); */ - template - auto remove_all(Args... args) { - return remove_all>(std::forward(args)...); - } +template +auto remove_all(Args... args) { + return remove_all>(std::forward(args)...); +} #endif - /** +/** * Create a get all statement. * T is an explicitly specified object mapped to a storage or a table alias. * R is a container type. std::vector is default * Usage: storage.prepare(get_all(...)); */ - template>, class... Args> - internal::get_all_t get_all(Args... conditions) { - using conditions_tuple = std::tuple; - internal::validate_conditions(); - return {{std::forward(conditions)...}}; - } +template>, class... Args> +internal::get_all_t get_all(Args... conditions) { + using conditions_tuple = std::tuple; + internal::validate_conditions(); + return {{std::forward(conditions)...}}; +} #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** +/** * Create a get all statement. * `mapped` is an explicitly specified table reference or table alias to be extracted. * `R` is the container return type, which must have a `R::push_back(T&&)` method, and defaults to `std::vector` * Usage: storage.get_all(...); */ - template>, - class... Args> - auto get_all(Args&&... conditions) { - return get_all, R>(std::forward(conditions)...); - } +template>, + class... Args> +auto get_all(Args&&... conditions) { + return get_all, R>(std::forward(conditions)...); +} #endif - /** +/** * Create an update all statement. * Usage: storage.update_all(set(...), ...); */ - template - internal::update_all_t update_all(S set, Wargs... wh) { - static_assert(internal::is_set::value, "first argument in update_all can be either set or dynamic_set"); - using args_tuple = std::tuple; - internal::validate_conditions(); - return {std::move(set), {std::forward(wh)...}}; - } +template +internal::update_all_t update_all(S set, Wargs... wh) { + static_assert(internal::is_set::value, "first argument in update_all can be either set or dynamic_set"); + using args_tuple = std::tuple; + internal::validate_conditions(); + return {std::move(set), {std::forward(wh)...}}; +} - /** +/** * Create a get all pointer statement. * T is an object type mapped to a storage. * R is a container return type. std::vector> is default * Usage: storage.prepare(get_all_pointer(...)); */ - template>, class... Args> - internal::get_all_pointer_t get_all_pointer(Args... conditions) { - using conditions_tuple = std::tuple; - internal::validate_conditions(); - return {{std::forward(conditions)...}}; - } +template>, class... Args> +internal::get_all_pointer_t get_all_pointer(Args... conditions) { + using conditions_tuple = std::tuple; + internal::validate_conditions(); + return {{std::forward(conditions)...}}; +} #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** +/** * Create a get all pointer statement. * `table` is an explicitly specified table reference of a mapped object to be extracted. * R is a container return type. std::vector> is default * Usage: storage.prepare(get_all_pointer(...)); */ - template>, - class... Args> - auto get_all_pointer(Args... conditions) { - return get_all_pointer, R>(std::forward(conditions)...); - } +template>, class... Args> +auto get_all_pointer(Args... conditions) { + return get_all_pointer, R>(std::forward(conditions)...); +} #endif #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - /** +/** * Create a get all optional statement. * T is an object type mapped to a storage. * R is a container return type. std::vector> is default * Usage: storage.get_all_optional(...); */ - template>, class... Args> - internal::get_all_optional_t get_all_optional(Args... conditions) { - using conditions_tuple = std::tuple; - internal::validate_conditions(); - return {{std::forward(conditions)...}}; - } +template>, class... Args> +internal::get_all_optional_t get_all_optional(Args... conditions) { + using conditions_tuple = std::tuple; + internal::validate_conditions(); + return {{std::forward(conditions)...}}; +} #endif // SQLITE_ORM_OPTIONAL_SUPPORTED #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** +/** * Create a get all optional statement. * `table` is an explicitly specified table reference of a mapped object to be extracted. * R is a container return type. std::vector> is default * Usage: storage.get_all_optional(...); */ - template>, - class... Args> - auto get_all_optional(Args&&... conditions) { - return get_all_optional, R>(std::forward(conditions)...); - } +template>, class... Args> +auto get_all_optional(Args&&... conditions) { + return get_all_optional, R>(std::forward(conditions)...); +} #endif } @@ -15156,7 +14835,7 @@ namespace sqlite_orm { #include // std::move -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { template @@ -15187,7 +14866,7 @@ namespace sqlite_orm { // #include "../tags.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { template @@ -15219,7 +14898,9 @@ namespace sqlite_orm { // #include "ast/match.h" -namespace sqlite_orm { +#include + +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { template @@ -15239,7 +14920,7 @@ namespace sqlite_orm { } } -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { @@ -16191,6 +15872,7 @@ inline constexpr bool std::ranges::enable_borrowed_range // std::function, std::bind, std::bind_front #include // std::string #include // std::stringstream +#include // std::flush #include // std::move #include // std::system_error #include // std::vector @@ -16249,6 +15931,7 @@ namespace sqlite_orm { #include // std::shared_ptr #include // std::vector #include +#include // std::flush // #include "error_code.h" @@ -16267,7 +15950,7 @@ namespace sqlite_orm { #include #include #include -#include // std::exchange, std::tuple_size +#include // std::exchange, std::tuple_size, std::make_index_sequence // #include "functional/cxx_universal.h" // ::size_t @@ -16697,7 +16380,7 @@ namespace sqlite_orm { } } -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { struct storage_base; @@ -16935,7 +16618,7 @@ namespace sqlite_orm { // #include "connection_holder.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { @@ -17163,7 +16846,7 @@ namespace sqlite_orm { // #include "connection_holder.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { @@ -17248,7 +16931,7 @@ namespace sqlite_orm { // #include "row_extractor.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { /** @short Wrapper around a dynamically typed value object. */ @@ -17431,7 +17114,7 @@ namespace sqlite_orm { // #include "udf_proxy.h" #include -#include // assert +#include // assert macro #include // std::true_type, std::false_type #include // std::bad_alloc #include // std::allocator, std::allocator_traits, std::unique_ptr @@ -17662,7 +17345,9 @@ namespace sqlite_orm { // #include "serializing_util.h" -namespace sqlite_orm { +// #include "table_info.h" + +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { @@ -18729,7 +18414,7 @@ namespace sqlite_orm { // #include "functional/cxx_optional.h" // #include "functional/cxx_universal.h" - +// ::nullptr_t, ::size_t // #include "functional/cxx_functional_polyfill.h" // #include "functional/mpl.h" @@ -18748,7 +18433,7 @@ namespace sqlite_orm { // #include "ast/rank.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { struct rank_t {}; } @@ -18766,483 +18451,772 @@ namespace sqlite_orm { // #include "conditions.h" -// #include "schema/column.h" - // #include "indexed_column.h" // #include "function.h" -// #include "prepared_statement.h" +// #include "prepared_statement.h" + +// #include "rowid.h" + +// #include "pointer_value.h" + +// #include "type_printer.h" + +// #include "field_printer.h" + +// #include "literal.h" + +// #include "table_name_collector.h" + +// #include "column_names_getter.h" + +#include // std::is_base_of +#include // std::string +#include // std::vector +#include // std::reference_wrapper +#include +#include // std::move + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "error_code.h" + +// #include "mapped_type_proxy.h" + +// #include "alias_traits.h" + +// #include "select_constraints.h" + +// #include "storage_lookup.h" +// pick_table +// #include "serializer_context.h" + +// #include "util.h" + +namespace sqlite_orm { + + namespace internal { + + template + auto serialize(const T& t, const C& context); + + template + std::vector& collect_table_column_names(std::vector& collectedExpressions, + bool definedOrder, + const Ctx& context) { + if(definedOrder) { + auto& table = pick_table>(context.db_objects); + collectedExpressions.reserve(collectedExpressions.size() + table.template count_of()); + table.for_each_column([qualified = !context.skip_table_name, + &tableName = table.name, + &collectedExpressions](const column_identifier& column) { + if(is_alias::value) { + collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + "." + + quote_identifier(column.name)); + } else if(qualified) { + collectedExpressions.push_back(quote_identifier(tableName) + "." + + quote_identifier(column.name)); + } else { + collectedExpressions.push_back(quote_identifier(column.name)); + } + }); + } else { + collectedExpressions.reserve(collectedExpressions.size() + 1); + if(is_alias::value) { + collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); + } else if(!context.skip_table_name) { + const basic_table& table = pick_table>(context.db_objects); + collectedExpressions.push_back(quote_identifier(table.name) + ".*"); + } else { + collectedExpressions.emplace_back("*"); + } + } + + return collectedExpressions; + } + + /** @short Column expression collector. + */ + struct column_names_getter { + /** + * The default implementation simply serializes the passed argument. + */ + template + std::vector& operator()(const E& t, const Ctx& context) { + auto columnExpression = serialize(t, context); + if(columnExpression.empty()) { + throw std::system_error{orm_error_code::column_not_found}; + } + this->collectedExpressions.reserve(this->collectedExpressions.size() + 1); + this->collectedExpressions.push_back(std::move(columnExpression)); + return this->collectedExpressions; + } + + template + std::vector& operator()(const std::reference_wrapper& expression, const Ctx& context) { + return (*this)(expression.get(), context); + } + + template + std::vector& operator()(const asterisk_t& expression, const Ctx& context) { + return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); + } + + template + std::vector& operator()(const object_t& expression, const Ctx& context) { + return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); + } + + template + std::vector& operator()(const columns_t& cols, const Ctx& context) { + this->collectedExpressions.reserve(this->collectedExpressions.size() + cols.count); + iterate_tuple(cols.columns, [this, &context](auto& colExpr) { + (*this)(colExpr, context); + }); + // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order + if(tuple_has_template::columns_type, asterisk_t>::value && + this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + this->collectedExpressions.shrink_to_fit(); + } + return this->collectedExpressions; + } + + template + std::vector& operator()(const struct_t& cols, const Ctx& context) { + this->collectedExpressions.reserve(this->collectedExpressions.size() + cols.count); + iterate_tuple(cols.columns, [this, &context](auto& colExpr) { + (*this)(colExpr, context); + }); + // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order + if(tuple_has_template::columns_type, asterisk_t>::value && + this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + this->collectedExpressions.shrink_to_fit(); + } + return this->collectedExpressions; + } + + std::vector collectedExpressions; + }; + + template + std::vector get_column_names(const T& t, const Ctx& context) { + column_names_getter serializer; + return serializer(t, context); + } + } +} + +// #include "cte_column_names_collector.h" + +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#include +#include +#include // std::reference_wrapper +#include +#endif + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + +// #include "member_traits/member_traits.h" + +// #include "error_code.h" + +// #include "alias.h" + +// #include "select_constraints.h" + +// #include "serializer_context.h" + +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +namespace sqlite_orm { + namespace internal { + // collecting column names utilizes the statement serializer + template + auto serialize(const T& t, const C& context); + + inline void unquote_identifier(std::string& identifier) { + if(!identifier.empty()) { + constexpr char quoteChar = '"'; + constexpr char sqlEscaped[] = {quoteChar, quoteChar}; + identifier.erase(identifier.end() - 1); + identifier.erase(identifier.begin()); + for(size_t pos = 0; (pos = identifier.find(sqlEscaped, pos, 2)) != identifier.npos; ++pos) { + identifier.erase(pos, 1); + } + } + } + + inline void unquote_or_erase(std::string& name) { + constexpr char quoteChar = '"'; + if(name.front() == quoteChar) { + unquote_identifier(name); + } else { + // unaliased expression - see 3. below + name.clear(); + } + } + + template + struct cte_column_names_collector { + using expression_type = T; -// #include "rowid.h" + // Compound statements are never passed in by db_objects_for_expression() + static_assert(!is_compound_operator_v); -// #include "pointer_value.h" + template + std::vector operator()(const expression_type& t, const Ctx& context) const { + auto newContext = context; + newContext.skip_table_name = true; + std::string columnName = serialize(t, newContext); + if(columnName.empty()) { + throw std::system_error{orm_error_code::column_not_found}; + } + unquote_or_erase(columnName); + return {std::move(columnName)}; + } + }; -// #include "type_printer.h" + template + std::vector get_cte_column_names(const T& t, const Ctx& context) { + cte_column_names_collector collector; + return collector(t, context); + } -// #include "field_printer.h" + template + struct cte_column_names_collector> { + using expression_type = As; -// #include "literal.h" + template + std::vector operator()(const expression_type& /*expression*/, const Ctx& /*context*/) const { + return {alias_extractor>::extract()}; + } + }; -// #include "table_name_collector.h" + template + struct cte_column_names_collector> { + using expression_type = Wrapper; -// #include "column_names_getter.h" + template + std::vector operator()(const expression_type& expression, const Ctx& context) const { + return get_cte_column_names(expression.get(), context); + } + }; -#include // std::is_base_of -#include // std::string -#include // std::vector -#include // std::reference_wrapper -#include -#include // std::move + template + struct cte_column_names_collector> { + using expression_type = Asterisk; + using T = typename Asterisk::type; -// #include "tuple_helper/tuple_traits.h" + template + std::vector operator()(const expression_type&, const Ctx& context) const { + auto& table = pick_table(context.db_objects); -// #include "tuple_helper/tuple_iteration.h" + std::vector columnNames; + columnNames.reserve(size_t(table.template count_of())); -// #include "error_code.h" + table.for_each_column([&columnNames](const column_identifier& column) { + columnNames.push_back(column.name); + }); + return columnNames; + } + }; -// #include "mapped_type_proxy.h" + // No CTE for object expressions. + template + struct cte_column_names_collector> { + static_assert(polyfill::always_false_v, "Selecting an object in a subselect is not allowed."); + }; -// #include "alias_traits.h" + // No CTE for object expressions. + template + struct cte_column_names_collector> { + static_assert(polyfill::always_false_v, "Repacking columns in a subselect is not allowed."); + }; -// #include "select_constraints.h" + template + struct cte_column_names_collector> { + using expression_type = Columns; -// #include "storage_lookup.h" -// pick_table -// #include "serializer_context.h" + template + std::vector operator()(const expression_type& cols, const Ctx& context) const { + std::vector columnNames; + columnNames.reserve(size_t(cols.count)); + auto newContext = context; + newContext.skip_table_name = true; + iterate_tuple(cols.columns, [&columnNames, &newContext](auto& m) { + using value_type = polyfill::remove_cvref_t; -// #include "util.h" + if constexpr(polyfill::is_specialization_of_v) { + columnNames.push_back(alias_extractor>::extract()); + } else { + std::string columnName = serialize(m, newContext); + if(!columnName.empty()) { + columnNames.push_back(std::move(columnName)); + } else { + throw std::system_error{orm_error_code::column_not_found}; + } + unquote_or_erase(columnNames.back()); + } + }); + return columnNames; + } + }; -namespace sqlite_orm { + template = true> + std::vector + collect_cte_column_names(const E& sel, const ExplicitColRefs& explicitColRefs, const Ctx& context) { + // 1. determine column names from subselect + std::vector columnNames = get_cte_column_names(sel.col, context); - namespace internal { + // 2. override column names from cte expression + if(size_t n = std::tuple_size_v) { + if(n != columnNames.size()) { + throw std::system_error{orm_error_code::column_not_found}; + } - template - auto serialize(const T& t, const C& context); + size_t idx = 0; + iterate_tuple(explicitColRefs, [&idx, &columnNames, &context](auto& colRef) { + using ColRef = polyfill::remove_cvref_t; - template - std::vector& collect_table_column_names(std::vector& collectedExpressions, - bool definedOrder, - const Ctx& context) { - if(definedOrder) { - auto& table = pick_table>(context.db_objects); - collectedExpressions.reserve(collectedExpressions.size() + table.template count_of()); - table.for_each_column([qualified = !context.skip_table_name, - &tableName = table.name, - &collectedExpressions](const column_identifier& column) { - if(is_alias::value) { - collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + "." + - quote_identifier(column.name)); - } else if(qualified) { - collectedExpressions.push_back(quote_identifier(tableName) + "." + - quote_identifier(column.name)); + if constexpr(polyfill::is_specialization_of_v) { + columnNames[idx] = alias_extractor>::extract(); + } else if constexpr(std::is_member_pointer::value) { + using O = table_type_of_t; + if(auto* columnName = find_column_name(context.db_objects, colRef)) { + columnNames[idx] = *columnName; + } else { + // relaxed: allow any member pointer as column reference + columnNames[idx] = typeid(ColRef).name(); + } + } else if constexpr(polyfill::is_specialization_of_v) { + columnNames[idx] = colRef.name; + } else if constexpr(std::is_same_v) { + if(!colRef.empty()) { + columnNames[idx] = colRef; + } + } else if constexpr(std::is_same_v>) { + if(columnNames[idx].empty()) { + columnNames[idx] = std::to_string(idx + 1); + } } else { - collectedExpressions.push_back(quote_identifier(column.name)); + static_assert(polyfill::always_false_v, "Invalid explicit column reference specified"); } + ++idx; }); - } else { - collectedExpressions.reserve(collectedExpressions.size() + 1); - if(is_alias::value) { - collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); - } else if(!context.skip_table_name) { - const basic_table& table = pick_table>(context.db_objects); - collectedExpressions.push_back(quote_identifier(table.name) + ".*"); - } else { - collectedExpressions.emplace_back("*"); + } + + // 3. fill in blanks with numerical column identifiers + { + for(size_t i = 0, n = columnNames.size(); i < n; ++i) { + if(columnNames[i].empty()) { + columnNames[i] = std::to_string(i + 1); + } } } - return collectedExpressions; + return columnNames; } + } +} +#endif - /** @short Column expression collector. - */ - struct column_names_getter { - /** - * The default implementation simply serializes the passed argument. - */ - template - std::vector& operator()(const E& t, const Ctx& context) { - auto columnExpression = serialize(t, context); - if(columnExpression.empty()) { - throw std::system_error{orm_error_code::column_not_found}; - } - this->collectedExpressions.reserve(this->collectedExpressions.size() + 1); - this->collectedExpressions.push_back(std::move(columnExpression)); - return this->collectedExpressions; - } +// #include "order_by_serializer.h" - template - std::vector& operator()(const std::reference_wrapper& expression, const Ctx& context) { - return (*this)(expression.get(), context); - } +#include // std::string +#include // std::stringstream - template - std::vector& operator()(const asterisk_t& expression, const Ctx& context) { - return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); - } +namespace sqlite_orm { - template - std::vector& operator()(const object_t& expression, const Ctx& context) { - return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); - } + namespace internal { - template - std::vector& operator()(const columns_t& cols, const Ctx& context) { - this->collectedExpressions.reserve(this->collectedExpressions.size() + cols.count); - iterate_tuple(cols.columns, [this, &context](auto& colExpr) { - (*this)(colExpr, context); - }); - // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if(tuple_has_template::columns_type, asterisk_t>::value && - this->collectedExpressions.capacity() > this->collectedExpressions.size()) { - this->collectedExpressions.shrink_to_fit(); - } - return this->collectedExpressions; - } + template + struct order_by_serializer; + + template + std::string serialize_order_by(const T& t, const Ctx& context) { + order_by_serializer serializer; + return serializer(t, context); + } + + template + struct order_by_serializer, void> { + using statement_type = order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; - template - std::vector& operator()(const struct_t& cols, const Ctx& context) { - this->collectedExpressions.reserve(this->collectedExpressions.size() + cols.count); - iterate_tuple(cols.columns, [this, &context](auto& colExpr) { - (*this)(colExpr, context); - }); - // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if(tuple_has_template::columns_type, asterisk_t>::value && - this->collectedExpressions.capacity() > this->collectedExpressions.size()) { - this->collectedExpressions.shrink_to_fit(); + ss << serialize(orderBy.expression, newContext); + if(!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; } - return this->collectedExpressions; + switch(orderBy.asc_desc) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + return ss.str(); } + }; - std::vector collectedExpressions; + template + struct order_by_serializer, void> { + using statement_type = dynamic_order_by_t; + + template + std::string operator()(const statement_type& orderBy, const Ctx&) const { + std::stringstream ss; + ss << static_cast(orderBy) << " "; + int index = 0; + for(const dynamic_order_by_entry_t& entry: orderBy) { + if(index > 0) { + ss << ", "; + } + + ss << entry.name; + if(!entry._collate_argument.empty()) { + ss << " COLLATE " << entry._collate_argument; + } + switch(entry.asc_desc) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + ++index; + }; + return ss.str(); + } }; - template - std::vector get_column_names(const T& t, const Ctx& context) { - column_names_getter serializer; - return serializer(t, context); - } } } -// #include "cte_column_names_collector.h" +// #include "serializing_util.h" -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include -#include -#include // std::reference_wrapper -#include -#endif +// #include "serialize_result_type.h" -// #include "functional/cxx_universal.h" +// #include "statement_binder.h" -// #include "functional/cxx_type_traits_polyfill.h" +// #include "values.h" -// #include "type_traits.h" +// #include "table_type_of.h" -// #include "member_traits/member_traits.h" +// #include "util.h" // #include "error_code.h" -// #include "alias.h" +// #include "schema/triggers.h" -// #include "select_constraints.h" +#include +#include +#include +#include -// #include "serializer_context.h" +// #include "../functional/cxx_universal.h" -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -namespace sqlite_orm { - namespace internal { - // collecting column names utilizes the statement serializer - template - auto serialize(const T& t, const C& context); +// #include "../optional_container.h" - inline void unquote_identifier(std::string& identifier) { - if(!identifier.empty()) { - constexpr char quoteChar = '"'; - constexpr char sqlEscaped[] = {quoteChar, quoteChar}; - identifier.erase(identifier.end() - 1); - identifier.erase(identifier.begin()); - for(size_t pos = 0; (pos = identifier.find(sqlEscaped, pos, 2)) != identifier.npos; ++pos) { - identifier.erase(pos, 1); - } - } - } +// NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? +// (Could be implemented with a normal trigger that insert or update an internal table and then retreive +// the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) +// It could be an interesting feature to bring to sqlite_orm that other libraries don't have ? - inline void unquote_or_erase(std::string& name) { - constexpr char quoteChar = '"'; - if(name.front() == quoteChar) { - unquote_identifier(name); - } else { - // unaliased expression - see 3. below - name.clear(); - } - } +_EXPORT_SQLITE_ORM namespace sqlite_orm { + namespace internal { + enum class trigger_timing { trigger_before, trigger_after, trigger_instead_of }; + enum class trigger_type { trigger_delete, trigger_insert, trigger_update }; - template - struct cte_column_names_collector { - using expression_type = T; + /** + * This class is an intermediate SQLite trigger, to be used with + * `make_trigger` to create a full trigger. + * T is the base of the trigger (contains its type, timing and associated table) + * S is the list of trigger statements + */ + template + struct partial_trigger_t { + using statements_type = std::tuple; - // Compound statements are never passed in by db_objects_for_expression() - static_assert(!is_compound_operator_v); + /** + * Base of the trigger (contains its type, timing and associated table) + */ + T base; + /** + * Statements of the triggers (to be executed when the trigger fires) + */ + statements_type statements; - template - std::vector operator()(const expression_type& t, const Ctx& context) const { - auto newContext = context; - newContext.skip_table_name = true; - std::string columnName = serialize(t, newContext); - if(columnName.empty()) { - throw std::system_error{orm_error_code::column_not_found}; - } - unquote_or_erase(columnName); - return {std::move(columnName)}; + partial_trigger_t(T trigger_base, S... statements) : + base{std::move(trigger_base)}, statements{std::make_tuple(std::forward(statements)...)} {} + + partial_trigger_t& end() { + return *this; } }; - template - std::vector get_cte_column_names(const T& t, const Ctx& context) { - cte_column_names_collector collector; - return collector(t, context); - } + struct base_trigger { + /** + * Name of the trigger + */ + std::string name; + }; - template - struct cte_column_names_collector> { - using expression_type = As; + /** + * This class represent a SQLite trigger + * T is the base of the trigger (contains its type, timing and associated table) + * S is the list of trigger statments + */ + template + struct trigger_t : base_trigger { + using object_type = void; + using elements_type = typename partial_trigger_t::statements_type; - template - std::vector operator()(const expression_type& /*expression*/, const Ctx& /*context*/) const { - return {alias_extractor>::extract()}; - } - }; + /** + * Base of the trigger (contains its type, timing and associated table) + */ + T base; - template - struct cte_column_names_collector> { - using expression_type = Wrapper; + /** + * Statements of the triggers (to be executed when the trigger fires) + */ + elements_type elements; - template - std::vector operator()(const expression_type& expression, const Ctx& context) const { - return get_cte_column_names(expression.get(), context); - } +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + trigger_t(std::string name, T trigger_base, elements_type statements) : + base_trigger{std::move(name)}, base(std::move(trigger_base)), elements(std::move(statements)) {} +#endif }; - template - struct cte_column_names_collector> { - using expression_type = Asterisk; - using T = typename Asterisk::type; + /** + * Base of a trigger. Contains the trigger type/timming and the table type + * T is the table type + * W is `when` expression type + * Type is the trigger base type (type+timing) + */ + template + struct trigger_base_t { + using table_type = T; + using when_type = W; + using trigger_type_base = Type; - template - std::vector operator()(const expression_type&, const Ctx& context) const { - auto& table = pick_table(context.db_objects); + /** + * Contains the trigger type and timing + */ + trigger_type_base type_base; + /** + * Value used to determine if we execute the trigger on each row or on each statement + * (SQLite doesn't support the FOR EACH STATEMENT syntax yet: https://sqlite.org/lang_createtrigger.html#description + * so this value is more of a placeholder for a later update) + */ + bool do_for_each_row = false; + /** + * When expression (if any) + * If a WHEN expression is specified, the trigger will only execute + * if the expression evaluates to true when the trigger is fired + */ + optional_container container_when; - std::vector columnNames; - columnNames.reserve(size_t(table.template count_of())); + trigger_base_t(trigger_type_base type_base_) : type_base(std::move(type_base_)) {} - table.for_each_column([&columnNames](const column_identifier& column) { - columnNames.push_back(column.name); - }); - return columnNames; + trigger_base_t& for_each_row() { + this->do_for_each_row = true; + return *this; } - }; - // No CTE for object expressions. - template - struct cte_column_names_collector> { - static_assert(polyfill::always_false_v, "Selecting an object in a subselect is not allowed."); - }; + template + trigger_base_t when(WW expression) { + trigger_base_t res(this->type_base); + res.container_when.field = std::move(expression); + return res; + } - // No CTE for object expressions. - template - struct cte_column_names_collector> { - static_assert(polyfill::always_false_v, "Repacking columns in a subselect is not allowed."); + template + partial_trigger_t, S...> begin(S... statements) { + return {*this, std::forward(statements)...}; + } }; - template - struct cte_column_names_collector> { - using expression_type = Columns; + /** + * Contains the trigger type and timing + */ + struct trigger_type_base_t { + /** + * Value indicating if the trigger is run BEFORE, AFTER or INSTEAD OF + * the statement that fired it. + */ + trigger_timing timing; + /** + * The type of the statement that would cause the trigger to fire. + * Can be DELETE, INSERT, or UPDATE. + */ + trigger_type type; - template - std::vector operator()(const expression_type& cols, const Ctx& context) const { - std::vector columnNames; - columnNames.reserve(size_t(cols.count)); - auto newContext = context; - newContext.skip_table_name = true; - iterate_tuple(cols.columns, [&columnNames, &newContext](auto& m) { - using value_type = polyfill::remove_cvref_t; + trigger_type_base_t(trigger_timing timing, trigger_type type) : timing(timing), type(type) {} - if constexpr(polyfill::is_specialization_of_v) { - columnNames.push_back(alias_extractor>::extract()); - } else { - std::string columnName = serialize(m, newContext); - if(!columnName.empty()) { - columnNames.push_back(std::move(columnName)); - } else { - throw std::system_error{orm_error_code::column_not_found}; - } - unquote_or_erase(columnNames.back()); - } - }); - return columnNames; + template + trigger_base_t on() { + return {*this}; } }; - template = true> - std::vector - collect_cte_column_names(const E& sel, const ExplicitColRefs& explicitColRefs, const Ctx& context) { - // 1. determine column names from subselect - std::vector columnNames = get_cte_column_names(sel.col, context); + /** + * Special case for UPDATE OF (columns) + * Contains the trigger type and timing + */ + template + struct trigger_update_type_t : trigger_type_base_t { + using columns_type = std::tuple; - // 2. override column names from cte expression - if(size_t n = std::tuple_size_v) { - if(n != columnNames.size()) { - throw std::system_error{orm_error_code::column_not_found}; - } + /** + * Contains the columns the trigger is watching. Will only + * trigger if one of theses columns is updated. + */ + columns_type columns; - size_t idx = 0; - iterate_tuple(explicitColRefs, [&idx, &columnNames, &context](auto& colRef) { - using ColRef = polyfill::remove_cvref_t; + trigger_update_type_t(trigger_timing timing, trigger_type type, Cs... columns) : + trigger_type_base_t(timing, type), columns(std::make_tuple(std::forward(columns)...)) {} - if constexpr(polyfill::is_specialization_of_v) { - columnNames[idx] = alias_extractor>::extract(); - } else if constexpr(std::is_member_pointer::value) { - using O = table_type_of_t; - if(auto* columnName = find_column_name(context.db_objects, colRef)) { - columnNames[idx] = *columnName; - } else { - // relaxed: allow any member pointer as column reference - columnNames[idx] = typeid(ColRef).name(); - } - } else if constexpr(polyfill::is_specialization_of_v) { - columnNames[idx] = colRef.name; - } else if constexpr(std::is_same_v) { - if(!colRef.empty()) { - columnNames[idx] = colRef; - } - } else if constexpr(std::is_same_v>) { - if(columnNames[idx].empty()) { - columnNames[idx] = std::to_string(idx + 1); - } - } else { - static_assert(polyfill::always_false_v, "Invalid explicit column reference specified"); - } - ++idx; - }); + template + trigger_base_t> on() { + return {*this}; } + }; - // 3. fill in blanks with numerical column identifiers - { - for(size_t i = 0, n = columnNames.size(); i < n; ++i) { - if(columnNames[i].empty()) { - columnNames[i] = std::to_string(i + 1); - } - } + struct trigger_timing_t { + trigger_timing timing; + + trigger_type_base_t delete_() { + return {timing, trigger_type::trigger_delete}; } - return columnNames; - } - } -} -#endif + trigger_type_base_t insert() { + return {timing, trigger_type::trigger_insert}; + } -// #include "order_by_serializer.h" + trigger_type_base_t update() { + return {timing, trigger_type::trigger_update}; + } -#include // std::string -#include // std::stringstream + template + trigger_update_type_t update_of(Cs... columns) { + return {timing, trigger_type::trigger_update, std::forward(columns)...}; + } + }; -namespace sqlite_orm { + struct raise_t { + enum class type_t { + ignore, + rollback, + abort, + fail, + }; - namespace internal { + type_t type = type_t::ignore; + std::string message; - template - struct order_by_serializer; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + raise_t(type_t type, std::string message) : type{type}, message{std::move(message)} {} +#endif + }; - template - std::string serialize_order_by(const T& t, const Ctx& context) { - order_by_serializer serializer; - return serializer(t, context); - } + template + struct new_t { + using expression_type = T; - template - struct order_by_serializer, void> { - using statement_type = order_by_t; + expression_type expression; + }; - template - std::string operator()(const statement_type& orderBy, const Ctx& context) const { - std::stringstream ss; - auto newContext = context; - newContext.skip_table_name = false; + template + struct old_t { + using expression_type = T; - ss << serialize(orderBy.expression, newContext); - if(!orderBy._collate_argument.empty()) { - ss << " COLLATE " << orderBy._collate_argument; - } - switch(orderBy.asc_desc) { - case 1: - ss << " ASC"; - break; - case -1: - ss << " DESC"; - break; - } - return ss.str(); - } + expression_type expression; }; + } // NAMESPACE internal - template - struct order_by_serializer, void> { - using statement_type = dynamic_order_by_t; + /** + * NEW.expression function used within TRIGGER expressions + */ + template + internal::new_t new_(T expression) { + return {std::move(expression)}; + } - template - std::string operator()(const statement_type& orderBy, const Ctx&) const { - std::stringstream ss; - ss << static_cast(orderBy) << " "; - int index = 0; - for(const dynamic_order_by_entry_t& entry: orderBy) { - if(index > 0) { - ss << ", "; - } + /** + * OLD.expression function used within TRIGGER expressions + */ + template + internal::old_t old(T expression) { + return {std::move(expression)}; + } - ss << entry.name; - if(!entry._collate_argument.empty()) { - ss << " COLLATE " << entry._collate_argument; - } - switch(entry.asc_desc) { - case 1: - ss << " ASC"; - break; - case -1: - ss << " DESC"; - break; - } - ++index; - }; - return ss.str(); - } - }; + /** + * RAISE(IGNORE) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_ignore() { + return {internal::raise_t::type_t::ignore, {}}; + } + + /** + * RAISE(ROLLBACK, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_rollback(std::string message) { + return {internal::raise_t::type_t::rollback, std::move(message)}; + } + /** + * RAISE(ABORT, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_abort(std::string message) { + return {internal::raise_t::type_t::abort, std::move(message)}; } -} -// #include "serializing_util.h" + /** + * RAISE(FAIL, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_fail(std::string message) { + return {internal::raise_t::type_t::fail, std::move(message)}; + } -// #include "serialize_result_type.h" + template + internal::trigger_t make_trigger(std::string name, const internal::partial_trigger_t& part) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::move(part.base), std::move(part.statements)}); + } -// #include "statement_binder.h" + inline internal::trigger_timing_t before() { + return {internal::trigger_timing::trigger_before}; + } -// #include "values.h" + inline internal::trigger_timing_t after() { + return {internal::trigger_timing::trigger_after}; + } -// #include "schema/triggers.h" + inline internal::trigger_timing_t instead_of() { + return {internal::trigger_timing::trigger_instead_of}; + } +} -// #include "table_type_of.h" +// #include "schema/column.h" // #include "schema/index.h" // #include "schema/table.h" -// #include "util.h" - -// #include "error_code.h" - namespace sqlite_orm { namespace internal { @@ -21701,7 +21675,7 @@ namespace sqlite_orm { // aliased column expressions, explicit or implicitly numbered template = true> - static auto make_cte_column(std::string name, const ColRef& /*finalColRef*/) { + auto make_cte_column(std::string name, const ColRef& /*finalColRef*/) { using object_type = aliased_field, F>; return sqlite_orm::make_column<>(std::move(name), &object_type::field); @@ -21709,7 +21683,7 @@ namespace sqlite_orm { // F O::* template = true> - static auto make_cte_column(std::string name, const ColRef& finalColRef) { + auto make_cte_column(std::string name, const ColRef& finalColRef) { using object_type = table_type_of_t; using column_type = column_t; @@ -21968,7 +21942,7 @@ namespace sqlite_orm { // #include "serializing_util.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { namespace internal { /* @@ -22091,177 +22065,536 @@ namespace sqlite_orm { this->rename_table(db, backupTableName, table.name); } - template - void assert_mapped_type() const { - static_assert(tuple_has_type::value, - "type is not mapped to storage"); + template + void assert_mapped_type() const { + static_assert(tuple_has_type::value, + "type is not mapped to storage"); + } + + template + void assert_updatable_type() const { +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + using Table = storage_pick_table_t; + using elements_type = elements_type_t; + using col_index_sequence = filter_tuple_sequence_t; + using pk_index_sequence = filter_tuple_sequence_t; + using pkcol_index_sequence = col_index_sequence_with; + constexpr size_t dedicatedPrimaryKeyColumnsCount = + nested_tuple_size_for_t::value; + + constexpr size_t primaryKeyColumnsCount = + dedicatedPrimaryKeyColumnsCount + pkcol_index_sequence::size(); + constexpr ptrdiff_t nonPrimaryKeysColumnsCount = col_index_sequence::size() - primaryKeyColumnsCount; + static_assert(primaryKeyColumnsCount > 0, "A table without primary keys cannot be updated"); + static_assert( + nonPrimaryKeysColumnsCount > 0, + "A table with only primary keys cannot be updated. You need at least 1 non-primary key column"); +#endif + } + + template, + std::enable_if_t = true> + void assert_insertable_type() const {} + + template, + std::enable_if_t = true> + void assert_insertable_type() const { + using elements_type = elements_type_t
; + using pkcol_index_sequence = col_index_sequence_with; + static_assert( + count_filtered_tuple::value <= 1, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " + "'insert', or you can use 'insert' with explicit column listing."); + static_assert(count_filtered_tuple::template fn, + pkcol_index_sequence>::value == 0, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " + "of 'insert', or you can use 'insert' with explicit column listing."); + } + + template + auto& get_table() const { + return pick_table(this->db_objects); + } + + template + auto& get_table() { + return pick_table(this->db_objects); + } + + public: + template, class... Args> + mapped_view iterate(Args&&... args) { + this->assert_mapped_type(); + + auto con = this->get_connection(); + return {*this, std::move(con), std::forward(args)...}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto iterate(Args&&... args) { + return this->iterate(std::forward(args)...); + } +#endif + +#if defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) + template +#ifdef SQLITE_ORM_CONCEPTS_SUPPORTED + requires(is_select_v
; - using col_index_sequence = filter_tuple_sequence_t; - using pk_index_sequence = filter_tuple_sequence_t; - using pkcol_index_sequence = col_index_sequence_with; - constexpr size_t dedicatedPrimaryKeyColumnsCount = - nested_tuple_size_for_t::value; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * SELECT * routine. + * O is an object type to be extracted. Must be specified explicitly. + * R is a container type. std::vector> is default + * @return All objects of type O as std::optional stored in database at the moment. + * @example: storage.get_all_optional>>(); - SELECT * FROM users + * @example: storage.get_all_optional>>(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 + */ + template>, class... Args> + auto get_all_optional(Args&&... conditions) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_all_optional(std::forward(conditions)...)); + return this->execute(statement); + } +#endif - constexpr size_t primaryKeyColumnsCount = - dedicatedPrimaryKeyColumnsCount + pkcol_index_sequence::size(); - constexpr ptrdiff_t nonPrimaryKeysColumnsCount = col_index_sequence::size() - primaryKeyColumnsCount; - static_assert(primaryKeyColumnsCount > 0, "A table without primary keys cannot be updated"); - static_assert( - nonPrimaryKeysColumnsCount > 0, - "A table with only primary keys cannot be updated. You need at least 1 non-primary key column"); +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template>>, + class... Args> + auto get_all_optional(Args&&... conditions) { + return this->get_all_optional>(std::forward(conditions)...); + } #endif + + /** + * Select * by id routine. + * throws std::system_error{orm_error_code::not_found} if object not found with given + * id. throws std::system_error with orm_error_category in case of db error. O is an object type to be + * extracted. Must be specified explicitly. + * @return Object of type O where id is equal parameter passed or throws + * `std::system_error{orm_error_code::not_found}` if there is no object with such id. + */ + template + O get(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get(std::forward(ids)...)); + return this->execute(statement); } - template, - std::enable_if_t = true> - void assert_insertable_type() const {} +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto get(Ids... ids) { + return this->get>(std::forward(ids)...); + } +#endif - template, - std::enable_if_t = true> - void assert_insertable_type() const { - using elements_type = elements_type_t
; - using pkcol_index_sequence = col_index_sequence_with; - static_assert( - count_filtered_tuple::value <= 1, - "Attempting to execute 'insert' request into an noninsertable table was detected. " - "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " - "'insert', or you can use 'insert' with explicit column listing."); - static_assert(count_filtered_tuple::template fn, - pkcol_index_sequence>::value == 0, - "Attempting to execute 'insert' request into an noninsertable table was detected. " - "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " - "of 'insert', or you can use 'insert' with explicit column listing."); + /** + * The same as `get` function but doesn't throw an exception if noting found but returns std::unique_ptr + * with null value. throws std::system_error in case of db error. + */ + template + std::unique_ptr get_pointer(Ids... ids) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::get_pointer(std::forward(ids)...)); + return this->execute(statement); } - template - auto& get_table() const { - return pick_table(this->db_objects); +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto get_pointer(Ids... ids) { + return this->get_pointer>(std::forward(ids)...); } +#endif - template - auto& get_table() { - return pick_table(this->db_objects); + /** + * A previous version of get_pointer() that returns a shared_ptr + * instead of a unique_ptr. New code should prefer get_pointer() + * unless the data needs to be shared. + * + * @note + * Most scenarios don't need shared ownership of data, so we should prefer + * unique_ptr when possible. It's more efficient, doesn't require atomic + * ops for a reference count (which can cause major slowdowns on + * weakly-ordered platforms like ARM), and can be easily promoted to a + * shared_ptr, exactly like we're doing here. + * (Conversely, you _can't_ go from shared back to unique.) + */ + template + std::shared_ptr get_no_throw(Ids... ids) { + return std::shared_ptr(this->get_pointer(std::forward(ids)...)); } - public: - template, class... Args> - mapped_view iterate(Args&&... args) { +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * The same as `get` function but doesn't throw an exception if noting found but + * returns an empty std::optional. throws std::system_error in case of db error. + */ + template + std::optional get_optional(Ids... ids) { this->assert_mapped_type(); - - auto con = this->get_connection(); - return {*this, std::move(con), std::forward(args)...}; + auto statement = this->prepare(sqlite_orm::get_optional(std::forward(ids)...)); + return this->execute(statement); } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto iterate(Args&&... args) { - return this->iterate(std::forward(args)...); + template + auto get_optional(Ids... ids) { + return this->get_optional>(std::forward(ids)...); } #endif -#if defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) - template -#ifdef SQLITE_ORM_CONCEPTS_SUPPORTED - requires(is_select_v) + requires (is_select_v) + requires (is_select_v
(42) } -> same_as>; - { get_pointer
(42) } -> same_as>; - { get_optional
(42) } -> same_as>; - { get_all
() } -> same_as>>; - { get_all_pointer
() } -> same_as>>; - { get_all_optional
() } -> same_as>>; - { remove
(42) } -> same_as>; - { remove_all
() } -> same_as>; - { count
() } -> same_as>; -}; - -template> -concept storage_refers_to_table_callable = requires(S& storage) { - { storage.get_all() } -> same_as>; - { storage.count() } -> same_as; - { storage.iterate() } -> same_as>; -}; - -template> -concept storage_table_reference_callable = requires(S& storage) { - { storage.get
(42) } -> same_as; - { storage.get_pointer
(42) } -> same_as>; - { storage.get_optional
(42) } -> same_as>; - { storage.get_all
() } -> same_as>; - { storage.get_all_pointer
() } -> same_as>>; - { storage.get_all_optional
() } -> same_as>>; - { storage.remove
(42) } -> same_as; - { storage.remove_all
() } -> same_as; - { storage.count
() } -> same_as; - { storage.iterate
() } -> same_as>; -}; -#endif - -TEST_CASE("column pointers") { - struct User { - int id; - }; - struct DerivedUser : User {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - constexpr auto derived_user = c(); -#endif - - SECTION("table reference") { -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - STATIC_REQUIRE(orm_table_reference); - STATIC_REQUIRE_FALSE(is_table_alias_v); - STATIC_REQUIRE_FALSE(is_recordset_alias_v); - STATIC_REQUIRE_FALSE(orm_table_alias); - STATIC_REQUIRE_FALSE(orm_recordset_alias); - runTest>(derived_user); - runTest(internal::auto_decay_table_ref_t{}); -#endif - } - SECTION("column pointer expressions") { - runTest>(column(&User::id)); - runTest>(column(&DerivedUser::id)); -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - runTest>(derived_user->*&DerivedUser::id); - STATIC_REQUIRE(field_callable); - STATIC_REQUIRE(field_callable*&DerivedUser::id)>); - - using storage_type = decltype(make_storage( - "", - make_table("user", make_column("id", &User::id, primary_key())), - make_table("derived_user", make_column("id", &DerivedUser::id, primary_key())))); - - STATIC_REQUIRE(storage_field_callable); - STATIC_REQUIRE(storage_field_callable*&DerivedUser::id)>); -#endif - } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - SECTION("table reference expressions") { - runTest>(make_table("derived_user")); - runTest>(from()); - runTest>(asterisk()); - runTest>(object()); - runTest>(count()); - runTest>(get(42)); - runTest>>(get_all()); - runTest>>( - left_join(using_(derived_user->*&DerivedUser::id))); - runTest>>( - join(using_(derived_user->*&DerivedUser::id))); - runTest>>( - left_outer_join(using_(derived_user->*&DerivedUser::id))); - runTest>>( - inner_join(using_(derived_user->*&DerivedUser::id))); - - STATIC_REQUIRE(refers_to_recordset_callable); - STATIC_REQUIRE(refers_to_table_callable); - STATIC_REQUIRE(table_reference_callable); - STATIC_REQUIRE(refers_to_recordset_callable); - STATIC_REQUIRE(refers_to_table_callable); - STATIC_REQUIRE(table_reference_callable); - STATIC_REQUIRE(refers_to_recordset_callable); - STATIC_REQUIRE(refers_to_table_callable); - - using storage_type = decltype(make_storage( - "", - make_sqlite_schema_table(), - make_table("derived_user", make_column("id", &DerivedUser::id, primary_key())))); - - STATIC_REQUIRE(storage_refers_to_table_callable); - STATIC_REQUIRE(storage_table_reference_callable); - STATIC_REQUIRE(storage_refers_to_table_callable); - STATIC_REQUIRE(storage_table_reference_callable); - STATIC_REQUIRE(storage_refers_to_table_callable); - } -#endif -} +#include +#include // std::is_same +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include // same_as +#endif +#include + +using namespace sqlite_orm; +using internal::column_pointer; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +using internal::count_asterisk_t; +using internal::decay_table_ref_t; +using internal::get_all_optional_t; +using internal::get_all_pointer_t; +using internal::get_all_t; +using internal::get_optional_t; +using internal::get_pointer_t; +using internal::get_t; +using internal::is_recordset_alias_v; +using internal::is_table_alias_v; +using internal::mapped_view; +using internal::remove_all_t; +using internal::remove_t; +using internal::table_reference; +using internal::using_t; +using std::same_as; +#endif + +template +void do_assert() { + STATIC_REQUIRE(std::is_same::value); +} + +template +void runTest(const T& /*test*/) { + do_assert(); +} + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +template +concept field_callable = requires(C field) { + count(field); + avg(field); + max(field); + min(field); + sum(field); + total(field); + group_concat(field); +}; + +template +concept storage_field_callable = requires(S& storage, C field) { + { storage.count(field) } -> same_as; + { storage.avg(field) } -> same_as; + { storage.max(field) }; + { storage.min(field) }; + { storage.sum(field) }; + { storage.total(field) } -> same_as; + { storage.group_concat(field) } -> same_as; + { storage.group_concat(field, "") } -> same_as; + { storage.group_concat(field, std::string{}) } -> same_as; + { storage.group_concat(field, 42) } -> same_as; +}; + +template +concept refers_to_recordset_callable = requires { + { count() } -> same_as>>; +}; + +template> +concept refers_to_table_callable = requires { + { get_all() } -> same_as, std::vector>>; + { count() } -> same_as>>; +}; + +template> +concept table_reference_callable = requires { + { get
(42) } -> same_as>; + { get_pointer
(42) } -> same_as>; + { get_optional
(42) } -> same_as>; + { get_all
() } -> same_as>>; + { get_all_pointer
() } -> same_as>>; + { get_all_optional
() } -> same_as>>; + { remove
(42) } -> same_as>; + { remove_all
() } -> same_as>; + { count
() } -> same_as>; +}; + +template> +concept storage_refers_to_table_callable = requires(S& storage) { + { storage.get_all() } -> same_as>; + { storage.count() } -> same_as; + { storage.iterate() } -> same_as>; +}; + +template> +concept storage_table_reference_callable = requires(S& storage) { + { storage.get
(42) } -> same_as; + { storage.get_pointer
(42) } -> same_as>; + { storage.get_optional
(42) } -> same_as>; + { storage.get_all
() } -> same_as>; + { storage.get_all_pointer
() } -> same_as>>; + { storage.get_all_optional
() } -> same_as>>; + { storage.remove
(42) } -> same_as; + { storage.remove_all
() } -> same_as; + { storage.count
() } -> same_as; + { storage.iterate
() } -> same_as>; +}; +#endif + +TEST_CASE("column pointers") { + struct User { + int id; + }; + struct DerivedUser : User {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr auto derived_user = c(); +#endif + + SECTION("table reference") { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + STATIC_REQUIRE(orm_table_reference); + STATIC_REQUIRE_FALSE(is_table_alias_v); + STATIC_REQUIRE_FALSE(is_recordset_alias_v); + STATIC_REQUIRE_FALSE(orm_table_alias); + STATIC_REQUIRE_FALSE(orm_recordset_alias); + runTest>(derived_user); + runTest(internal::auto_decay_table_ref_t{}); +#endif + } + SECTION("column pointer expressions") { + runTest>(column(&User::id)); + runTest>(column(&DerivedUser::id)); +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + runTest>(derived_user->*&DerivedUser::id); + STATIC_REQUIRE(field_callable); + STATIC_REQUIRE(field_callable*&DerivedUser::id)>); + + using storage_type = decltype(make_storage( + "", + make_table("user", make_column("id", &User::id, primary_key())), + make_table("derived_user", make_column("id", &DerivedUser::id, primary_key())))); + + STATIC_REQUIRE(storage_field_callable); + STATIC_REQUIRE(storage_field_callable*&DerivedUser::id)>); +#endif + } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("table reference expressions") { + runTest>(make_table("derived_user")); + runTest>(from()); + runTest>(asterisk()); + runTest>(object()); + runTest>(count()); + runTest>(get(42)); + runTest>>(get_all()); + runTest>>( + left_join(using_(derived_user->*&DerivedUser::id))); + runTest>>( + join(using_(derived_user->*&DerivedUser::id))); + runTest>>( + left_outer_join(using_(derived_user->*&DerivedUser::id))); + runTest>>( + inner_join(using_(derived_user->*&DerivedUser::id))); + + STATIC_REQUIRE(refers_to_recordset_callable); + STATIC_REQUIRE(refers_to_table_callable); + STATIC_REQUIRE(table_reference_callable); + STATIC_REQUIRE(refers_to_recordset_callable); + STATIC_REQUIRE(refers_to_table_callable); + STATIC_REQUIRE(table_reference_callable); + STATIC_REQUIRE(refers_to_recordset_callable); + STATIC_REQUIRE(refers_to_table_callable); + + using storage_type = decltype(make_storage( + "", + make_sqlite_schema_table(), + make_table("derived_user", make_column("id", &DerivedUser::id, primary_key())))); + + STATIC_REQUIRE(storage_refers_to_table_callable); + STATIC_REQUIRE(storage_table_reference_callable); + STATIC_REQUIRE(storage_refers_to_table_callable); + STATIC_REQUIRE(storage_table_reference_callable); + STATIC_REQUIRE(storage_refers_to_table_callable); + } +#endif +} diff --git a/tests/static_tests/column_result_t.cpp b/tests/static_tests/column_result_t.cpp index cbfef385f..5f154bc73 100644 --- a/tests/static_tests/column_result_t.cpp +++ b/tests/static_tests/column_result_t.cpp @@ -137,7 +137,7 @@ TEST_CASE("column_result_of_t") { columns(struct_(asterisk()), struct_(asterisk()))); runTest(union_all(select(1), select(2))); runTest(union_all(select(1ll), select(2))); -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) using cte_1 = decltype(1_ctealias); // 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 diff --git a/tests/static_tests/cte.cpp b/tests/static_tests/cte.cpp index b55231f6c..b069ce4fb 100644 --- a/tests/static_tests/cte.cpp +++ b/tests/static_tests/cte.cpp @@ -1,5 +1,5 @@ #include -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include // std::is_same, std::is_constructible #include // std::ignore #include // std::string diff --git a/tests/static_tests/function_static_tests.cpp b/tests/static_tests/function_static_tests.cpp index ee7c387c7..cbb827d43 100644 --- a/tests/static_tests/function_static_tests.cpp +++ b/tests/static_tests/function_static_tests.cpp @@ -33,9 +33,9 @@ TEST_CASE("function static") { std::string operator()(const arg_values& args) const { std::string res; res.reserve(args.size()); - for(auto value: args) { + for (auto value: args) { auto stringValue = value.get(); - if(!stringValue.empty()) { + if (!stringValue.empty()) { res += stringValue.front(); } } diff --git a/tests/static_tests/iterator_t.cpp b/tests/static_tests/iterator_t.cpp index 5902c925e..554692cf5 100644 --- a/tests/static_tests/iterator_t.cpp +++ b/tests/static_tests/iterator_t.cpp @@ -77,7 +77,7 @@ concept storage_iterate_mapped = requires(S& storage_type) { }; #endif -#if(defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED)) && \ +#if (defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED)) && \ defined(SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED) template concept can_iterate_result_set = requires(Iter it) { @@ -131,7 +131,9 @@ TEST_CASE("can view and iterate mapped") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); // copyable (partially, as it is a rather extensive concept) - { STATIC_REQUIRE(std::is_copy_constructible::value); } + { + STATIC_REQUIRE(std::is_copy_constructible::value); + } } // equality_comparable (sentinel) { @@ -143,7 +145,9 @@ TEST_CASE("can view and iterate mapped") { STATIC_REQUIRE(std::is_same::difference_type, ptrdiff_t>::value); } // semiregular (actually sentinel_for, but the other concepts were verified above) - { STATIC_REQUIRE(std::is_default_constructible::value); } + { + STATIC_REQUIRE(std::is_default_constructible::value); + } STATIC_REQUIRE(std::is_same::pointer, Object*>::value); // note: should actually be only present for contiguous iterators STATIC_REQUIRE(std::is_same()), Object*>::value); @@ -154,7 +158,7 @@ TEST_CASE("can view and iterate mapped") { #endif } -#if(defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED)) && \ +#if (defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED)) && \ defined(SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED) TEST_CASE("can view and iterate result set") { struct Object {}; @@ -184,7 +188,7 @@ TEST_CASE("can view and iterate result set") { storage_iterate_result_set())), table_reference>); #endif -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #ifdef SQLITE_ORM_WITH_CPP20_ALIASES constexpr orm_cte_moniker auto x = "x"_cte; constexpr orm_column_alias auto i = "i"_col; diff --git a/tests/static_tests/node_tuple.cpp b/tests/static_tests/node_tuple.cpp index 4a9a07bc6..7109cbf72 100644 --- a/tests/static_tests/node_tuple.cpp +++ b/tests/static_tests/node_tuple.cpp @@ -271,7 +271,7 @@ TEST_CASE("Node tuple") { using Expected = tuple; static_assert(is_same::value, "count(*)"); } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) SECTION("count(*) cte") { auto node = count(); using Node = decltype(node); @@ -997,7 +997,7 @@ TEST_CASE("Node tuple") { using ExpectedTuple = tuple; STATIC_REQUIRE(std::is_same::value); } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) SECTION("with ordinary") { using cte_1 = decltype(1_ctealias); auto expression = with(1_ctealias().as(select(1)), select(column(1_colalias))); diff --git a/tests/static_tests/operators_adl.cpp b/tests/static_tests/operators_adl.cpp index 111358ab2..300d50ce0 100644 --- a/tests/static_tests/operators_adl.cpp +++ b/tests/static_tests/operators_adl.cpp @@ -125,7 +125,7 @@ void runTests(E expression) { } TEST_CASE("inline namespace literals expressions") { -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) constexpr auto col1 = 1_colalias; constexpr auto cte1 = 1_ctealias; #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -149,13 +149,13 @@ TEST_CASE("ADL and pointer-to-member expressions") { }; constexpr auto user_table = c(); constexpr auto u_alias = "u"_alias.for_(); -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) constexpr auto cte = "1"_cte; #endif user_table->*&User::id; u_alias->*&User::id; -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) cte->*&User::id; #endif } diff --git a/tests/static_tests/static_tests_storage_traits.h b/tests/static_tests/static_tests_storage_traits.h index 8c3ad7092..453cf2955 100644 --- a/tests/static_tests/static_tests_storage_traits.h +++ b/tests/static_tests/static_tests_storage_traits.h @@ -1,124 +1,124 @@ -#pragma once +#pragma once /* * All symbols used to be in dev/storage_traits.h up to sqlite_orm 1.7. * Because they were not used, they were moved to unit tests, * and for simplicitly the namespace sqlite_orm::internal::storage_traits was kept. - */ - -#include // std::integral_constant - -#include - -namespace sqlite_orm { - - namespace internal { - - template - using source_type_t = typename T::source_type; - - namespace storage_traits { - - template - struct storage_columns_count_impl - : std::integral_constant>::value> {}; - - template<> - struct storage_columns_count_impl : std::integral_constant {}; - + */ + +#include // std::integral_constant + +#include + +namespace sqlite_orm { + + namespace internal { + + template + using source_type_t = typename T::source_type; + + namespace storage_traits { + + template + struct storage_columns_count_impl + : std::integral_constant>::value> {}; + + template<> + struct storage_columns_count_impl : std::integral_constant {}; + /** * S - storage_t * Lookup - mapped or not mapped data type - */ - template - struct storage_columns_count - : storage_columns_count_impl> {}; - + */ + template + struct storage_columns_count + : storage_columns_count_impl> {}; + /** * Table A `table_t<>` * Lookup is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has Lookup = User) - */ - template - struct table_foreign_keys_count - : count_filtered_tuple, - check_if_is_type::template fn, - filter_tuple_sequence_t, is_foreign_key>, - target_type_t> {}; - + */ + template + struct table_foreign_keys_count + : count_filtered_tuple, + check_if_is_type::template fn, + filter_tuple_sequence_t, is_foreign_key>, + target_type_t> {}; + /** * DBOs - db_objects_tuple * Lookup - type mapped to storage - */ - template - struct storage_foreign_keys_count_impl : std::integral_constant {}; - -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED - template - struct storage_foreign_keys_count_impl, Lookup> { - static constexpr int value = (table_foreign_keys_count::value + ...); - }; -#else - template - struct storage_foreign_keys_count_impl, Lookup> { - static constexpr int value = table_foreign_keys_count::value + - storage_foreign_keys_count_impl, Lookup>::value; - }; -#endif - + */ + template + struct storage_foreign_keys_count_impl : std::integral_constant {}; + +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + struct storage_foreign_keys_count_impl, Lookup> { + static constexpr int value = (table_foreign_keys_count::value + ...); + }; +#else + template + struct storage_foreign_keys_count_impl, Lookup> { + static constexpr int value = table_foreign_keys_count::value + + storage_foreign_keys_count_impl, Lookup>::value; + }; +#endif + /** * S - storage class * Lookup - type mapped to storage * This class tells how many types mapped to DBOs have foreign keys to Lookup - */ - template - struct storage_foreign_keys_count : storage_foreign_keys_count_impl {}; - - template - using table_foreign_keys_t = - filter_tuple_t, - check_if_is_type::template fn, - target_type_t, - filter_tuple_sequence_t, is_foreign_key>>; - + */ + template + struct storage_foreign_keys_count : storage_foreign_keys_count_impl {}; + + template + using table_foreign_keys_t = + filter_tuple_t, + check_if_is_type::template fn, + target_type_t, + filter_tuple_sequence_t, is_foreign_key>>; + /* * Implementation note: must be a struct instead of an alias template because the foreign keys tuple * must be hoisted into a named alias, otherwise type replacement may fail for legacy compilers * if an alias template has a dependent expression in it [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. - */ - template - struct table_fk_references { - using foreign_keys = table_foreign_keys_t; - - using type = transform_tuple_t; - }; - + */ + template + struct table_fk_references { + using foreign_keys = table_foreign_keys_t; + + using type = transform_tuple_t; + }; + /** * DBOs - db_objects_tuple * Lookup - type mapped to storage - */ - template - struct storage_fk_references_impl; - - template - struct storage_foreign_keys_impl; - - template - struct storage_fk_references_impl, Lookup> - : conc_tuple::type...> {}; - - template - struct storage_foreign_keys_impl, Lookup> - : conc_tuple...> {}; - + */ + template + struct storage_fk_references_impl; + + template + struct storage_foreign_keys_impl; + + template + struct storage_fk_references_impl, Lookup> + : conc_tuple::type...> {}; + + template + struct storage_foreign_keys_impl, Lookup> + : conc_tuple...> {}; + /** * S - storage class * Lookup - type mapped to storage * type holds `std::tuple` with types that has references to Lookup as foreign keys - */ - template - struct storage_fk_references : storage_fk_references_impl {}; - - template - struct storage_foreign_keys : storage_foreign_keys_impl {}; - } - } -} + */ + template + struct storage_fk_references : storage_fk_references_impl {}; + + template + struct storage_foreign_keys : storage_foreign_keys_impl {}; + } + } +} diff --git a/tests/storage_non_crud_tests.cpp b/tests/storage_non_crud_tests.cpp index a4cfe0b47..15ad250f9 100644 --- a/tests/storage_non_crud_tests.cpp +++ b/tests/storage_non_crud_tests.cpp @@ -177,7 +177,7 @@ TEST_CASE("InsertRange") { SECTION("pointers") { std::vector> objects; objects.reserve(100); - for(auto i = 0; i < 100; ++i) { + for (auto i = 0; i < 100; ++i) { objects.push_back(std::make_unique(0, "Skillet")); } storage.insert_range(objects.begin(), objects.end(), &std::unique_ptr::operator*); @@ -224,7 +224,7 @@ TEST_CASE("Select") { REQUIRE(rc == SQLITE_OK); rc = sqlite3_step(stmt); - if(rc != SQLITE_DONE) { + if (rc != SQLITE_DONE) { throw std::runtime_error(sqlite3_errmsg(db)); } sqlite3_finalize(stmt); @@ -240,7 +240,7 @@ TEST_CASE("Select") { sqlite3_bind_text(stmt, 3, "hey", -1, nullptr); sqlite3_bind_int(stmt, 4, 5); rc = sqlite3_step(stmt); - if(rc != SQLITE_DONE) { + if (rc != SQLITE_DONE) { throw std::runtime_error(sqlite3_errmsg(db)); } sqlite3_finalize(stmt); @@ -256,7 +256,7 @@ TEST_CASE("Select") { sqlite3_bind_text(stmt, 3, "brothers", -1, nullptr); sqlite3_bind_int(stmt, 4, 15); rc = sqlite3_step(stmt); - if(rc != SQLITE_DONE) { + if (rc != SQLITE_DONE) { throw std::runtime_error(sqlite3_errmsg(db)); } sqlite3_finalize(stmt); @@ -274,7 +274,7 @@ TEST_CASE("Select") { sqlite3_bind_int64(stmt, 1, firstId); rc = sqlite3_step(stmt); - if(rc != SQLITE_ROW) { + if (rc != SQLITE_ROW) { throw std::runtime_error(sqlite3_errmsg(db)); } REQUIRE(sqlite3_column_int(stmt, 0) == firstId); diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index b241d78e7..437eced79 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -334,7 +334,7 @@ TEST_CASE("insert") { storage.remove_all(); storage.remove_all(); - for(auto i = 0; i < 100; ++i) { + for (auto i = 0; i < 100; ++i) { storage.insert(Object{ 0, "Skillet", @@ -520,7 +520,7 @@ TEST_CASE("last insert rowid") { } } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) TEST_CASE("With clause") { using Catch::Matchers::Equals; diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index 65d48945c..6c0aab30f 100644 --- a/tests/sync_schema_tests.cpp +++ b/tests/sync_schema_tests.cpp @@ -58,7 +58,7 @@ TEST_CASE("Sync schema") { usersToInsert.push_back({-1, "Brad", std::make_unique(65), nullptr}); usersToInsert.push_back({-1, "Paul", std::make_unique(65), nullptr}); - for(auto& user: usersToInsert) { + for (auto& user: usersToInsert) { auto insertedId = storage.insert(user); user.id = insertedId; } @@ -97,7 +97,7 @@ TEST_CASE("Sync schema") { REQUIRE(usersFromDb.size() == usersToInsert.size()); - for(size_t i = 0; i < usersFromDb.size(); ++i) { + for (size_t i = 0; i < usersFromDb.size(); ++i) { auto& userFromDb = usersFromDb[i]; auto& oldUser = usersToInsert[i]; REQUIRE(userFromDb == oldUser); @@ -170,7 +170,7 @@ TEST_CASE("issue521") { pocosToInsert.push_back({-1, "Michael", 10, 10.10f}); pocosToInsert.push_back({-1, "Joyce", 20, 20.20f}); - for(auto& poco: pocosToInsert) { + for (auto& poco: pocosToInsert) { auto insertedId = storage.insert(poco); poco.id = insertedId; } @@ -180,7 +180,7 @@ TEST_CASE("issue521") { using namespace sqlite_orm; auto pocosFromDb = storage.get_all(order_by(&MockDatabasePoco::id)); - for(size_t i = 0; i < pocosFromDb.size(); ++i) { + for (size_t i = 0; i < pocosFromDb.size(); ++i) { auto& pocoFromDb = pocosFromDb[i]; auto& oldPoco = pocosToInsert[i]; @@ -204,7 +204,7 @@ TEST_CASE("issue521") { REQUIRE(static_cast(storage.count()) == pocosToInsert.size()); auto pocosFromDb = storage.get_all(order_by(&MockDatabasePoco::id)); - for(size_t i = 0; i < pocosFromDb.size(); ++i) { + for (size_t i = 0; i < pocosFromDb.size(); ++i) { auto& pocoFromDb = pocosFromDb[i]; auto& oldPoco = pocosToInsert[i]; REQUIRE(pocoFromDb.id == oldPoco.id); @@ -229,7 +229,7 @@ TEST_CASE("issue521") { REQUIRE(static_cast(storage.count()) == pocosToInsert.size()); auto pocosFromDb = storage.get_all(order_by(&MockDatabasePoco::id)); - for(size_t i = 0; i < pocosFromDb.size(); ++i) { + for (size_t i = 0; i < pocosFromDb.size(); ++i) { auto& pocoFromDb = pocosFromDb[i]; auto& oldPoco = pocosToInsert[i]; REQUIRE(pocoFromDb.id == oldPoco.id); @@ -256,7 +256,7 @@ TEST_CASE("issue521") { REQUIRE(static_cast(storage.count()) == pocosToInsert.size()); auto pocosFromDb = storage.get_all(order_by(&MockDatabasePoco::id)); - for(size_t i = 0; i < pocosFromDb.size(); ++i) { + for (size_t i = 0; i < pocosFromDb.size(); ++i) { auto& pocoFromDb = pocosFromDb[i]; auto& oldPoco = pocosToInsert[i]; @@ -268,10 +268,10 @@ TEST_CASE("issue521") { } bool compareUniquePointers(const std::unique_ptr& lhs, const std::unique_ptr& rhs) { - if(!lhs && !rhs) { + if (!lhs && !rhs) { return true; } else { - if(lhs && rhs) { + if (lhs && rhs) { return *lhs == *rhs; } else { return false; diff --git a/tests/table_name_collector.cpp b/tests/table_name_collector.cpp index a89896962..d4a838c17 100644 --- a/tests/table_name_collector.cpp +++ b/tests/table_name_collector.cpp @@ -49,7 +49,7 @@ TEST_CASE("table name collector") { REQUIRE(collector.table_names == expected); } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#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)))); diff --git a/tests/tests.cpp b/tests/tests.cpp index dd5bdc63d..8fd149691 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -110,7 +110,7 @@ TEST_CASE("Custom collate") { struct OtotoCollation { int operator()(int leftLength, const void* lhs, int rightLength, const void* rhs) const { - if(leftLength == rightLength) { + if (leftLength == rightLength) { return ::strncmp((const char*)lhs, (const char*)rhs, leftLength); } else { return 1; @@ -150,9 +150,9 @@ TEST_CASE("Custom collate") { storage.remove_all(); storage.insert(Item{0, "Mercury"}); storage.insert(Item{0, "Mars"}); - if(useLegacyScript) { + if (useLegacyScript) { storage.create_collation("ototo", [](int leftLength, const void* lhs, int rightLength, const void* rhs) { - if(leftLength == rightLength) { + if (leftLength == rightLength) { return ::strncmp((const char*)lhs, (const char*)rhs, leftLength); } else { return 1; @@ -181,7 +181,7 @@ TEST_CASE("Custom collate") { where(is_equal(&Item::name, "Mercury").collate()), order_by(&Item::name).collate()); - if(useLegacyScript) { + if (useLegacyScript) { storage.create_collation("ototo", {}); } else { storage.delete_collation(); diff --git a/tests/tests3.cpp b/tests/tests3.cpp index 234a0eff9..c60bc07b0 100644 --- a/tests/tests3.cpp +++ b/tests/tests3.cpp @@ -31,7 +31,7 @@ TEST_CASE("Multi order by") { auto expectedIds = {1, 2, 3, 5, 6, 4}; REQUIRE(expectedIds.size() == singers.size()); auto it = expectedIds.begin(); - for(size_t i = 0; i < singers.size(); ++i) { + for (size_t i = 0; i < singers.size(); ++i) { REQUIRE(*it == singers[i].id); ++it; } @@ -42,7 +42,7 @@ TEST_CASE("Multi order by") { auto singers = storage.get_all(order_by(&Singer::id).asc()); auto singers2 = storage.get_all(multi_order_by(order_by(&Singer::id).asc())); REQUIRE(singers.size() == singers2.size()); - for(size_t i = 0; i < singers.size(); ++i) { + for (size_t i = 0; i < singers.size(); ++i) { REQUIRE(singers[i].id == singers2[i].id); } } @@ -115,7 +115,7 @@ TEST_CASE("Wide string") { L"АаБбВвГгДдЕеЁёЖжЗзИиЙйКкЛлМмНнОоППРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮюЯя", // russian L"AaBbCcÇçDdEeFFGgĞğHhIıİiJjKkLlMmNnOoÖöPpRrSsŞşTtUuÜüVvYyZz", // turkish }; - for(auto& expectedString: expectedStrings) { + for (auto& expectedString: expectedStrings) { auto id = storage.insert(Alphabet{0, expectedString}); REQUIRE(storage.get(id).letters == expectedString); } @@ -285,8 +285,8 @@ TEST_CASE("Blob") { auto generateData = [](size_t size) -> byte* { auto data = (byte*)::malloc(size * sizeof(byte)); - for(int i = 0; i < static_cast(size); ++i) { - if((i + 1) % 10 == 0) { + for (int i = 0; i < static_cast(size); ++i) { + if ((i + 1) % 10 == 0) { data[i] = 0; } else { data[i] = static_cast((rand() % 100) + 1); diff --git a/tests/tests5.cpp b/tests/tests5.cpp index a4ce1bf28..4fed2b70f 100644 --- a/tests/tests5.cpp +++ b/tests/tests5.cpp @@ -267,7 +267,7 @@ TEST_CASE("issue822") { public: A() = default; A(const uint8_t& address, const uint8_t& type, const uint8_t& idx, std::shared_ptr value) : - address(address), type(type), idx(idx), value(std::move(value)){}; + address(address), type(type), idx(idx), value(std::move(value)) {}; const uint8_t& getAddress() const { return this->address; @@ -317,7 +317,7 @@ TEST_CASE("issue822") { storage.sync_schema(); storage.replace(A(1, 1, 0, std::make_shared(55.5))); auto records = storage.get_all(where(c(&A::getAddress) == 1 and c(&A::getType) == 1 and c(&A::getIndex) == 0)); - if(records.size() != 0) { + if (records.size() != 0) { A a = records[0]; a.setValue(std::make_shared(10)); storage.update(a); diff --git a/tests/transaction_tests.cpp b/tests/transaction_tests.cpp index b40be81a2..5fee37f60 100644 --- a/tests/transaction_tests.cpp +++ b/tests/transaction_tests.cpp @@ -337,7 +337,7 @@ TEST_CASE("Transaction guard") { storage2.get_all(); alignas(alignof(internal::transaction_guard_t)) char buffer[sizeof(internal::transaction_guard_t)]; - auto guard = new(&buffer) internal::transaction_guard_t{storage.transaction_guard()}; + auto guard = new (&buffer) internal::transaction_guard_t{storage.transaction_guard()}; storage.insert({}); guard->commit_on_destroy = true; REQUIRE_THROWS_WITH(guard->~transaction_guard_t(), ContainsSubstring("database is locked")); diff --git a/tests/unique_cases/issue663.cpp b/tests/unique_cases/issue663.cpp index b4ab6135d..1a2343cdd 100644 --- a/tests/unique_cases/issue663.cpp +++ b/tests/unique_cases/issue663.cpp @@ -28,7 +28,7 @@ namespace { storage.insert_range(usersInput.begin(), usersInput.end()); const auto users = storage.template get_all(); REQUIRE(users.size() == usersInput.size()); - for(size_t i = 0; i < users.size(); ++i) { + for (size_t i = 0; i < users.size(); ++i) { REQUIRE(-1 != users[i].id); usersInput[i].id = users[i].id; } @@ -176,7 +176,7 @@ TEST_CASE("Issue 663 - fail test") { try { storage.insert_range(inputUsers.begin(), inputUsers.end()); REQUIRE(false); - } catch(const std::system_error& e) { + } catch (const std::system_error& e) { REQUIRE(e.code() == make_error_code(orm_error_code::cannot_use_default_value)); REQUIRE(storage.count() == 0); } diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index 6aec5ef6b..45548f607 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -132,9 +132,9 @@ struct FirstFunction { ++staticCallsCount; std::string res; res.reserve(args.size()); - for(auto value: args) { + for (auto value: args) { auto stringValue = value.get(); - if(!stringValue.empty()) { + if (!stringValue.empty()) { res += stringValue.front(); } } @@ -168,8 +168,8 @@ struct MultiSum { } void step(const arg_values& args) { - for(auto it = args.begin(); it != args.end(); ++it) { - if(!it->empty() && (it->is_integer() || it->is_float())) { + for (auto it = args.begin(); it != args.end(); ++it) { + if (!it->empty() && (it->is_integer() || it->is_float())) { this->sum += it->get(); } } From b5d80a8c1c199a564ab740650fe2844eb2d13b84 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 28 Jan 2025 14:49:23 +0200 Subject: [PATCH 030/170] Module-Export of locking mode --- dev/locking_mode.h | 6 +++++- include/sqlite_orm/sqlite_orm.h | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dev/locking_mode.h b/dev/locking_mode.h index 55ba6ac1f..9ba45e333 100644 --- a/dev/locking_mode.h +++ b/dev/locking_mode.h @@ -1,19 +1,23 @@ #pragma once +#ifndef _IMPORT_STD_MODULE #include // std::array #include // std::string #include // std::pair #include // std::ranges::transform #include // std::toupper +#endif #include "serialize_result_type.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { enum class locking_mode : signed char { NORMAL = 0, EXCLUSIVE = 1, }; +} +namespace sqlite_orm { namespace internal { inline const serialize_result_type& locking_mode_to_string(locking_mode value) { #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 7e337e27c..a2a4292e9 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -12951,20 +12951,24 @@ namespace sqlite_orm { // #include "locking_mode.h" +#ifndef _IMPORT_STD_MODULE #include // std::array #include // std::string #include // std::pair #include // std::ranges::transform #include // std::toupper +#endif // #include "serialize_result_type.h" -namespace sqlite_orm { +_EXPORT_SQLITE_ORM namespace sqlite_orm { enum class locking_mode : signed char { NORMAL = 0, EXCLUSIVE = 1, }; +} +namespace sqlite_orm { namespace internal { inline const serialize_result_type& locking_mode_to_string(locking_mode value) { #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED From 84f2289d6b94721c2225fb8d9d7f255f4daf1d09 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 28 Jan 2025 19:51:57 +0200 Subject: [PATCH 031/170] Corrected exporting symbols for `match` --- dev/ast/match.h | 6 +++--- include/sqlite_orm/sqlite_orm.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/ast/match.h b/dev/ast/match.h index 767224258..48eadd677 100644 --- a/dev/ast/match.h +++ b/dev/ast/match.h @@ -4,7 +4,7 @@ #include #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +namespace sqlite_orm { namespace internal { template @@ -13,11 +13,11 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { using argument_type = X; argument_type argument; - - match_t(argument_type argument) : argument(std::move(argument)) {} }; } +} +_EXPORT_SQLITE_ORM namespace sqlite_orm { template internal::match_t match(X argument) { return {std::move(argument)}; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a2a4292e9..07aa7cfca 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -15306,7 +15306,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #include #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +namespace sqlite_orm { namespace internal { template @@ -15315,11 +15315,11 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { using argument_type = X; argument_type argument; - - match_t(argument_type argument) : argument(std::move(argument)) {} }; } +} +_EXPORT_SQLITE_ORM namespace sqlite_orm { template internal::match_t match(X argument) { return {std::move(argument)}; From 868fefbb55062dcf64c08232d7c1e1de7669daec Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 28 Jan 2025 20:27:54 +0200 Subject: [PATCH 032/170] Use range-based for with init-statement --- dev/cte_column_names_collector.h | 9 ++++++ dev/functional/cxx_core_features.h | 1 + dev/implementations/table_definitions.h | 13 ++++++--- dev/pragma.h | 3 +- dev/serializing_util.h | 6 ++++ dev/statement_serializer.h | 7 +++++ include/sqlite_orm/sqlite_orm.h | 39 +++++++++++++++++++++---- 7 files changed, 66 insertions(+), 12 deletions(-) diff --git a/dev/cte_column_names_collector.h b/dev/cte_column_names_collector.h index 7d11b928a..68ce56764 100644 --- a/dev/cte_column_names_collector.h +++ b/dev/cte_column_names_collector.h @@ -197,11 +197,20 @@ namespace sqlite_orm { // 3. fill in blanks with numerical column identifiers { +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + for (size_t n = 1; std::string & name: columnNames) { + if (name.empty()) { + name = std::to_string(n); + } + ++n; + } +#else for (size_t i = 0, n = columnNames.size(); i < n; ++i) { if (columnNames[i].empty()) { columnNames[i] = std::to_string(i + 1); } } +#endif } return columnNames; diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index 86da3dcb3..be69e699f 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -96,4 +96,5 @@ #if __cplusplus >= 202002L #define SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED +#define SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED #endif diff --git a/dev/implementations/table_definitions.h b/dev/implementations/table_definitions.h index 722ffc894..97a320015 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -45,19 +45,24 @@ namespace sqlite_orm { column.template is()); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); +#if defined(SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) + for (int n = 1; const std::string& columnName: compositeKeyColumnNames) { + if (auto it = std::ranges::find(res, columnName, &table_xinfo::name); it != res.end()) { + it->pk = n; + } + ++n; + } +#else for (size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { const std::string& columnName = compositeKeyColumnNames[i]; -#if __cpp_lib_ranges >= 201911L - auto it = std::ranges::find(res, columnName, &table_xinfo::name); -#else auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_xinfo& ti) { return ti.name == columnName; }); -#endif if (it != res.end()) { it->pk = static_cast(i + 1); } } +#endif return res; } diff --git a/dev/pragma.h b/dev/pragma.h index 34d95b3f2..00e4b17e1 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -34,8 +34,7 @@ namespace sqlite_orm { res.reserve(argc); const auto rowExtractor = column_text_extractor(); for (int i = 0; i < argc; ++i) { - auto rowString = rowExtractor.extract(argv[i]); - res.push_back(std::move(rowString)); + res.push_back(rowExtractor.extract(argv[i])); } return 0; } diff --git a/dev/serializing_util.h b/dev/serializing_util.h index d44d93a48..6ac985d7b 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -239,9 +239,15 @@ namespace sqlite_orm { const auto& strings = std::get<1>(tpl); static constexpr std::array sep = {", ", ""}; +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + for (bool first = true; auto& s: strings) { + ss << sep[std::exchange(first, false)] << s; + } +#else for (size_t i = 0, first = true; i < strings.size(); ++i) { ss << sep[std::exchange(first, false)] << strings[i]; } +#endif return ss; } diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 7d4f73d9f..9a967660d 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1691,12 +1691,19 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::table_has_no_primary_key_column}; } +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + static constexpr std::array sep = {" AND ", ""}; + for (bool first = true; const std::string& pkName: primaryKeyColumnNames) { + ss << sep[std::exchange(first, false)] << streaming_identifier(pkName) << " = ?"; + } +#else for (size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { if (i > 0) { ss << " AND "; } ss << streaming_identifier(primaryKeyColumnNames[i]) << " = ?"; } +#endif return ss.str(); } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 07aa7cfca..bbea65e42 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -131,6 +131,7 @@ using std::nullptr_t; #if __cplusplus >= 202002L #define SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED +#define SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED #endif // #include "cxx_compiler_quirks.h" @@ -16607,9 +16608,15 @@ namespace sqlite_orm { const auto& strings = std::get<1>(tpl); static constexpr std::array sep = {", ", ""}; +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + for (bool first = true; auto& s: strings) { + ss << sep[std::exchange(first, false)] << s; + } +#else for (size_t i = 0, first = true; i < strings.size(); ++i) { ss << sep[std::exchange(first, false)] << strings[i]; } +#endif return ss; } @@ -16817,8 +16824,7 @@ namespace sqlite_orm { res.reserve(argc); const auto rowExtractor = column_text_extractor(); for (int i = 0; i < argc; ++i) { - auto rowString = rowExtractor.extract(argv[i]); - res.push_back(std::move(rowString)); + res.push_back(rowExtractor.extract(argv[i])); } return 0; } @@ -19352,11 +19358,20 @@ namespace sqlite_orm { // 3. fill in blanks with numerical column identifiers { +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + for (size_t n = 1; std::string & name: columnNames) { + if (name.empty()) { + name = std::to_string(n); + } + ++n; + } +#else for (size_t i = 0, n = columnNames.size(); i < n; ++i) { if (columnNames[i].empty()) { columnNames[i] = std::to_string(i + 1); } } +#endif } return columnNames; @@ -21395,12 +21410,19 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::table_has_no_primary_key_column}; } +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + static constexpr std::array sep = {" AND ", ""}; + for (bool first = true; const std::string& pkName: primaryKeyColumnNames) { + ss << sep[std::exchange(first, false)] << streaming_identifier(pkName) << " = ?"; + } +#else for (size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { if (i > 0) { ss << " AND "; } ss << streaming_identifier(primaryKeyColumnNames[i]) << " = ?"; } +#endif return ss.str(); } @@ -24300,19 +24322,24 @@ namespace sqlite_orm { column.template is()); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); +#if defined(SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) + for (int n = 1; const std::string& columnName: compositeKeyColumnNames) { + if (auto it = std::ranges::find(res, columnName, &table_xinfo::name); it != res.end()) { + it->pk = n; + } + ++n; + } +#else for (size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { const std::string& columnName = compositeKeyColumnNames[i]; -#if __cpp_lib_ranges >= 201911L - auto it = std::ranges::find(res, columnName, &table_xinfo::name); -#else auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_xinfo& ti) { return ti.name == columnName; }); -#endif if (it != res.end()) { it->pk = static_cast(i + 1); } } +#endif return res; } From 348e1b461fbe0b4b9f25af0e30d81366de039028 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 28 Jan 2025 20:30:01 +0200 Subject: [PATCH 033/170] Used `std::remove()` Using named modules the namespace scope will be much stricter, so adhere to it. --- examples/composite_key.cpp | 1 - tests/backup_tests.cpp | 4 ++-- tests/pragma_tests.cpp | 14 +++++++------- tests/tests.cpp | 4 ++-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/examples/composite_key.cpp b/examples/composite_key.cpp index 345734d46..30a0c9a65 100644 --- a/examples/composite_key.cpp +++ b/examples/composite_key.cpp @@ -2,7 +2,6 @@ * This example shows you how to create a storage with a tablw with a composite primary key * and another table woth foreign key to first table's primary compisite key. */ -#include #include #include diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index b935e3e82..dde1f252c 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -1,6 +1,6 @@ #include #include -#include // remove +#include // std::remove using namespace sqlite_orm; @@ -47,7 +47,7 @@ TEST_CASE("backup") { static int filenameSuffix = 0; // to make an unique filename for every test const std::string backupFilename = "backup" + std::to_string(filenameSuffix++) + ".sqlite"; SECTION("to") { - ::remove(backupFilename.c_str()); + std::remove(backupFilename.c_str()); auto storage2 = makeStorage(backupFilename); auto storage = makeStorage(""); storage.sync_schema(); diff --git a/tests/pragma_tests.cpp b/tests/pragma_tests.cpp index 062f1a10d..39819f2c2 100644 --- a/tests/pragma_tests.cpp +++ b/tests/pragma_tests.cpp @@ -1,5 +1,5 @@ #include -#include // ::remove +#include // std::remove #include using namespace sqlite_orm; @@ -12,7 +12,7 @@ TEST_CASE("module_list") { TEST_CASE("recursive_triggers") { auto filename = "recursive_triggers.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); storage.open_forever(); storage.pragma.recursive_triggers(true); @@ -37,7 +37,7 @@ TEST_CASE("locking_mode") { TEST_CASE("journal_mode") { auto filename = "journal_mode.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); auto storageCopy = storage; decltype(storage)* stor = nullptr; @@ -118,7 +118,7 @@ TEST_CASE("User version") { TEST_CASE("Auto vacuum") { auto filename = "autovacuum.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); @@ -160,7 +160,7 @@ TEST_CASE("Integrity Check") { }; auto filename = "integrity.sqlite"; - ::remove(filename); + std::remove(filename); std::string tablename = "users"; auto storage = make_storage(filename, @@ -185,7 +185,7 @@ TEST_CASE("Quick Check") { }; auto filename = "quick_check.sqlite"; - ::remove(filename); + std::remove(filename); std::string tablename = "users"; auto storage = make_storage(filename, @@ -201,7 +201,7 @@ TEST_CASE("Quick Check") { TEST_CASE("application_id") { auto filename = "application_id.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); storage.pragma.application_id(3); diff --git a/tests/tests.cpp b/tests/tests.cpp index 8fd149691..4b354657f 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -3,7 +3,7 @@ #include // std::vector #include // std::string -#include // remove +#include // std::remove using namespace sqlite_orm; @@ -141,7 +141,7 @@ TEST_CASE("Custom collate") { } auto filename = "custom_collate.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage( filename, make_table("items", make_column("id", &Item::id, primary_key()), make_column("name", &Item::name))); From 3c1f7d24242622ecd67cde4d9fa6d8d3a16a787b Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 28 Jan 2025 20:40:43 +0200 Subject: [PATCH 034/170] Used internal test macro for C++ range algortithms --- dev/implementations/storage_definitions.h | 2 +- dev/journal_mode.h | 2 +- dev/locking_mode.h | 2 +- dev/storage.h | 4 ++-- dev/storage_base.h | 4 ++-- include/sqlite_orm/sqlite_orm.h | 14 +++++++------- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index a1d2da31c..b01ca23f7 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -128,7 +128,7 @@ namespace sqlite_orm { columnNames.reserve(table.template count_of()); table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { auto& columnName = column.name; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); #else auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), diff --git a/dev/journal_mode.h b/dev/journal_mode.h index 3ca88c743..8f2f09030 100644 --- a/dev/journal_mode.h +++ b/dev/journal_mode.h @@ -63,7 +63,7 @@ namespace sqlite_orm { journal_mode::WAL, journal_mode::OFF, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); diff --git a/dev/locking_mode.h b/dev/locking_mode.h index 9ba45e333..945781e4a 100644 --- a/dev/locking_mode.h +++ b/dev/locking_mode.h @@ -37,7 +37,7 @@ namespace sqlite_orm { locking_mode::EXCLUSIVE, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); diff --git a/dev/storage.h b/dev/storage.h index d4ead3329..55890665f 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1438,7 +1438,7 @@ namespace sqlite_orm { static_if::value>( [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::for_each(expression.range.first, expression.range.second, std::ref(processObject), @@ -1483,7 +1483,7 @@ namespace sqlite_orm { static_if::value>( [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::for_each(expression.range.first, expression.range.second, std::ref(processObject), diff --git a/dev/storage_base.h b/dev/storage_base.h index a97060dcf..bb4f3f2f4 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -852,7 +852,7 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, std::list& functions) const { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto it = std::ranges::find(functions, name, &udf_proxy::name); #else auto it = std::find_if(functions.begin(), functions.end(), [&name](auto& udfProxy) { @@ -987,7 +987,7 @@ namespace sqlite_orm { const std::string& columnName = storageColumnInfo.name; // search for a column in db with the same name -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto dbColumnInfoIt = std::ranges::find(dbTableInfo, columnName, &table_xinfo::name); #else auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index bbea65e42..59bfd7811 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -12880,7 +12880,7 @@ namespace sqlite_orm { journal_mode::WAL, journal_mode::OFF, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); @@ -12989,7 +12989,7 @@ namespace sqlite_orm { locking_mode::EXCLUSIVE, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); @@ -18634,7 +18634,7 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, std::list& functions) const { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto it = std::ranges::find(functions, name, &udf_proxy::name); #else auto it = std::find_if(functions.begin(), functions.end(), [&name](auto& udfProxy) { @@ -18769,7 +18769,7 @@ namespace sqlite_orm { const std::string& columnName = storageColumnInfo.name; // search for a column in db with the same name -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto dbColumnInfoIt = std::ranges::find(dbTableInfo, columnName, &table_xinfo::name); #else auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { @@ -23913,7 +23913,7 @@ namespace sqlite_orm { static_if::value>( [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::for_each(expression.range.first, expression.range.second, std::ref(processObject), @@ -23958,7 +23958,7 @@ namespace sqlite_orm { static_if::value>( [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::for_each(expression.range.first, expression.range.second, std::ref(processObject), @@ -24578,7 +24578,7 @@ namespace sqlite_orm { columnNames.reserve(table.template count_of()); table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { auto& columnName = column.name; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); #else auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), From 05c3868a2ec4dea02f6f2a1c27d927fc57344043 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 28 Jan 2025 21:19:34 +0200 Subject: [PATCH 035/170] Fixed a wrong word in the example file for `std::any` --- examples/any.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/any.cpp b/examples/any.cpp index 4355766fd..8f7815c39 100644 --- a/examples/any.cpp +++ b/examples/any.cpp @@ -3,7 +3,7 @@ * It is a bit more complex than the other examples, because it * uses custom row_extractor and statement_binder. * Please note that this implementation is not the one and only - * option of implementation of std:any bindong to sqlite_orm. + * option of implementation of `std:any` binding to sqlite_orm. * It is just an example of how to use std::any as a mapped type. * Implementation is based on 5 types SQLite supports: * NULL, INTEGER, REAL, TEXT, BLOB. From 27388f1c5d6beaead303b0337c0257aadea38593 Mon Sep 17 00:00:00 2001 From: Max Gorbatenko Date: Tue, 28 Jan 2025 14:37:59 -0600 Subject: [PATCH 036/170] Bumped up Catch2 version. --- dependencies/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt index 282243910..bf8f4d6e5 100644 --- a/dependencies/CMakeLists.txt +++ b/dependencies/CMakeLists.txt @@ -6,13 +6,13 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.2.1 + GIT_TAG v3.8.0 ) else() FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.2.1 + GIT_TAG v3.8.0 # prefer find_package() over building from source FIND_PACKAGE_ARGS 3 CONFIG ) From 7c49e34919408d82c325a749c312b7fa225a2083 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 28 Jan 2025 23:02:00 +0200 Subject: [PATCH 037/170] Use range-based for with init-statement --- dev/cte_column_names_collector.h | 9 ++++++ dev/functional/cxx_core_features.h | 1 + dev/implementations/table_definitions.h | 13 ++++++--- dev/pragma.h | 3 +- dev/serializing_util.h | 6 ++++ dev/statement_serializer.h | 7 +++++ include/sqlite_orm/sqlite_orm.h | 39 +++++++++++++++++++++---- 7 files changed, 66 insertions(+), 12 deletions(-) diff --git a/dev/cte_column_names_collector.h b/dev/cte_column_names_collector.h index a122a395a..dbc1c5514 100644 --- a/dev/cte_column_names_collector.h +++ b/dev/cte_column_names_collector.h @@ -195,11 +195,20 @@ namespace sqlite_orm { // 3. fill in blanks with numerical column identifiers { +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + for (size_t n = 1; std::string & name: columnNames) { + if (name.empty()) { + name = std::to_string(n); + } + ++n; + } +#else for (size_t i = 0, n = columnNames.size(); i < n; ++i) { if (columnNames[i].empty()) { columnNames[i] = std::to_string(i + 1); } } +#endif } return columnNames; diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index 86da3dcb3..be69e699f 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -96,4 +96,5 @@ #if __cplusplus >= 202002L #define SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED +#define SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED #endif diff --git a/dev/implementations/table_definitions.h b/dev/implementations/table_definitions.h index c474df26c..26d9282b8 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -43,19 +43,24 @@ namespace sqlite_orm { column.template is()); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); +#if defined(SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) + for (int n = 1; const std::string& columnName: compositeKeyColumnNames) { + if (auto it = std::ranges::find(res, columnName, &table_xinfo::name); it != res.end()) { + it->pk = n; + } + ++n; + } +#else for (size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { const std::string& columnName = compositeKeyColumnNames[i]; -#if __cpp_lib_ranges >= 201911L - auto it = std::ranges::find(res, columnName, &table_xinfo::name); -#else auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_xinfo& ti) { return ti.name == columnName; }); -#endif if (it != res.end()) { it->pk = static_cast(i + 1); } } +#endif return res; } diff --git a/dev/pragma.h b/dev/pragma.h index b0f35f8ab..571a8d2b2 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -33,8 +33,7 @@ namespace sqlite_orm { res.reserve(argc); const auto rowExtractor = column_text_extractor(); for (int i = 0; i < argc; ++i) { - auto rowString = rowExtractor.extract(argv[i]); - res.push_back(std::move(rowString)); + res.push_back(rowExtractor.extract(argv[i])); } return 0; } diff --git a/dev/serializing_util.h b/dev/serializing_util.h index 4c538316a..b505c557d 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -237,9 +237,15 @@ namespace sqlite_orm { const auto& strings = std::get<1>(tpl); static constexpr std::array sep = {", ", ""}; +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + for (bool first = true; auto& s: strings) { + ss << sep[std::exchange(first, false)] << s; + } +#else for (size_t i = 0, first = true; i < strings.size(); ++i) { ss << sep[std::exchange(first, false)] << strings[i]; } +#endif return ss; } diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 7fd20144b..42bdbcb3d 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1689,12 +1689,19 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::table_has_no_primary_key_column}; } +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + static constexpr std::array sep = {" AND ", ""}; + for (bool first = true; const std::string& pkName: primaryKeyColumnNames) { + ss << sep[std::exchange(first, false)] << streaming_identifier(pkName) << " = ?"; + } +#else for (size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { if (i > 0) { ss << " AND "; } ss << streaming_identifier(primaryKeyColumnNames[i]) << " = ?"; } +#endif return ss.str(); } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index c92f4cb5b..dcb355b8c 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -127,6 +127,7 @@ using std::nullptr_t; #if __cplusplus >= 202002L #define SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED +#define SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED #endif // #include "cxx_compiler_quirks.h" @@ -16324,9 +16325,15 @@ namespace sqlite_orm { const auto& strings = std::get<1>(tpl); static constexpr std::array sep = {", ", ""}; +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + for (bool first = true; auto& s: strings) { + ss << sep[std::exchange(first, false)] << s; + } +#else for (size_t i = 0, first = true; i < strings.size(); ++i) { ss << sep[std::exchange(first, false)] << strings[i]; } +#endif return ss; } @@ -16534,8 +16541,7 @@ namespace sqlite_orm { res.reserve(argc); const auto rowExtractor = column_text_extractor(); for (int i = 0; i < argc; ++i) { - auto rowString = rowExtractor.extract(argv[i]); - res.push_back(std::move(rowString)); + res.push_back(rowExtractor.extract(argv[i])); } return 0; } @@ -19036,11 +19042,20 @@ namespace sqlite_orm { // 3. fill in blanks with numerical column identifiers { +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + for (size_t n = 1; std::string & name: columnNames) { + if (name.empty()) { + name = std::to_string(n); + } + ++n; + } +#else for (size_t i = 0, n = columnNames.size(); i < n; ++i) { if (columnNames[i].empty()) { columnNames[i] = std::to_string(i + 1); } } +#endif } return columnNames; @@ -21073,12 +21088,19 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::table_has_no_primary_key_column}; } +#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED + static constexpr std::array sep = {" AND ", ""}; + for (bool first = true; const std::string& pkName: primaryKeyColumnNames) { + ss << sep[std::exchange(first, false)] << streaming_identifier(pkName) << " = ?"; + } +#else for (size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { if (i > 0) { ss << " AND "; } ss << streaming_identifier(primaryKeyColumnNames[i]) << " = ?"; } +#endif return ss.str(); } @@ -23966,19 +23988,24 @@ namespace sqlite_orm { column.template is()); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); +#if defined(SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) + for (int n = 1; const std::string& columnName: compositeKeyColumnNames) { + if (auto it = std::ranges::find(res, columnName, &table_xinfo::name); it != res.end()) { + it->pk = n; + } + ++n; + } +#else for (size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { const std::string& columnName = compositeKeyColumnNames[i]; -#if __cpp_lib_ranges >= 201911L - auto it = std::ranges::find(res, columnName, &table_xinfo::name); -#else auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_xinfo& ti) { return ti.name == columnName; }); -#endif if (it != res.end()) { it->pk = static_cast(i + 1); } } +#endif return res; } From ae6aa1022ad6633592edf2b78a87582efc83769c Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 28 Jan 2025 23:02:29 +0200 Subject: [PATCH 038/170] Used `std::remove()` Using named modules the namespace scope will be much stricter, so adhere to it. --- examples/composite_key.cpp | 1 - tests/backup_tests.cpp | 4 ++-- tests/pragma_tests.cpp | 14 +++++++------- tests/tests.cpp | 4 ++-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/examples/composite_key.cpp b/examples/composite_key.cpp index 345734d46..30a0c9a65 100644 --- a/examples/composite_key.cpp +++ b/examples/composite_key.cpp @@ -2,7 +2,6 @@ * This example shows you how to create a storage with a tablw with a composite primary key * and another table woth foreign key to first table's primary compisite key. */ -#include #include #include diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index b935e3e82..dde1f252c 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -1,6 +1,6 @@ #include #include -#include // remove +#include // std::remove using namespace sqlite_orm; @@ -47,7 +47,7 @@ TEST_CASE("backup") { static int filenameSuffix = 0; // to make an unique filename for every test const std::string backupFilename = "backup" + std::to_string(filenameSuffix++) + ".sqlite"; SECTION("to") { - ::remove(backupFilename.c_str()); + std::remove(backupFilename.c_str()); auto storage2 = makeStorage(backupFilename); auto storage = makeStorage(""); storage.sync_schema(); diff --git a/tests/pragma_tests.cpp b/tests/pragma_tests.cpp index 062f1a10d..39819f2c2 100644 --- a/tests/pragma_tests.cpp +++ b/tests/pragma_tests.cpp @@ -1,5 +1,5 @@ #include -#include // ::remove +#include // std::remove #include using namespace sqlite_orm; @@ -12,7 +12,7 @@ TEST_CASE("module_list") { TEST_CASE("recursive_triggers") { auto filename = "recursive_triggers.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); storage.open_forever(); storage.pragma.recursive_triggers(true); @@ -37,7 +37,7 @@ TEST_CASE("locking_mode") { TEST_CASE("journal_mode") { auto filename = "journal_mode.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); auto storageCopy = storage; decltype(storage)* stor = nullptr; @@ -118,7 +118,7 @@ TEST_CASE("User version") { TEST_CASE("Auto vacuum") { auto filename = "autovacuum.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); @@ -160,7 +160,7 @@ TEST_CASE("Integrity Check") { }; auto filename = "integrity.sqlite"; - ::remove(filename); + std::remove(filename); std::string tablename = "users"; auto storage = make_storage(filename, @@ -185,7 +185,7 @@ TEST_CASE("Quick Check") { }; auto filename = "quick_check.sqlite"; - ::remove(filename); + std::remove(filename); std::string tablename = "users"; auto storage = make_storage(filename, @@ -201,7 +201,7 @@ TEST_CASE("Quick Check") { TEST_CASE("application_id") { auto filename = "application_id.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename); storage.pragma.application_id(3); diff --git a/tests/tests.cpp b/tests/tests.cpp index 8fd149691..4b354657f 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -3,7 +3,7 @@ #include // std::vector #include // std::string -#include // remove +#include // std::remove using namespace sqlite_orm; @@ -141,7 +141,7 @@ TEST_CASE("Custom collate") { } auto filename = "custom_collate.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage( filename, make_table("items", make_column("id", &Item::id, primary_key()), make_column("name", &Item::name))); From c11ee541684e9d90929130620481e96b346f5262 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 28 Jan 2025 23:03:02 +0200 Subject: [PATCH 039/170] Used internal test macro for C++ range algortithms There's an internal feature test macro called `SQLITE_ORM_CPP20_RANGES_SUPPORTED` for C++ range algorithms available since a while. Use this instead of the `__cpp_lib_ranges` test macro. --- dev/implementations/storage_definitions.h | 2 +- dev/journal_mode.h | 2 +- dev/locking_mode.h | 2 +- dev/storage.h | 4 ++-- dev/storage_base.h | 4 ++-- include/sqlite_orm/sqlite_orm.h | 14 +++++++------- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index cdb820b59..54eff3fef 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -126,7 +126,7 @@ namespace sqlite_orm { columnNames.reserve(table.template count_of()); table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { auto& columnName = column.name; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); #else auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), diff --git a/dev/journal_mode.h b/dev/journal_mode.h index 1c8c21424..aa0a8028c 100644 --- a/dev/journal_mode.h +++ b/dev/journal_mode.h @@ -59,7 +59,7 @@ namespace sqlite_orm { journal_mode::WAL, journal_mode::OFF, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); diff --git a/dev/locking_mode.h b/dev/locking_mode.h index 55ba6ac1f..2bf9d7d8a 100644 --- a/dev/locking_mode.h +++ b/dev/locking_mode.h @@ -33,7 +33,7 @@ namespace sqlite_orm { locking_mode::EXCLUSIVE, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); diff --git a/dev/storage.h b/dev/storage.h index 144283a97..5773559ea 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1436,7 +1436,7 @@ namespace sqlite_orm { static_if::value>( [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::for_each(expression.range.first, expression.range.second, std::ref(processObject), @@ -1481,7 +1481,7 @@ namespace sqlite_orm { static_if::value>( [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::for_each(expression.range.first, expression.range.second, std::ref(processObject), diff --git a/dev/storage_base.h b/dev/storage_base.h index 49c60124e..3750853da 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -850,7 +850,7 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, std::list& functions) const { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto it = std::ranges::find(functions, name, &udf_proxy::name); #else auto it = std::find_if(functions.begin(), functions.end(), [&name](auto& udfProxy) { @@ -985,7 +985,7 @@ namespace sqlite_orm { const std::string& columnName = storageColumnInfo.name; // search for a column in db with the same name -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto dbColumnInfoIt = std::ranges::find(dbTableInfo, columnName, &table_xinfo::name); #else auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index dcb355b8c..93bccd1a4 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -12666,7 +12666,7 @@ namespace sqlite_orm { journal_mode::WAL, journal_mode::OFF, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); @@ -12767,7 +12767,7 @@ namespace sqlite_orm { locking_mode::EXCLUSIVE, }}; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { return std::toupper(c); }); @@ -18328,7 +18328,7 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, std::list& functions) const { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto it = std::ranges::find(functions, name, &udf_proxy::name); #else auto it = std::find_if(functions.begin(), functions.end(), [&name](auto& udfProxy) { @@ -18463,7 +18463,7 @@ namespace sqlite_orm { const std::string& columnName = storageColumnInfo.name; // search for a column in db with the same name -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto dbColumnInfoIt = std::ranges::find(dbTableInfo, columnName, &table_xinfo::name); #else auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { @@ -23587,7 +23587,7 @@ namespace sqlite_orm { static_if::value>( [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::for_each(expression.range.first, expression.range.second, std::ref(processObject), @@ -23632,7 +23632,7 @@ namespace sqlite_orm { static_if::value>( [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED std::ranges::for_each(expression.range.first, expression.range.second, std::ref(processObject), @@ -24238,7 +24238,7 @@ namespace sqlite_orm { columnNames.reserve(table.template count_of()); table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { auto& columnName = column.name; -#if __cpp_lib_ranges >= 201911L +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); #else auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), From ed147d2801913a415a9be252cffb396eee9877c7 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 28 Jan 2025 23:05:20 +0200 Subject: [PATCH 040/170] Fixed a wrong word in the example file for `std::any` --- examples/any.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/any.cpp b/examples/any.cpp index 4355766fd..8f7815c39 100644 --- a/examples/any.cpp +++ b/examples/any.cpp @@ -3,7 +3,7 @@ * It is a bit more complex than the other examples, because it * uses custom row_extractor and statement_binder. * Please note that this implementation is not the one and only - * option of implementation of std:any bindong to sqlite_orm. + * option of implementation of `std:any` binding to sqlite_orm. * It is just an example of how to use std::any as a mapped type. * Implementation is based on 5 types SQLite supports: * NULL, INTEGER, REAL, TEXT, BLOB. From 178794a778237c47c8242ca78cbebffcbfa1446e Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 28 Jan 2025 23:56:36 +0200 Subject: [PATCH 041/170] Say good-bye to pre-C++14 --- dev/conditions.h | 23 +-- dev/constraints.h | 7 - dev/function.h | 13 -- dev/functional/cxx_check_prerequisites.h | 13 ++ dev/functional/cxx_core_features.h | 8 - dev/functional/cxx_universal.h | 1 + dev/functional/mpl.h | 38 +---- dev/indexed_column.h | 9 +- dev/object_from_column_builder.h | 5 - dev/order_by_serializer.h | 8 +- dev/prepared_statement.h | 8 - dev/schema/index.h | 4 - dev/schema/triggers.h | 4 - dev/select_constraints.h | 25 --- dev/statement_serializer.h | 4 +- dev/table_info.h | 5 +- examples/blob_binding.cpp | 10 -- examples/case.cpp | 6 - examples/check.cpp | 13 -- examples/column_aliases.cpp | 6 - examples/core_functions.cpp | 33 ---- examples/custom_aliases.cpp | 11 -- examples/except_intersection.cpp | 15 -- examples/generated_column.cpp | 6 - examples/prepared_statement.cpp | 18 -- examples/triggers.cpp | 7 - examples/user_defined_functions.cpp | 5 - include/sqlite_orm/sqlite_orm.h | 160 +++--------------- tests/backup_tests.cpp | 5 - .../core_functions_tests.cpp | 49 ------ tests/constraints/composite_key.cpp | 5 - tests/constraints/unique.cpp | 17 -- tests/get_all_custom_containers.cpp | 5 - tests/operators/binary_operators.cpp | 5 - tests/operators/bitwise_operators.cpp | 5 - tests/operators/glob.cpp | 6 - tests/operators/in.cpp | 5 - tests/operators/is_null.cpp | 5 - tests/operators/like.cpp | 5 - tests/operators/not_operator.cpp | 5 - .../prepared_common.h | 17 -- tests/schema/index_tests.cpp | 5 - tests/select_constraints_tests.cpp | 38 ----- .../statements/insert_replace.cpp | 10 -- .../statements/update.cpp | 5 - .../table_constraints/foreign_key.cpp | 5 - tests/storage_non_crud_tests.cpp | 10 -- tests/storage_tests.cpp | 6 - tests/sync_schema_tests.cpp | 11 -- tests/tests.cpp | 8 + tests/tests4.cpp | 10 -- tests/transaction_tests.cpp | 5 - tests/trigger_tests.cpp | 15 -- .../unique_cases/get_all_with_two_tables.cpp | 5 - .../prepare_get_all_with_case.cpp | 5 - tests/user_defined_functions.cpp | 5 - 56 files changed, 71 insertions(+), 671 deletions(-) create mode 100644 dev/functional/cxx_check_prerequisites.h diff --git a/dev/conditions.h b/dev/conditions.h index 880336a56..3d33fe65b 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -368,10 +368,6 @@ namespace sqlite_orm { struct in_base { bool negative = false; // used in not_in - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - in_base(bool negative) : negative{negative} {} -#endif }; /** @@ -435,14 +431,7 @@ namespace sqlite_orm { struct order_by_base { int asc_desc = 0; // 1: asc, -1: desc - std::string _collate_argument; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - order_by_base() = default; - - order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : - asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} -#endif + std::string collate_argument; }; struct order_by_string { @@ -477,25 +466,25 @@ namespace sqlite_orm { self collate_binary() const { auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); + res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); return res; } self collate_nocase() const { auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); + res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); return res; } self collate_rtrim() const { auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); + res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); return res; } self collate(std::string name) const { auto res = *this; - res._collate_argument = std::move(name); + res.collate_argument = std::move(name); return res; } @@ -544,7 +533,7 @@ namespace sqlite_orm { auto columnName = serialize(order_by.expression, newContext); this->entries.emplace_back(std::move(columnName), order_by.asc_desc, - std::move(order_by._collate_argument)); + std::move(order_by.collate_argument)); } const_iterator begin() const { diff --git a/dev/constraints.h b/dev/constraints.h index 7eb4a1cda..0fda4b7b9 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -386,9 +386,6 @@ namespace sqlite_orm { struct collate_constraint_t { collate_argument argument = collate_argument::binary; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - collate_constraint_t(collate_argument argument) : argument{argument} {} -#endif operator std::string() const { return "COLLATE " + this->string_from_collate_argument(this->argument); @@ -425,10 +422,6 @@ namespace sqlite_orm { bool full = true; storage_type storage = storage_type::not_specified; #endif - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} -#endif }; #if SQLITE_VERSION_NUMBER >= 3031000 diff --git a/dev/function.h b/dev/function.h index cffff9732..0a1759ad7 100644 --- a/dev/function.h +++ b/dev/function.h @@ -300,7 +300,6 @@ namespace sqlite_orm { using func_param_type = std::tuple_element_t; using call_arg_type = unpacked_arg_t>; -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED constexpr bool valid = validate_pointer_value_type, unpacked_arg_t>>( @@ -308,14 +307,6 @@ namespace sqlite_orm { (polyfill::is_specialization_of_v) > {}); return validate_pointer_value_types(polyfill::index_constant{}) && valid; -#else - return validate_pointer_value_types(polyfill::index_constant{}) && - validate_pointer_value_type, - unpacked_arg_t>>( - polyfill::bool_constant < (polyfill::is_specialization_of_v) || - (polyfill::is_specialization_of_v) > {}); -#endif } /* @@ -323,11 +314,7 @@ namespace sqlite_orm { * but other call argument types are not checked against the parameter types of the function. */ template -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED SQLITE_ORM_CONSTEVAL void check_function_call() { -#else - void check_function_call() { -#endif using call_args_tuple = std::tuple; using function_params_tuple = typename callable_arguments::args_tuple; constexpr size_t callArgsCount = std::tuple_size::value; diff --git a/dev/functional/cxx_check_prerequisites.h b/dev/functional/cxx_check_prerequisites.h new file mode 100644 index 000000000..0531d656e --- /dev/null +++ b/dev/functional/cxx_check_prerequisites.h @@ -0,0 +1,13 @@ +#pragma once + +/* + * This header detects missing core C++ language features on which sqlite_orm depends, bailing out with a hard error. + */ + +#if __cpp_aggregate_nsdmi < 201304L +#error A C++14 conforming compiler is required +#endif + +#if __cpp_constexpr < 201304L +#error A C++14 conforming compiler is required +#endif diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index be69e699f..45363b7bf 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -17,14 +17,6 @@ #define SQLITE_ORM_HAS_INCLUDE(file) 0L #endif -#if __cpp_aggregate_nsdmi >= 201304L -#define SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED -#endif - -#if __cpp_constexpr >= 201304L -#define SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED -#endif - #if __cpp_noexcept_function_type >= 201510L #define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED #endif diff --git a/dev/functional/cxx_universal.h b/dev/functional/cxx_universal.h index dd2b4dc20..e84acbf67 100644 --- a/dev/functional/cxx_universal.h +++ b/dev/functional/cxx_universal.h @@ -16,5 +16,6 @@ // actually it should be available when including stddef.h using std::nullptr_t; +#include "cxx_check_prerequisites.h" #include "cxx_core_features.h" #include "cxx_compiler_quirks.h" diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index 4c02b1244..d14456d60 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -29,11 +29,7 @@ */ #include // std::true_type, std::false_type, std::is_same, std::negation, std::conjunction, std::disjunction -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED #include -#else -#include -#endif #include "cxx_type_traits_polyfill.h" #include "mpl/conditional.h" @@ -334,7 +330,6 @@ namespace sqlite_orm { using bind_front_higherorder_fn = bind_front::quote_fn, quote_fn, Bound...>; -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED constexpr size_t find_first_true_helper(std::initializer_list values) { size_t i = 0; for (auto first = values.begin(); first != values.end() && !*first; ++first) { @@ -350,17 +345,6 @@ namespace sqlite_orm { } return n; } -#else - template - constexpr size_t find_first_true_helper(const std::array& values, size_t i = 0) { - return i == N || values[i] ? 0 : 1 + find_first_true_helper(values, i + 1); - } - - template - constexpr size_t count_true_helper(const std::array& values, size_t i = 0) { - return i == N ? 0 : values[i] + count_true_helper(values, i + 1); - } -#endif /* * Quoted metafunction that invokes the specified quoted predicate metafunction on each element of a type list, @@ -377,11 +361,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = find_first_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({PredicateQ::template fn>::value...}); + static constexpr size_t value = + find_first_true_helper({PredicateQ::template fn>::value...}); using type = polyfill::index_constant; }; @@ -407,11 +388,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = count_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({PredicateQ::template fn>::value...}); + static constexpr size_t value = + count_true_helper({PredicateQ::template fn>::value...}); using type = polyfill::index_constant; }; @@ -437,12 +415,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = - static_cast(count_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({TraitQ::template fn>::value...})); + static constexpr size_t value = static_cast( + count_true_helper({TraitQ::template fn>::value...})); using type = polyfill::bool_constant; }; diff --git a/dev/indexed_column.h b/dev/indexed_column.h index daa36d488..eb8221da0 100644 --- a/dev/indexed_column.h +++ b/dev/indexed_column.h @@ -13,18 +13,13 @@ namespace sqlite_orm { struct indexed_column_t { using column_type = C; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - indexed_column_t(column_type _column_or_expression) : - column_or_expression(std::move(_column_or_expression)) {} -#endif - column_type column_or_expression; - std::string _collation_name; + std::string collation_name; int _order = 0; // -1 = desc, 1 = asc, 0 = not specified indexed_column_t collate(std::string name) { auto res = std::move(*this); - res._collation_name = std::move(name); + res.collation_name = std::move(name); return res; } diff --git a/dev/object_from_column_builder.h b/dev/object_from_column_builder.h index 86aae7f40..a8f633ba2 100644 --- a/dev/object_from_column_builder.h +++ b/dev/object_from_column_builder.h @@ -18,11 +18,6 @@ namespace sqlite_orm { struct object_from_column_builder_base { sqlite3_stmt* stmt = nullptr; int columnIndex = -1; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - object_from_column_builder_base(sqlite3_stmt* stmt, int columnIndex = -1) : - stmt{stmt}, columnIndex{columnIndex} {} -#endif }; /** diff --git a/dev/order_by_serializer.h b/dev/order_by_serializer.h index bc2d418ac..e6addabf2 100644 --- a/dev/order_by_serializer.h +++ b/dev/order_by_serializer.h @@ -27,8 +27,8 @@ namespace sqlite_orm { newContext.skip_table_name = false; ss << serialize(orderBy.expression, newContext); - if (!orderBy._collate_argument.empty()) { - ss << " COLLATE " << orderBy._collate_argument; + if (!orderBy.collate_argument.empty()) { + ss << " COLLATE " << orderBy.collate_argument; } switch (orderBy.asc_desc) { case 1: @@ -57,8 +57,8 @@ namespace sqlite_orm { } ss << entry.name; - if (!entry._collate_argument.empty()) { - ss << " COLLATE " << entry._collate_argument; + if (!entry.collate_argument.empty()) { + ss << " COLLATE " << entry.collate_argument; } switch (entry.asc_desc) { case 1: diff --git a/dev/prepared_statement.h b/dev/prepared_statement.h index e3dfdebd4..440d96ff1 100644 --- a/dev/prepared_statement.h +++ b/dev/prepared_statement.h @@ -27,10 +27,6 @@ namespace sqlite_orm { sqlite3_stmt* stmt = nullptr; connection_ref con; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} -#endif - ~prepared_statement_base() { sqlite3_finalize(this->stmt); } @@ -313,10 +309,6 @@ namespace sqlite_orm { struct insert_constraint { conflict_action action = conflict_action::abort; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - insert_constraint(conflict_action action) : action{action} {} -#endif }; template diff --git a/dev/schema/index.h b/dev/schema/index.h index 63c6846ea..02ee38d7b 100644 --- a/dev/schema/index.h +++ b/dev/schema/index.h @@ -15,10 +15,6 @@ namespace sqlite_orm { struct index_base { std::string name; bool unique = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} -#endif }; template diff --git a/dev/schema/triggers.h b/dev/schema/triggers.h index 5ec53e38b..c95d312a2 100644 --- a/dev/schema/triggers.h +++ b/dev/schema/triggers.h @@ -203,10 +203,6 @@ namespace sqlite_orm { type_t type = type_t::ignore; std::string message; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - raise_t(type_t type, std::string message) : type{type}, message{std::move(message)} {} -#endif }; template diff --git a/dev/select_constraints.h b/dev/select_constraints.h index d023745c5..3ebb8482a 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -88,10 +88,6 @@ namespace sqlite_orm { columns_type columns; static constexpr int count = std::tuple_size::value; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - columns_t(columns_type columns) : columns{std::move(columns)} {} -#endif }; template @@ -112,10 +108,6 @@ namespace sqlite_orm { columns_type columns; static constexpr int count = std::tuple_size::value; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - struct_t(columns_type columns) : columns{std::move(columns)} {} -#endif }; template @@ -135,11 +127,6 @@ namespace sqlite_orm { return_type col; conditions_type conditions; bool highest_level = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - select_t(return_type col, conditions_type conditions) : - col{std::move(col)}, conditions{std::move(conditions)} {} -#endif }; template @@ -173,10 +160,6 @@ namespace sqlite_orm { struct union_base { bool all = false; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - union_base(bool all) : all{all} {} -#endif - operator std::string() const { if (!this->all) { return "UNION"; @@ -339,10 +322,6 @@ namespace sqlite_orm { using type = T; bool defined_order = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - asterisk_t(bool definedOrder) : defined_order{definedOrder} {} -#endif }; template @@ -350,10 +329,6 @@ namespace sqlite_orm { using type = T; bool defined_order = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - object_t(bool definedOrder) : defined_order{definedOrder} {} -#endif }; template diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 42bdbcb3d..05dc3ffd5 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1835,8 +1835,8 @@ namespace sqlite_orm { std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; ss << serialize(statement.column_or_expression, context); - if (!statement._collation_name.empty()) { - ss << " COLLATE " << statement._collation_name; + if (!statement.collation_name.empty()) { + ss << " COLLATE " << statement.collation_name; } if (statement._order) { switch (statement._order) { diff --git a/dev/table_info.h b/dev/table_info.h index ac0118412..a8d2d1b71 100644 --- a/dev/table_info.h +++ b/dev/table_info.h @@ -1,6 +1,7 @@ #pragma once #include // std::string +#include // std::move namespace sqlite_orm { @@ -12,7 +13,7 @@ namespace sqlite_orm { std::string dflt_value; int pk = 0; -#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED table_info(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, @@ -33,7 +34,7 @@ namespace sqlite_orm { int pk = 0; int hidden = 0; // different than 0 => generated_always_as() - TODO verify -#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED table_xinfo(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, diff --git a/examples/blob_binding.cpp b/examples/blob_binding.cpp index fbd29219f..ab8b57967 100644 --- a/examples/blob_binding.cpp +++ b/examples/blob_binding.cpp @@ -16,11 +16,6 @@ struct Rect { int y = 0; int width = 0; int height = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Rect() = default; - Rect(int x, int y, int width, int height) : x{x}, y{y}, width{width}, height{height} {} -#endif }; bool operator==(const Rect& lhs, const Rect& rhs) { @@ -30,11 +25,6 @@ bool operator==(const Rect& lhs, const Rect& rhs) { struct Zone { int id = 0; Rect rect; // this member will be mapped as BLOB column - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Zone() = default; - Zone(int id, Rect rect) : id{id}, rect{rect} {} -#endif }; bool operator==(const Zone& lhs, const Zone& rhs) { diff --git a/examples/case.cpp b/examples/case.cpp index d40810afe..4249fbd85 100644 --- a/examples/case.cpp +++ b/examples/case.cpp @@ -15,12 +15,6 @@ int main() { std::string name; std::string email; float marks = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Student() {} - Student(int id, std::string name, std::string email, float marks) : - id{id}, name{std::move(name)}, email{std::move(email)}, marks{marks} {} -#endif }; auto storage = make_storage({}, diff --git a/examples/check.cpp b/examples/check.cpp index 7233862ff..0ed10c880 100644 --- a/examples/check.cpp +++ b/examples/check.cpp @@ -13,13 +13,6 @@ int main() { std::string lastName; std::string email; std::string phone; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Contact() {} - Contact(int id, std::string firstName, std::string lastName, std::string email, std::string phone) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, email{std::move(email)}, - phone{std::move(phone)} {} -#endif }; struct Product { @@ -27,12 +20,6 @@ int main() { std::string name; float listPrice = 0; float discount = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Product() {} - Product(int id, std::string name, float listPrice, float discount) : - id{id}, name{std::move(name)}, listPrice{listPrice}, discount{discount} {} -#endif }; auto storage = make_storage(":memory:", diff --git a/examples/column_aliases.cpp b/examples/column_aliases.cpp index 8fdb55cc1..0068586d3 100644 --- a/examples/column_aliases.cpp +++ b/examples/column_aliases.cpp @@ -13,12 +13,6 @@ void marvel_hero_ordered_by_o_pos() { std::string name; std::string abilities; short points = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - MarvelHero() {} - MarvelHero(int id, std::string name, std::string abilities, short points) : - id{id}, name{std::move(name)}, abilities{std::move(abilities)}, points{points} {} -#endif }; auto storage = make_storage("", diff --git a/examples/core_functions.cpp b/examples/core_functions.cpp index ce6dc4149..b3edb2d9c 100644 --- a/examples/core_functions.cpp +++ b/examples/core_functions.cpp @@ -10,12 +10,6 @@ struct MarvelHero { std::string name; std::string abilities; short points = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - MarvelHero() {} - MarvelHero(int id, std::string name, std::string abilities, short points) : - id{id}, name{std::move(name)}, abilities{std::move(abilities)}, points{points} {} -#endif }; struct Contact { @@ -23,12 +17,6 @@ struct Contact { std::string firstName; std::string lastName; std::string phone; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Contact() {} - Contact(int id, std::string firstName, std::string lastName, std::string phone) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, phone{std::move(phone)} {} -#endif }; struct Customer { @@ -45,27 +33,6 @@ struct Customer { std::unique_ptr fax; std::string email; int supportRepId = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Customer() {} - Customer(int id, - std::string firstName, - std::string lastName, - std::string company, - std::string address, - std::string city, - std::string state, - std::string country, - std::string postalCode, - std::string phone, - std::unique_ptr fax, - std::string email, - int supportRepId) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, company{std::move(company)}, - address{std::move(address)}, city{std::move(city)}, state{std::move(state)}, country{std::move(country)}, - postalCode{std::move(postalCode)}, phone{std::move(phone)}, fax{std::move(fax)}, email{std::move(email)}, - supportRepId{supportRepId} {} -#endif }; int main(int, char** argv) { diff --git a/examples/custom_aliases.cpp b/examples/custom_aliases.cpp index fcd44189f..931f54589 100644 --- a/examples/custom_aliases.cpp +++ b/examples/custom_aliases.cpp @@ -17,23 +17,12 @@ struct Employee { int age = 0; std::string address; float salary = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Employee() {} - Employee(int id, std::string name, int age, std::string address, float salary) : - id{id}, name{std::move(name)}, age{age}, address{std::move(address)}, salary{salary} {} -#endif }; struct Department { int id = 0; std::string dept; int empId = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Department() {} - Department(int id, std::string dept, int empId) : id{id}, dept{std::move(dept)}, empId{empId} {} -#endif }; using namespace sqlite_orm; diff --git a/examples/except_intersection.cpp b/examples/except_intersection.cpp index 7b3f3bd65..928649d0f 100644 --- a/examples/except_intersection.cpp +++ b/examples/except_intersection.cpp @@ -11,11 +11,6 @@ using std::endl; struct DeptMaster { int deptId = 0; std::string deptName; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - DeptMaster() = default; - DeptMaster(int deptId, std::string deptName) : deptId{deptId}, deptName{std::move(deptName)} {} -#endif }; struct EmpMaster { @@ -24,16 +19,6 @@ struct EmpMaster { std::string lastName; long salary; decltype(DeptMaster::deptId) deptId; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - EmpMaster() = default; - EmpMaster(int empId, - std::string firstName, - std::string lastName, - long salary, - decltype(DeptMaster::deptId) deptId) : - empId{empId}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, salary{salary}, deptId{deptId} {} -#endif }; int main() { diff --git a/examples/generated_column.cpp b/examples/generated_column.cpp index 7626cec3f..c56d4c5c7 100644 --- a/examples/generated_column.cpp +++ b/examples/generated_column.cpp @@ -23,12 +23,6 @@ int main() { int quantity = 0; float price = 0; float totalValue = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Product() {} - Product(int id, std::string name, int quantity, float price, float totalValue = 0.f) : - id{id}, name{std::move(name)}, quantity{quantity}, price{price}, totalValue{totalValue} {} -#endif }; auto storage = make_storage({}, make_table("products", diff --git a/examples/prepared_statement.cpp b/examples/prepared_statement.cpp index f06c4ed44..6ba752b43 100644 --- a/examples/prepared_statement.cpp +++ b/examples/prepared_statement.cpp @@ -18,36 +18,18 @@ struct Doctor { int doctor_id = 0; std::string doctor_name; std::string degree; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Doctor() = default; - Doctor(int doctor_id, std::string doctor_name, std::string degree) : - doctor_id{doctor_id}, doctor_name{std::move(doctor_name)}, degree{std::move(degree)} {} -#endif }; struct Speciality { int spl_id = 0; std::string spl_descrip; int doctor_id = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Speciality() = default; - Speciality(int spl_id, std::string spl_descrip, int doctor_id) : - spl_id{spl_id}, spl_descrip{std::move(spl_descrip)}, doctor_id{doctor_id} {} -#endif }; struct Visit { int doctor_id = 0; std::string patient_name; std::string vdate; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Visit() = default; - Visit(int doctor_id, std::string patient_name, std::string vdate) : - doctor_id{doctor_id}, patient_name{std::move(patient_name)}, vdate{std::move(vdate)} {} -#endif }; int main() { diff --git a/examples/triggers.cpp b/examples/triggers.cpp index 24810bf9e..3dd8972c9 100644 --- a/examples/triggers.cpp +++ b/examples/triggers.cpp @@ -16,13 +16,6 @@ struct Lead { std::string lastName; std::string email; std::string phone; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Lead() = default; - Lead(int id, std::string firstName, std::string lastName, std::string email, std::string phone) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, email{std::move(email)}, - phone{std::move(phone)} {} -#endif }; struct LeadLog { diff --git a/examples/user_defined_functions.cpp b/examples/user_defined_functions.cpp index d9ee12ee7..062d809bb 100644 --- a/examples/user_defined_functions.cpp +++ b/examples/user_defined_functions.cpp @@ -121,11 +121,6 @@ int main() { int a = 0; int b = 0; int c = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Table() = default; - Table(int a, int b, int c) : a{a}, b{b}, c{c} {} -#endif }; auto storage = make_storage( diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 93bccd1a4..be0a22df1 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -48,12 +48,12 @@ using std::nullptr_t; #define SQLITE_ORM_HAS_INCLUDE(file) 0L #endif -#if __cpp_aggregate_nsdmi >= 201304L -#define SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED +#if __cpp_aggregate_nsdmi < 201304L +#error A C++14 conforming compiler is required #endif -#if __cpp_constexpr >= 201304L -#define SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED +#if __cpp_constexpr < 201304L +#error A C++14 conforming compiler is required #endif #if __cpp_noexcept_function_type >= 201510L @@ -776,11 +776,7 @@ namespace sqlite_orm { */ #include // std::true_type, std::false_type, std::is_same, std::negation, std::conjunction, std::disjunction -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED #include -#else -#include -#endif // #include "cxx_type_traits_polyfill.h" @@ -1082,7 +1078,6 @@ namespace sqlite_orm { using bind_front_higherorder_fn = bind_front::quote_fn, quote_fn, Bound...>; -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED constexpr size_t find_first_true_helper(std::initializer_list values) { size_t i = 0; for (auto first = values.begin(); first != values.end() && !*first; ++first) { @@ -1098,17 +1093,6 @@ namespace sqlite_orm { } return n; } -#else - template - constexpr size_t find_first_true_helper(const std::array& values, size_t i = 0) { - return i == N || values[i] ? 0 : 1 + find_first_true_helper(values, i + 1); - } - - template - constexpr size_t count_true_helper(const std::array& values, size_t i = 0) { - return i == N ? 0 : values[i] + count_true_helper(values, i + 1); - } -#endif /* * Quoted metafunction that invokes the specified quoted predicate metafunction on each element of a type list, @@ -1125,11 +1109,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = find_first_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({PredicateQ::template fn>::value...}); + static constexpr size_t value = + find_first_true_helper({PredicateQ::template fn>::value...}); using type = polyfill::index_constant; }; @@ -1155,11 +1136,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = count_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({PredicateQ::template fn>::value...}); + static constexpr size_t value = + count_true_helper({PredicateQ::template fn>::value...}); using type = polyfill::index_constant; }; @@ -1185,12 +1163,8 @@ namespace sqlite_orm { template class Pack, class... T, class ProjectQ> struct invoke_this_fn, ProjectQ> { // hoist result into `value` [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR] - static constexpr size_t value = - static_cast(count_true_helper -#ifndef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED - -#endif - ({TraitQ::template fn>::value...})); + static constexpr size_t value = static_cast( + count_true_helper({TraitQ::template fn>::value...})); using type = polyfill::bool_constant; }; @@ -3558,9 +3532,6 @@ namespace sqlite_orm { struct collate_constraint_t { collate_argument argument = collate_argument::binary; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - collate_constraint_t(collate_argument argument) : argument{argument} {} -#endif operator std::string() const { return "COLLATE " + this->string_from_collate_argument(this->argument); @@ -3597,10 +3568,6 @@ namespace sqlite_orm { bool full = true; storage_type storage = storage_type::not_specified; #endif - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} -#endif }; #if SQLITE_VERSION_NUMBER >= 3031000 @@ -5144,10 +5111,6 @@ namespace sqlite_orm { struct in_base { bool negative = false; // used in not_in - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - in_base(bool negative) : negative{negative} {} -#endif }; /** @@ -5211,14 +5174,7 @@ namespace sqlite_orm { struct order_by_base { int asc_desc = 0; // 1: asc, -1: desc - std::string _collate_argument; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - order_by_base() = default; - - order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : - asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} -#endif + std::string collate_argument; }; struct order_by_string { @@ -5253,25 +5209,25 @@ namespace sqlite_orm { self collate_binary() const { auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); + res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); return res; } self collate_nocase() const { auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); + res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); return res; } self collate_rtrim() const { auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); + res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); return res; } self collate(std::string name) const { auto res = *this; - res._collate_argument = std::move(name); + res.collate_argument = std::move(name); return res; } @@ -5320,7 +5276,7 @@ namespace sqlite_orm { auto columnName = serialize(order_by.expression, newContext); this->entries.emplace_back(std::move(columnName), order_by.asc_desc, - std::move(order_by._collate_argument)); + std::move(order_by.collate_argument)); } const_iterator begin() const { @@ -8641,10 +8597,6 @@ namespace sqlite_orm { columns_type columns; static constexpr int count = std::tuple_size::value; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - columns_t(columns_type columns) : columns{std::move(columns)} {} -#endif }; template @@ -8665,10 +8617,6 @@ namespace sqlite_orm { columns_type columns; static constexpr int count = std::tuple_size::value; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - struct_t(columns_type columns) : columns{std::move(columns)} {} -#endif }; template @@ -8688,11 +8636,6 @@ namespace sqlite_orm { return_type col; conditions_type conditions; bool highest_level = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - select_t(return_type col, conditions_type conditions) : - col{std::move(col)}, conditions{std::move(conditions)} {} -#endif }; template @@ -8726,10 +8669,6 @@ namespace sqlite_orm { struct union_base { bool all = false; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - union_base(bool all) : all{all} {} -#endif - operator std::string() const { if (!this->all) { return "UNION"; @@ -8892,10 +8831,6 @@ namespace sqlite_orm { using type = T; bool defined_order = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - asterisk_t(bool definedOrder) : defined_order{definedOrder} {} -#endif }; template @@ -8903,10 +8838,6 @@ namespace sqlite_orm { using type = T; bool defined_order = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - object_t(bool definedOrder) : defined_order{definedOrder} {} -#endif }; template @@ -11111,7 +11042,6 @@ namespace sqlite_orm { using func_param_type = std::tuple_element_t; using call_arg_type = unpacked_arg_t>; -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED constexpr bool valid = validate_pointer_value_type, unpacked_arg_t>>( @@ -11119,14 +11049,6 @@ namespace sqlite_orm { (polyfill::is_specialization_of_v) > {}); return validate_pointer_value_types(polyfill::index_constant{}) && valid; -#else - return validate_pointer_value_types(polyfill::index_constant{}) && - validate_pointer_value_type, - unpacked_arg_t>>( - polyfill::bool_constant < (polyfill::is_specialization_of_v) || - (polyfill::is_specialization_of_v) > {}); -#endif } /* @@ -11134,11 +11056,7 @@ namespace sqlite_orm { * but other call argument types are not checked against the parameter types of the function. */ template -#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED SQLITE_ORM_CONSTEVAL void check_function_call() { -#else - void check_function_call() { -#endif using call_args_tuple = std::tuple; using function_params_tuple = typename callable_arguments::args_tuple; constexpr size_t callArgsCount = std::tuple_size::value; @@ -11786,7 +11704,7 @@ namespace sqlite_orm { std::string dflt_value; int pk = 0; -#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED table_info(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, @@ -11807,7 +11725,7 @@ namespace sqlite_orm { int pk = 0; int hidden = 0; // different than 0 => generated_always_as() - TODO verify -#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED table_xinfo(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, @@ -11919,18 +11837,13 @@ namespace sqlite_orm { struct indexed_column_t { using column_type = C; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - indexed_column_t(column_type _column_or_expression) : - column_or_expression(std::move(_column_or_expression)) {} -#endif - column_type column_or_expression; - std::string _collation_name; + std::string collation_name; int _order = 0; // -1 = desc, 1 = asc, 0 = not specified indexed_column_t collate(std::string name) { auto res = std::move(*this); - res._collation_name = std::move(name); + res.collation_name = std::move(name); return res; } @@ -11982,10 +11895,6 @@ namespace sqlite_orm { struct index_base { std::string name; bool unique = false; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} -#endif }; template @@ -13395,11 +13304,6 @@ namespace sqlite_orm { struct object_from_column_builder_base { sqlite3_stmt* stmt = nullptr; int columnIndex = -1; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - object_from_column_builder_base(sqlite3_stmt* stmt, int columnIndex = -1) : - stmt{stmt}, columnIndex{columnIndex} {} -#endif }; /** @@ -14165,10 +14069,6 @@ namespace sqlite_orm { sqlite3_stmt* stmt = nullptr; connection_ref con; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} -#endif - ~prepared_statement_base() { sqlite3_finalize(this->stmt); } @@ -14451,10 +14351,6 @@ namespace sqlite_orm { struct insert_constraint { conflict_action action = conflict_action::abort; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - insert_constraint(conflict_action action) : action{action} {} -#endif }; template @@ -19093,8 +18989,8 @@ namespace sqlite_orm { newContext.skip_table_name = false; ss << serialize(orderBy.expression, newContext); - if (!orderBy._collate_argument.empty()) { - ss << " COLLATE " << orderBy._collate_argument; + if (!orderBy.collate_argument.empty()) { + ss << " COLLATE " << orderBy.collate_argument; } switch (orderBy.asc_desc) { case 1: @@ -19123,8 +19019,8 @@ namespace sqlite_orm { } ss << entry.name; - if (!entry._collate_argument.empty()) { - ss << " COLLATE " << entry._collate_argument; + if (!entry.collate_argument.empty()) { + ss << " COLLATE " << entry.collate_argument; } switch (entry.asc_desc) { case 1: @@ -19362,10 +19258,6 @@ namespace sqlite_orm { type_t type = type_t::ignore; std::string message; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - raise_t(type_t type, std::string message) : type{type}, message{std::move(message)} {} -#endif }; template @@ -21234,8 +21126,8 @@ namespace sqlite_orm { std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; ss << serialize(statement.column_or_expression, context); - if (!statement._collation_name.empty()) { - ss << " COLLATE " << statement._collation_name; + if (!statement.collation_name.empty()) { + ss << " COLLATE " << statement.collation_name; } if (statement._order) { switch (statement._order) { diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index dde1f252c..09574ea40 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -8,11 +8,6 @@ namespace { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; bool operator==(const User& lhs, const User& rhs) { diff --git a/tests/built_in_functions_tests/core_functions_tests.cpp b/tests/built_in_functions_tests/core_functions_tests.cpp index ad47b5f6a..e003f0b06 100644 --- a/tests/built_in_functions_tests/core_functions_tests.cpp +++ b/tests/built_in_functions_tests/core_functions_tests.cpp @@ -8,11 +8,6 @@ TEST_CASE("substr") { std::string text; int x = 0; int y = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Test() = default; - Test(std::string text, int x, int y) : text{std::move(text)}, x{x}, y{y} {} -#endif }; auto storage = make_storage( {}, @@ -51,11 +46,6 @@ TEST_CASE("substr") { TEST_CASE("zeroblob") { struct Test { int value = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Test() = default; - Test(int value) : value{value} {} -#endif }; auto storage = make_storage({}, make_table("test", make_column("value", &Test::value))); @@ -180,12 +170,6 @@ TEST_CASE("quote") { std::string name; int managerId = 0; int locationId = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Department() = default; - Department(int id, std::string name, int managerId, int locationId) : - id{id}, name{std::move(name)}, managerId{managerId}, locationId{locationId} {} -#endif }; auto storage = make_storage({}, make_table("departments", @@ -264,12 +248,6 @@ TEST_CASE("instr") { std::string firstName; std::string lastName; std::string address; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Employee() = default; - Employee(int id, std::string firstName, std::string lastName, std::string address) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, address{std::move(address)} {} -#endif }; struct sw : alias_tag { @@ -336,12 +314,6 @@ namespace replace_func_local { std::string firstName; std::string lastName; std::string phone; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Contact() = default; - Contact(int id, std::string firstName, std::string lastName, std::string phone) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, phone{std::move(phone)} {} -#endif }; bool operator==(const Contact& lhs, const Contact& rhs) { @@ -524,27 +496,6 @@ TEST_CASE("ifnull") { std::unique_ptr fax; std::string email; int supportRepId = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Customer() = default; - Customer(int id, - std::string firstName, - std::string lastName, - std::string company, - std::string address, - std::string city, - std::string state, - std::string country, - std::string postalCode, - std::string phone, - decltype(fax) fax, - std::string email, - int supportRepId) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, company{std::move(company)}, - address{std::move(address)}, city{std::move(city)}, state{std::move(state)}, country{std::move(country)}, - postalCode{std::move(postalCode)}, phone{std::move(phone)}, fax{std::move(fax)}, email{std::move(email)}, - supportRepId{supportRepId} {} -#endif }; auto storage = make_storage({}, make_table("customers", diff --git a/tests/constraints/composite_key.cpp b/tests/constraints/composite_key.cpp index 91d9b06f0..1641dc63d 100644 --- a/tests/constraints/composite_key.cpp +++ b/tests/constraints/composite_key.cpp @@ -8,11 +8,6 @@ TEST_CASE("Composite key") { int year = 0; int month = 0; int amount = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Record() = default; - Record(int year, int month, int amount) : year{year}, month{month}, amount{amount} {} -#endif }; auto recordsTableName = "records"; diff --git a/tests/constraints/unique.cpp b/tests/constraints/unique.cpp index dd7a81be9..bea9d37c2 100644 --- a/tests/constraints/unique.cpp +++ b/tests/constraints/unique.cpp @@ -11,32 +11,15 @@ TEST_CASE("Unique") { std::string firstName; std::string lastName; std::string email; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Contact() = default; - Contact(int id, std::string firstName, std::string lastName, std::string email) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, email{std::move(email)} {} -#endif }; struct Shape { int id = 0; std::string backgroundColor; std::string foregroundColor; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Shape() = default; - Shape(int id, std::string backgroundColor, std::string foregroundColor) : - id{id}, backgroundColor{std::move(backgroundColor)}, foregroundColor{std::move(foregroundColor)} {} -#endif }; struct List { int id = 0; std::unique_ptr email; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - List() = default; - List(int id, decltype(email) email) : id{id}, email{std::move(email)} {} -#endif }; auto storage = make_storage( diff --git a/tests/get_all_custom_containers.cpp b/tests/get_all_custom_containers.cpp index ec7da2e97..7bd85e72b 100644 --- a/tests/get_all_custom_containers.cpp +++ b/tests/get_all_custom_containers.cpp @@ -10,11 +10,6 @@ namespace { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; struct Comparator { diff --git a/tests/operators/binary_operators.cpp b/tests/operators/binary_operators.cpp index 0f984fe67..b31457b10 100644 --- a/tests/operators/binary_operators.cpp +++ b/tests/operators/binary_operators.cpp @@ -8,11 +8,6 @@ TEST_CASE("binary operators") { struct User { int id = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id) : id{id} {} -#endif }; auto storage = make_storage({}, make_table("users", make_column("id", &User::id))); storage.sync_schema(); diff --git a/tests/operators/bitwise_operators.cpp b/tests/operators/bitwise_operators.cpp index c65241555..d5b31900a 100644 --- a/tests/operators/bitwise_operators.cpp +++ b/tests/operators/bitwise_operators.cpp @@ -7,11 +7,6 @@ TEST_CASE("bitwise operators") { struct Entry { int lhs = 0; int rhs = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Entry() = default; - Entry(int lhs, int rhs) : lhs{lhs}, rhs{rhs} {} -#endif }; auto storage = make_storage({}, make_table("entries", make_column("lhs", &Entry::lhs), make_column("rhs", &Entry::rhs))); diff --git a/tests/operators/glob.cpp b/tests/operators/glob.cpp index 45fc3d001..eac12beec 100644 --- a/tests/operators/glob.cpp +++ b/tests/operators/glob.cpp @@ -15,12 +15,6 @@ namespace { std::string lastName; float salary = 0; int deptId = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Employee() = default; - Employee(int id, std::string firstName, std::string lastName, float salary, int deptId) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, salary{salary}, deptId{deptId} {} -#endif }; } TEST_CASE("Glob") { diff --git a/tests/operators/in.cpp b/tests/operators/in.cpp index 2997403fa..d8751430c 100644 --- a/tests/operators/in.cpp +++ b/tests/operators/in.cpp @@ -9,11 +9,6 @@ TEST_CASE("In") { struct User { int id = 0; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id) : id{id} {} -#endif - bool operator==(const User& other) const { return this->id == other.id; } diff --git a/tests/operators/is_null.cpp b/tests/operators/is_null.cpp index 473559e99..e75d4cc0e 100644 --- a/tests/operators/is_null.cpp +++ b/tests/operators/is_null.cpp @@ -7,11 +7,6 @@ TEST_CASE("Is null") { struct User { int id = 0; std::unique_ptr name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, decltype(name) name = nullptr) : id{id}, name{std::move(name)} {} -#endif }; auto storage = make_storage( "", diff --git a/tests/operators/like.cpp b/tests/operators/like.cpp index 2b01a9a7b..e921245ec 100644 --- a/tests/operators/like.cpp +++ b/tests/operators/like.cpp @@ -7,11 +7,6 @@ TEST_CASE("Like operator") { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; struct Pattern { std::string value; diff --git a/tests/operators/not_operator.cpp b/tests/operators/not_operator.cpp index 638a5784e..52ea46fe7 100644 --- a/tests/operators/not_operator.cpp +++ b/tests/operators/not_operator.cpp @@ -6,11 +6,6 @@ using namespace sqlite_orm; TEST_CASE("Not operator") { struct Object { int id = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Object() = default; - Object(int id) : id{id} {} -#endif }; auto storage = make_storage("", make_table("objects", make_column("id", &Object::id, primary_key()))); diff --git a/tests/prepared_statement_tests/prepared_common.h b/tests/prepared_statement_tests/prepared_common.h index 1d4f81927..576b9eecd 100644 --- a/tests/prepared_statement_tests/prepared_common.h +++ b/tests/prepared_statement_tests/prepared_common.h @@ -9,35 +9,18 @@ namespace PreparedStatementTests { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; struct Visit { int id = 0; decltype(User::id) userId; long time = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Visit() = default; - Visit(int id, decltype(Visit::userId) userId, long time) : id{id}, userId{userId}, time{time} {} -#endif }; struct UserAndVisit { decltype(User::id) userId; decltype(Visit::id) visitId; std::string description; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - UserAndVisit() = default; - UserAndVisit(decltype(UserAndVisit::userId) userId, - decltype(UserAndVisit::visitId) visitId, - std::string description) : userId{userId}, visitId{visitId}, description{std::move(description)} {} -#endif }; inline bool operator==(const User& lhs, const User& rhs) { diff --git a/tests/schema/index_tests.cpp b/tests/schema/index_tests.cpp index 080967656..f0a23485f 100644 --- a/tests/schema/index_tests.cpp +++ b/tests/schema/index_tests.cpp @@ -102,11 +102,6 @@ TEST_CASE("Compound index") { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); diff --git a/tests/select_constraints_tests.cpp b/tests/select_constraints_tests.cpp index 034b8869d..ee0a01c76 100644 --- a/tests/select_constraints_tests.cpp +++ b/tests/select_constraints_tests.cpp @@ -218,22 +218,12 @@ namespace { struct User1 { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User1() = default; - User1(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; struct Visit1 { int id = 0; int userId = 0; time_t time = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Visit1() = default; - Visit1(int id, int userId, time_t time) : id{id}, userId{userId}, time{time} {} -#endif }; } #if SQLITE_VERSION_NUMBER >= 3006019 @@ -270,24 +260,12 @@ namespace { std::string firstName; std::string lastName; std::string country; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User2() = default; - User2(int id, std::string firstName, std::string lastName, std::string country) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, country{country} {} -#endif }; struct Track2 { int id = 0; std::string name; long milliseconds = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Track2() = default; - Track2(int id, std::string name, long milliseconds) : - id{id}, name{std::move(name)}, milliseconds{milliseconds} {} -#endif }; } @@ -379,11 +357,6 @@ namespace { int id = 0; int age = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User3() = default; - User3(int id, int age, std::string name) : id{id}, age{age}, name{std::move(name)} {} -#endif }; } @@ -423,11 +396,6 @@ namespace { int id = 0; std::string firstName; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User4() = default; - User4(int id, std::string firstName) : id{id}, firstName{std::move(firstName)} {} -#endif - bool operator==(const User4& user) const { return this->id == user.id && this->firstName == user.firstName; } @@ -458,12 +426,6 @@ namespace { std::string firstName; std::string lastName; long registerTime = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User5() = default; - User5(int id, std::string firstName, std::string lastName, long registerTime) : - id{id}, firstName{std::move(firstName)}, lastName{std::move(lastName)}, registerTime{registerTime} {} -#endif }; } diff --git a/tests/statement_serializer_tests/statements/insert_replace.cpp b/tests/statement_serializer_tests/statements/insert_replace.cpp index 1a470b2ee..3a5b16f14 100644 --- a/tests/statement_serializer_tests/statements/insert_replace.cpp +++ b/tests/statement_serializer_tests/statements/insert_replace.cpp @@ -13,20 +13,10 @@ TEST_CASE("statement_serializer insert/replace") { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; struct UserBackup { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - UserBackup() = default; - UserBackup(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); auto table2 = diff --git a/tests/statement_serializer_tests/statements/update.cpp b/tests/statement_serializer_tests/statements/update.cpp index d76c3622d..b0e6e6a12 100644 --- a/tests/statement_serializer_tests/statements/update.cpp +++ b/tests/statement_serializer_tests/statements/update.cpp @@ -10,11 +10,6 @@ TEST_CASE("statement_serializer update") { int type = 0; int index = 0; double value = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - A() = default; - A(int address, int type, int index, double value) : address{address}, type{type}, index{index}, value{value} {} -#endif }; std::string value; decltype(value) expected; diff --git a/tests/statement_serializer_tests/table_constraints/foreign_key.cpp b/tests/statement_serializer_tests/table_constraints/foreign_key.cpp index c3bafb405..f208cd78b 100644 --- a/tests/statement_serializer_tests/table_constraints/foreign_key.cpp +++ b/tests/statement_serializer_tests/table_constraints/foreign_key.cpp @@ -287,11 +287,6 @@ TEST_CASE("statement_serializer foreign key") { SECTION("one to explicit one") { struct Object { int id = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Object() = default; - Object(int id) : id{id} {} -#endif }; struct User : Object { diff --git a/tests/storage_non_crud_tests.cpp b/tests/storage_non_crud_tests.cpp index 15ad250f9..f78c632ad 100644 --- a/tests/storage_non_crud_tests.cpp +++ b/tests/storage_non_crud_tests.cpp @@ -8,11 +8,6 @@ TEST_CASE("explicit from") { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; auto storage = make_storage( {}, @@ -78,11 +73,6 @@ TEST_CASE("update set null") { struct User { int id = 0; std::unique_ptr name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, decltype(name) name) : id{id}, name{std::move(name)} {} -#endif }; auto storage = make_storage( diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index 437eced79..ee96605db 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -454,12 +454,6 @@ TEST_CASE("insert with generated column") { double tax = 0; double netPrice = 0; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Product() = default; - Product(std::string name, double price, double discount, double tax, double netPrice) : - name{std::move(name)}, price{price}, discount{discount}, tax{tax}, netPrice{netPrice} {} -#endif - bool operator==(const Product& other) const { return this->name == other.name && this->price == other.price && this->discount == other.discount && this->tax == other.tax && this->netPrice == other.netPrice; diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index 6c0aab30f..e5f355956 100644 --- a/tests/sync_schema_tests.cpp +++ b/tests/sync_schema_tests.cpp @@ -141,12 +141,6 @@ TEST_CASE("issue521") { std::string name; std::uint32_t alpha{0}; float beta{0.0}; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - MockDatabasePoco() = default; - MockDatabasePoco(int id, std::string name, uint32_t alpha, float beta) : - id{id}, name{std::move(name)}, alpha{alpha}, beta{beta} {} -#endif }; std::vector pocosToInsert; @@ -491,11 +485,6 @@ TEST_CASE("sync_schema with generated columns") { int id = 0; int hash = 0; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, int hash = 0) : id{id}, hash{hash} {} -#endif - bool operator==(const User& other) const { return this->id == other.id && this->hash == other.hash; } diff --git a/tests/tests.cpp b/tests/tests.cpp index 4b354657f..06e21b6d3 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -1,6 +1,14 @@ #include #include +#ifdef _DEBUG +#pragma comment(lib, "Catch2d.Lib") +#pragma comment(lib, "manual-link/Catch2Maind.Lib") +#else +#pragma comment(lib, "Catch2.Lib") +#pragma comment(lib, "manual-link/Catch2Main.Lib") +#endif + #include // std::vector #include // std::string #include // std::remove diff --git a/tests/tests4.cpp b/tests/tests4.cpp index f2626497e..50e855b96 100644 --- a/tests/tests4.cpp +++ b/tests/tests4.cpp @@ -74,22 +74,12 @@ TEST_CASE("join") { struct User { int id = 0; std::string name; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif }; struct Visit { int id = 0; int userId = 0; time_t date = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Visit() = default; - Visit(int id, int userId, time_t date) : id{id}, userId{userId}, date{date} {} -#endif }; auto storage = diff --git a/tests/transaction_tests.cpp b/tests/transaction_tests.cpp index a23fdba03..a11b042cd 100644 --- a/tests/transaction_tests.cpp +++ b/tests/transaction_tests.cpp @@ -9,11 +9,6 @@ namespace { int id = 0; std::string name; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Object() = default; - Object(int id, std::string name) : id{id}, name{std::move(name)} {} -#endif - bool operator==(const Object& other) const { return this->id == other.id && this->name == other.name; } diff --git a/tests/trigger_tests.cpp b/tests/trigger_tests.cpp index aad420adc..adbaa676f 100644 --- a/tests/trigger_tests.cpp +++ b/tests/trigger_tests.cpp @@ -9,33 +9,18 @@ TEST_CASE("triggers_basics") { std::string text; int x = 0; int y = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - TestInsert() = default; - TestInsert(int id, std::string text, int x, int y) : id{id}, text{std::move(text)}, x{x}, y{y} {} -#endif }; struct TestUpdate { int id; std::string text; int x = 0; int y = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - TestUpdate() = default; - TestUpdate(int id, std::string text, int x, int y) : id{id}, text{std::move(text)}, x{x}, y{y} {} -#endif }; struct TestDelete { int id; std::string text; int x = 0; int y = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - TestDelete() = default; - TestDelete(int id, std::string text, int x, int y) : id{id}, text{std::move(text)}, x{x}, y{y} {} -#endif }; TestInsert test_insert{4, "test", 1, 2}; diff --git a/tests/unique_cases/get_all_with_two_tables.cpp b/tests/unique_cases/get_all_with_two_tables.cpp index ddf7d6ad2..b5e69494e 100644 --- a/tests/unique_cases/get_all_with_two_tables.cpp +++ b/tests/unique_cases/get_all_with_two_tables.cpp @@ -15,11 +15,6 @@ namespace { struct Item { int id = 0; std::string attributes; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - Item() = default; - Item(int id, std::string attributes) : id{id}, attributes{std::move(attributes)} {} -#endif }; inline bool operator==(const Item& lhs, const Item& rhs) { diff --git a/tests/unique_cases/prepare_get_all_with_case.cpp b/tests/unique_cases/prepare_get_all_with_case.cpp index 60d21dcb4..e0657fcfb 100644 --- a/tests/unique_cases/prepare_get_all_with_case.cpp +++ b/tests/unique_cases/prepare_get_all_with_case.cpp @@ -7,11 +7,6 @@ TEST_CASE("Prepare with case") { struct UserProfile { int id = 0; std::string firstName; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - UserProfile() = default; - UserProfile(int id, std::string firstName) : id{id}, firstName{std::move(firstName)} {} -#endif }; auto storage = make_storage({}, diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index 45548f607..b75dab081 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -295,11 +295,6 @@ TEST_CASE("custom functions") { } struct User { int id = 0; - -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - User() = default; - User(int id) : id{id} {} -#endif }; auto storage = make_storage(path, make_table("users", make_column("id", &User::id))); storage.sync_schema(); From 3f91f93603d20552c24b7df9e52a8e00f3fb5a80 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 13:50:58 +0200 Subject: [PATCH 042/170] Renamed feature test macro for range-based `for` --- dev/cte_column_names_collector.h | 2 +- dev/functional/cxx_core_features.h | 2 +- dev/implementations/table_definitions.h | 2 +- dev/serializing_util.h | 2 +- dev/statement_serializer.h | 2 +- include/sqlite_orm/sqlite_orm.h | 10 +++++----- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dev/cte_column_names_collector.h b/dev/cte_column_names_collector.h index dbc1c5514..9ffd78b26 100644 --- a/dev/cte_column_names_collector.h +++ b/dev/cte_column_names_collector.h @@ -195,7 +195,7 @@ namespace sqlite_orm { // 3. fill in blanks with numerical column identifiers { -#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED for (size_t n = 1; std::string & name: columnNames) { if (name.empty()) { name = std::to_string(n); diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index be69e699f..864d89ba3 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -96,5 +96,5 @@ #if __cplusplus >= 202002L #define SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED -#define SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED +#define SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED #endif diff --git a/dev/implementations/table_definitions.h b/dev/implementations/table_definitions.h index 26d9282b8..15c889356 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -43,7 +43,7 @@ namespace sqlite_orm { column.template is()); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); -#if defined(SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) +#if defined(SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) for (int n = 1; const std::string& columnName: compositeKeyColumnNames) { if (auto it = std::ranges::find(res, columnName, &table_xinfo::name); it != res.end()) { it->pk = n; diff --git a/dev/serializing_util.h b/dev/serializing_util.h index b505c557d..8dbc6ac62 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -237,7 +237,7 @@ namespace sqlite_orm { const auto& strings = std::get<1>(tpl); static constexpr std::array sep = {", ", ""}; -#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED for (bool first = true; auto& s: strings) { ss << sep[std::exchange(first, false)] << s; } diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 42bdbcb3d..e906bd593 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1689,7 +1689,7 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::table_has_no_primary_key_column}; } -#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED static constexpr std::array sep = {" AND ", ""}; for (bool first = true; const std::string& pkName: primaryKeyColumnNames) { ss << sep[std::exchange(first, false)] << streaming_identifier(pkName) << " = ?"; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 93bccd1a4..795a57e70 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -127,7 +127,7 @@ using std::nullptr_t; #if __cplusplus >= 202002L #define SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED -#define SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED +#define SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED #endif // #include "cxx_compiler_quirks.h" @@ -16325,7 +16325,7 @@ namespace sqlite_orm { const auto& strings = std::get<1>(tpl); static constexpr std::array sep = {", ", ""}; -#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED for (bool first = true; auto& s: strings) { ss << sep[std::exchange(first, false)] << s; } @@ -19042,7 +19042,7 @@ namespace sqlite_orm { // 3. fill in blanks with numerical column identifiers { -#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED for (size_t n = 1; std::string & name: columnNames) { if (name.empty()) { name = std::to_string(n); @@ -21088,7 +21088,7 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::table_has_no_primary_key_column}; } -#ifdef SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED static constexpr std::array sep = {" AND ", ""}; for (bool first = true; const std::string& pkName: primaryKeyColumnNames) { ss << sep[std::exchange(first, false)] << streaming_identifier(pkName) << " = ?"; @@ -23988,7 +23988,7 @@ namespace sqlite_orm { column.template is()); }); auto compositeKeyColumnNames = this->composite_key_columns_names(); -#if defined(SQLITE_ORM_INIT_RANGE_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) +#if defined(SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_CPP20_RANGES_SUPPORTED) for (int n = 1; const std::string& columnName: compositeKeyColumnNames) { if (auto it = std::ranges::find(res, columnName, &table_xinfo::name); it != res.end()) { it->pk = n; From 1dfa9b361cb0eddb2a1c893f775f8c1b109ce39c Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 14:21:56 +0200 Subject: [PATCH 043/170] Initialized members of mapped objects in unit tests --- include/sqlite_orm/sqlite_orm.h | 23 +++++++++++------- tests/backup_tests.cpp | 22 ++++++++--------- tests/iterate.cpp | 4 ++-- tests/operators/in.cpp | 2 +- .../insert_explicit.cpp | 2 +- .../prepared_common.h | 6 ++--- tests/row_extractor.cpp | 8 +++---- tests/select_constraints_tests.cpp | 24 +++++++++---------- tests/storage_non_crud_tests.cpp | 8 +++---- tests/sync_schema_tests.cpp | 19 ++++++++------- tests/trigger_tests.cpp | 6 ++--- tests/unique_cases/issue663.cpp | 4 ++-- 12 files changed, 68 insertions(+), 60 deletions(-) diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index be0a22df1..68d33bc30 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -29,6 +29,20 @@ __pragma(push_macro("max")) // actually it should be available when including stddef.h using std::nullptr_t; +// #include "cxx_check_prerequisites.h" + +/* + * This header detects missing core C++ language features on which sqlite_orm depends, bailing out with a hard error. + */ + +#if __cpp_aggregate_nsdmi < 201304L +#error A C++14 conforming compiler is required +#endif + +#if __cpp_constexpr < 201304L +#error A C++14 conforming compiler is required +#endif + // #include "cxx_core_features.h" /* @@ -48,14 +62,6 @@ using std::nullptr_t; #define SQLITE_ORM_HAS_INCLUDE(file) 0L #endif -#if __cpp_aggregate_nsdmi < 201304L -#error A C++14 conforming compiler is required -#endif - -#if __cpp_constexpr < 201304L -#error A C++14 conforming compiler is required -#endif - #if __cpp_noexcept_function_type >= 201510L #define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED #endif @@ -11693,6 +11699,7 @@ namespace sqlite_orm { // #include "table_info.h" #include // std::string +#include // std::move namespace sqlite_orm { diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index 09574ea40..490a7aeaf 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -15,7 +15,7 @@ namespace { } struct MarvelHero { - int id; + int id = 0; std::string name; std::string abilities; }; @@ -133,16 +133,16 @@ TEST_CASE("Backup crash") { storage.remove_all(); // --- Insert values - storage.insert(MarvelHero{-1, "Tony Stark", "Iron man, playboy, billionaire, philanthropist"}); - storage.insert(MarvelHero{-1, "Thor", "Storm god"}); - storage.insert(MarvelHero{-1, "Vision", "Min Stone"}); - storage.insert(MarvelHero{-1, "Captain America", "Vibranium shield"}); - storage.insert(MarvelHero{-1, "Hulk", "Strength"}); - storage.insert(MarvelHero{-1, "Star Lord", "Humor"}); - storage.insert(MarvelHero{-1, "Peter Parker", "Spiderman"}); - storage.insert(MarvelHero{-1, "Clint Barton", "Hawkeye"}); - storage.insert(MarvelHero{-1, "Natasha Romanoff", "Black widow"}); - storage.insert(MarvelHero{-1, "Groot", "I am Groot!"}); + storage.insert(MarvelHero{0, "Tony Stark", "Iron man, playboy, billionaire, philanthropist"}); + storage.insert(MarvelHero{0, "Thor", "Storm god"}); + storage.insert(MarvelHero{0, "Vision", "Min Stone"}); + storage.insert(MarvelHero{0, "Captain America", "Vibranium shield"}); + storage.insert(MarvelHero{0, "Hulk", "Strength"}); + storage.insert(MarvelHero{0, "Star Lord", "Humor"}); + storage.insert(MarvelHero{0, "Peter Parker", "Spiderman"}); + storage.insert(MarvelHero{0, "Clint Barton", "Hawkeye"}); + storage.insert(MarvelHero{0, "Natasha Romanoff", "Black widow"}); + storage.insert(MarvelHero{0, "Groot", "I am Groot!"}); REQUIRE(storage.count() == 10); // --- Create backup file name and verify that the file does not exist diff --git a/tests/iterate.cpp b/tests/iterate.cpp index 8e70e4e56..9c62a300e 100644 --- a/tests/iterate.cpp +++ b/tests/iterate.cpp @@ -6,7 +6,7 @@ using namespace sqlite_orm; TEST_CASE("Iterate mapped") { struct Test { - int64_t id; + int64_t id = 0; std::vector key; bool operator==(const Test& rhs) const { @@ -63,7 +63,7 @@ TEST_CASE("Iterate mapped") { #if defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) TEST_CASE("Iterate select statement") { struct Test { - int64_t id; + int64_t id = 0; std::vector key; bool operator==(const Test&) const = default; diff --git a/tests/operators/in.cpp b/tests/operators/in.cpp index d8751430c..0f8d133fe 100644 --- a/tests/operators/in.cpp +++ b/tests/operators/in.cpp @@ -82,7 +82,7 @@ TEST_CASE("In") { } { struct Letter { - int id; + int id = 0; std::string name; }; auto storage = make_storage( diff --git a/tests/prepared_statement_tests/insert_explicit.cpp b/tests/prepared_statement_tests/insert_explicit.cpp index 847e1594a..b4056e642 100644 --- a/tests/prepared_statement_tests/insert_explicit.cpp +++ b/tests/prepared_statement_tests/insert_explicit.cpp @@ -102,7 +102,7 @@ TEST_CASE("Prepared insert explicit") { REQUIRE(insertedVisit.time == defaultVisitTime); } { - Visit visit{-1, 2, defaultVisitTime + 2}; + Visit visit{0, 2, defaultVisitTime + 2}; auto statement = storage.prepare(insert(visit, columns(&Visit::userId))); auto insertedId = storage.execute(statement); REQUIRE(insertedId == 3); diff --git a/tests/prepared_statement_tests/prepared_common.h b/tests/prepared_statement_tests/prepared_common.h index 576b9eecd..e69830347 100644 --- a/tests/prepared_statement_tests/prepared_common.h +++ b/tests/prepared_statement_tests/prepared_common.h @@ -13,13 +13,13 @@ namespace PreparedStatementTests { struct Visit { int id = 0; - decltype(User::id) userId; + decltype(User::id) userId = 0; long time = 0; }; struct UserAndVisit { - decltype(User::id) userId; - decltype(Visit::id) visitId; + decltype(User::id) userId = 0; + decltype(Visit::id) visitId = 0; std::string description; }; diff --git a/tests/row_extractor.cpp b/tests/row_extractor.cpp index 95d3bda7b..b62d906ae 100644 --- a/tests/row_extractor.cpp +++ b/tests/row_extractor.cpp @@ -12,7 +12,7 @@ enum class Gender { }; struct SuperHero { - int id; + int id = 0; std::string name; Gender gender = Gender::Invalid; }; @@ -132,9 +132,9 @@ TEST_CASE("Custom row extractors") { storage.sync_schema(); storage.transaction([&storage]() { - storage.insert(SuperHero{-1, "Batman", Gender::Male}); - storage.insert(SuperHero{-1, "Wonder woman", Gender::Female}); - storage.insert(SuperHero{-1, "Superman", Gender::Male}); + storage.insert(SuperHero{0, "Batman", Gender::Male}); + storage.insert(SuperHero{0, "Wonder woman", Gender::Female}); + storage.insert(SuperHero{0, "Superman", Gender::Male}); return true; }); diff --git a/tests/select_constraints_tests.cpp b/tests/select_constraints_tests.cpp index ee0a01c76..64cee2665 100644 --- a/tests/select_constraints_tests.cpp +++ b/tests/select_constraints_tests.cpp @@ -5,9 +5,9 @@ using namespace sqlite_orm; namespace { struct Employee { - int id; + int id = 0; std::string name; - int age; + int age = 0; std::string address; // optional double salary; // optional @@ -30,13 +30,13 @@ TEST_CASE("select constraints") { storage.sync_schema(); // create employees.. - Employee paul{-1, "Paul", 32, "California", 20000.0}; - Employee allen{-1, "Allen", 25, "Texas", 15000.0}; - Employee teddy{-1, "Teddy", 23, "Norway", 20000.0}; - Employee mark{-1, "Mark", 25, "Rich-Mond", 65000.0}; - Employee david{-1, "David", 27, "Texas", 85000.0}; - Employee kim{-1, "Kim", 22, "South-Hall", 45000.0}; - Employee james{-1, "James", 24, "Houston", 10000.0}; + Employee paul{0, "Paul", 32, "California", 20000.0}; + Employee allen{0, "Allen", 25, "Texas", 15000.0}; + Employee teddy{0, "Teddy", 23, "Norway", 20000.0}; + Employee mark{0, "Mark", 25, "Rich-Mond", 65000.0}; + Employee david{0, "David", 27, "Texas", 85000.0}; + Employee kim{0, "Kim", 22, "South-Hall", 45000.0}; + Employee james{0, "James", 24, "Houston", 10000.0}; // insert employees. `insert` function returns id of inserted object.. paul.id = storage.insert(paul); @@ -69,9 +69,9 @@ TEST_CASE("select constraints") { } } SECTION("distinct") { - storage.insert(Employee{-1, "Paul", 24, "Houston", 20000.0}); - storage.insert(Employee{-1, "James", 44, "Norway", 5000.0}); - storage.insert(Employee{-1, "James", 45, "Texas", 5000.0}); + storage.insert(Employee{0, "Paul", 24, "Houston", 20000.0}); + storage.insert(Employee{0, "James", 44, "Norway", 5000.0}); + storage.insert(Employee{0, "James", 45, "Texas", 5000.0}); std::vector names; decltype(names) expected; diff --git a/tests/storage_non_crud_tests.cpp b/tests/storage_non_crud_tests.cpp index f78c632ad..54f5e5dd2 100644 --- a/tests/storage_non_crud_tests.cpp +++ b/tests/storage_non_crud_tests.cpp @@ -113,7 +113,7 @@ TEST_CASE("update set null") { TEST_CASE("InsertRange") { struct Object { - int id; + int id = 0; std::string name; #ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED @@ -123,7 +123,7 @@ TEST_CASE("InsertRange") { }; struct ObjectWithoutRowid { - int id; + int id = 0; std::string name; #ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED @@ -377,7 +377,7 @@ TEST_CASE("Select") { TEST_CASE("Replace query") { struct Object { - int id; + int id = 0; std::string name; #ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED @@ -483,7 +483,7 @@ TEST_CASE("Replace query") { TEST_CASE("Remove all") { struct Object { - int id; + int id = 0; std::string name; }; diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index e5f355956..aea631f0d 100644 --- a/tests/sync_schema_tests.cpp +++ b/tests/sync_schema_tests.cpp @@ -16,7 +16,7 @@ TEST_CASE("Sync schema") { // this is an old version of user.. struct UserBefore { - int id; + int id = 0; std::string name; std::unique_ptr categoryId; std::unique_ptr surname; @@ -24,7 +24,7 @@ TEST_CASE("Sync schema") { // this is a new version of user struct UserAfter { - int id; + int id = 0; std::string name; bool operator==(const UserBefore& userBefore) const { @@ -117,12 +117,12 @@ TEST_CASE("Sync schema") { TEST_CASE("issue854") { struct Base { std::string name; - int64_t timestamp; - int64_t value; + int64_t timestamp = 0; + int64_t value = 0; }; struct A : public Base { - int64_t id; + int64_t id = 0; }; auto storage = make_storage({}, make_table("entries", @@ -137,7 +137,7 @@ TEST_CASE("issue521") { auto storagePath = "issue521.sqlite"; struct MockDatabasePoco { - int id{-1}; + int id = 0; std::string name; std::uint32_t alpha{0}; float beta{0.0}; @@ -161,8 +161,8 @@ TEST_CASE("issue521") { // --- Insert two rows pocosToInsert.clear(); - pocosToInsert.push_back({-1, "Michael", 10, 10.10f}); - pocosToInsert.push_back({-1, "Joyce", 20, 20.20f}); + pocosToInsert.push_back({0, "Michael", 10, 10.10f}); + pocosToInsert.push_back({0, "Joyce", 20, 20.20f}); for (auto& poco: pocosToInsert) { auto insertedId = storage.insert(poco); @@ -470,7 +470,7 @@ TEST_CASE("sync_schema") { TEST_CASE("sync_schema_simulate") { struct Cols { - int Col1; + int Col1 = 0; }; auto storage = @@ -479,6 +479,7 @@ TEST_CASE("sync_schema_simulate") { storage.sync_schema(); storage.sync_schema_simulate(); } + #if SQLITE_VERSION_NUMBER >= 3031000 TEST_CASE("sync_schema with generated columns") { struct User { diff --git a/tests/trigger_tests.cpp b/tests/trigger_tests.cpp index adbaa676f..f930f4307 100644 --- a/tests/trigger_tests.cpp +++ b/tests/trigger_tests.cpp @@ -5,19 +5,19 @@ using namespace sqlite_orm; TEST_CASE("triggers_basics") { struct TestInsert { - int id; + int id = 0; std::string text; int x = 0; int y = 0; }; struct TestUpdate { - int id; + int id = 0; std::string text; int x = 0; int y = 0; }; struct TestDelete { - int id; + int id = 0; std::string text; int x = 0; int y = 0; diff --git a/tests/unique_cases/issue663.cpp b/tests/unique_cases/issue663.cpp index 1a2343cdd..cf2646659 100644 --- a/tests/unique_cases/issue663.cpp +++ b/tests/unique_cases/issue663.cpp @@ -71,9 +71,9 @@ namespace { bool operator!=(const User1& rhs) const { return !(rhs == *this); } - int id; + int id = 0; std::string name; - int age; + int age = 0; std::string email; }; } From e60c92368a0d987ad4cb99c36075b36209c128a7 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 17:54:40 +0200 Subject: [PATCH 044/170] Runner-up: Used qualified `std::remove()` Using named modules the namespace scope will be much stricter, so adhere to it. --- examples/any.cpp | 4 ++-- examples/chrono_binding.cpp | 6 +++--- tests/backup_tests.cpp | 2 +- tests/constraints/default.cpp | 3 ++- tests/sync_schema_tests.cpp | 9 +++++---- tests/transaction_tests.cpp | 7 ++++--- tests/unique_cases/issue937.cpp | 3 ++- tests/user_defined_functions.cpp | 3 ++- 8 files changed, 21 insertions(+), 16 deletions(-) diff --git a/examples/any.cpp b/examples/any.cpp index 8f7815c39..01843a50e 100644 --- a/examples/any.cpp +++ b/examples/any.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include // std::remove using namespace sqlite_orm; using std::cout; @@ -135,7 +135,7 @@ int main() { std::any value; }; auto filename = "any.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage( filename, make_table("test", make_column("id", &Value::id, primary_key()), make_column("value", &Value::value))); diff --git a/examples/chrono_binding.cpp b/examples/chrono_binding.cpp index 8307b18a9..1adb2c7d0 100644 --- a/examples/chrono_binding.cpp +++ b/examples/chrono_binding.cpp @@ -11,6 +11,7 @@ #ifdef ENABLE_THIS_EXAMPLE #include +#include // std::remove #include #include #include @@ -30,8 +31,7 @@ // also we need transform functions to make string from enum.. static std::string sysDaysToString(std::chrono::sys_days pt) { - auto r = std::format("{:%F}", pt); - return r; + return std::format("{:%F}", pt); } /** @@ -149,7 +149,7 @@ struct Person { int main(int, char**) { #ifdef ENABLE_THIS_EXAMPLE const std::string db_name = "sys_days.sqlite"; - ::remove(db_name.c_str()); + std::remove(db_name.c_str()); auto storage = make_storage(db_name, make_table("Persons", diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index 490a7aeaf..72be5ea64 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -82,7 +82,7 @@ TEST_CASE("backup") { REQUIRE_THAT(rowsFromBackup, UnorderedEquals(expectedRows)); } SECTION("from") { - ::remove(backupFilename.c_str()); + std::remove(backupFilename.c_str()); auto storage = makeStorage(backupFilename); storage.sync_schema(); storage.replace(User{1, "Sharon"}); diff --git a/tests/constraints/default.cpp b/tests/constraints/default.cpp index d35c3347c..14979883d 100644 --- a/tests/constraints/default.cpp +++ b/tests/constraints/default.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove using namespace sqlite_orm; @@ -14,7 +15,7 @@ TEST_CASE("Default value") { auto filename = "test_db.sqlite"; - ::remove(filename); + std::remove(filename); auto storage1 = make_storage(filename, make_table("User", diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index aea631f0d..ebe875f79 100644 --- a/tests/sync_schema_tests.cpp +++ b/tests/sync_schema_tests.cpp @@ -1,4 +1,5 @@ #include +#include // std::remove #include #include @@ -34,7 +35,7 @@ TEST_CASE("Sync schema") { // create an old storage auto filename = "sync_schema_text.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename, make_table("users", make_column("id", &UserBefore::id, primary_key()), @@ -144,7 +145,7 @@ TEST_CASE("issue521") { }; std::vector pocosToInsert; - ::remove(storagePath); + std::remove(storagePath); { // --- Create the initial database auto storage = sqlite_orm::make_storage( @@ -304,7 +305,7 @@ TEST_CASE("sync_schema") { const std::string name = "name"; const std::string age = "age"; } columnNames; - ::remove(storagePath); + std::remove(storagePath); { auto storage = make_storage(storagePath, make_table(tableName, @@ -491,7 +492,7 @@ TEST_CASE("sync_schema with generated columns") { } }; auto storagePath = "sync_schema_with_generated.sqlite"; - ::remove(storagePath); + std::remove(storagePath); auto storage1 = make_storage(storagePath, make_table("users", make_column("id", &User::id))); storage1.sync_schema(); storage1.insert(User{5}); diff --git a/tests/transaction_tests.cpp b/tests/transaction_tests.cpp index a11b042cd..1b37606b9 100644 --- a/tests/transaction_tests.cpp +++ b/tests/transaction_tests.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove #include "catch_matchers.h" using namespace sqlite_orm; @@ -17,7 +18,7 @@ namespace { TEST_CASE("transaction") { auto filename = "transaction_test.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage( "test_transaction_guard.sqlite", make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name))); @@ -59,7 +60,7 @@ TEST_CASE("begin_transaction") { } TEST_CASE("Transaction guard") { - ::remove("guard.sqlite"); + std::remove("guard.sqlite"); auto table = make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name)); auto storage = make_storage("guard.sqlite", table); @@ -337,5 +338,5 @@ TEST_CASE("Transaction guard") { guard->commit_on_destroy = true; REQUIRE_THROWS_WITH(guard->~transaction_guard_t(), ContainsSubstring("database is locked")); } - ::remove("guard.sqlite"); + std::remove("guard.sqlite"); } diff --git a/tests/unique_cases/issue937.cpp b/tests/unique_cases/issue937.cpp index b0baf876a..19f7370a0 100644 --- a/tests/unique_cases/issue937.cpp +++ b/tests/unique_cases/issue937.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove #if SQLITE_VERSION_NUMBER >= 3006019 using namespace sqlite_orm; @@ -32,7 +33,7 @@ TEST_CASE("issue937") { using namespace sqlite_orm; - ::remove("SQLCookbook.sqlite"); + std::remove("SQLCookbook.sqlite"); auto storage = make_storage("SQLCookbook.sqlite", make_table("Emp", make_column("empno", &Employee::m_empno, primary_key().autoincrement()), diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index b75dab081..20e34653d 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove #include "catch_matchers.h" using namespace sqlite_orm; @@ -291,7 +292,7 @@ TEST_CASE("custom functions") { } SECTION("file") { path = "custom_function.sqlite"; - ::remove(path.c_str()); + std::remove(path.c_str()); } struct User { int id = 0; From 051203f4a3c7068fd1b52a970fc52e93c9d34c09 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 18:32:37 +0200 Subject: [PATCH 045/170] Correctly observe availability of `std::any` `std::any` is only available starting with a C++17 library. --- examples/any.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/examples/any.cpp b/examples/any.cpp index 01843a50e..4d6f30bb0 100644 --- a/examples/any.cpp +++ b/examples/any.cpp @@ -14,7 +14,17 @@ * BLOB is mapped to std::vector. */ #include +#ifdef __has_include +#if __has_include() #include +#endif +#endif + +#if __cpp_lib_any >= 201606L +#define ENABLE_THIS_EXAMPLE +#endif + +#ifdef ENABLE_THIS_EXAMPLE #include #include // std::remove @@ -128,8 +138,10 @@ namespace sqlite_orm { } }; } +#endif int main() { +#ifdef ENABLE_THIS_EXAMPLE struct Value { int id = 0; std::any value; @@ -151,5 +163,6 @@ int main() { cout << storage.dump(test) << endl; } cout << endl; +#endif return 0; } From ec30c3d48668c4fcfce7280dc8dde84445043f5a Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 18:37:25 +0200 Subject: [PATCH 046/170] Replaced #include of `` with `` Macros will not be available when importing the Standard Library as a named module. The recommended and only fallback is to include the C header file for `assert()`. --- dev/udf_proxy.h | 28 ++++++++++++++-------------- examples/blob.cpp | 2 +- examples/chrono_binding.cpp | 2 +- examples/column_aliases.cpp | 1 - examples/core_functions.cpp | 2 +- examples/custom_aliases.cpp | 2 +- examples/enum_binding.cpp | 2 +- examples/foreign_key.cpp | 2 +- examples/in_memory.cpp | 2 +- examples/private_class_members.cpp | 2 +- examples/self_join.cpp | 2 +- examples/union.cpp | 2 +- include/sqlite_orm/sqlite_orm.h | 2 +- 13 files changed, 25 insertions(+), 26 deletions(-) diff --git a/dev/udf_proxy.h b/dev/udf_proxy.h index bd14a5023..2d49a84b1 100644 --- a/dev/udf_proxy.h +++ b/dev/udf_proxy.h @@ -1,7 +1,7 @@ #pragma once #include -#include // assert macro +#include // assert macro #include // std::true_type, std::false_type #include // std::bad_alloc #include // std::allocator, std::allocator_traits, std::unique_ptr @@ -13,9 +13,9 @@ namespace sqlite_orm { namespace internal { - /* - * Returns properly allocated memory space for the specified application-defined function object - * paired with an accompanying deallocation function. + /* + * Returns properly allocated memory space for the specified application-defined function object + * paired with an accompanying deallocation function. */ template std::pair preallocate_udf_memory() { @@ -31,8 +31,8 @@ namespace sqlite_orm { return {traits::allocate(allocator, 1), deallocate}; } - /* - * Returns a pair of functions to allocate/deallocate properly aligned memory space for the specified application-defined function object. + /* + * Returns a pair of functions to allocate/deallocate properly aligned memory space for the specified application-defined function object. */ template std::pair obtain_udf_allocator() { @@ -51,8 +51,8 @@ namespace sqlite_orm { return {allocate, deallocate}; } - /* - * A deleter that only destroys the application-defined function object. + /* + * A deleter that only destroys the application-defined function object. */ struct udf_destruct_only_deleter { template @@ -63,12 +63,12 @@ namespace sqlite_orm { } }; - /* - * Stores type-erased information in relation to an application-defined scalar or aggregate function object: - * - name and argument count - * - function dispatch (step, final) - * - either preallocated memory with a possibly a priori constructed function object [scalar], - * - or memory allocation/deallocation functions [aggregate] + /* + * Stores type-erased information in relation to an application-defined scalar or aggregate function object: + * - name and argument count + * - function dispatch (step, final) + * - either preallocated memory with a possibly a priori constructed function object [scalar], + * - or memory allocation/deallocation functions [aggregate] */ struct udf_proxy { using sqlite_func_t = void (*)(sqlite3_context* context, int argsCount, sqlite3_value** values); diff --git a/examples/blob.cpp b/examples/blob.cpp index 279ef842d..3ad94cf6d 100644 --- a/examples/blob.cpp +++ b/examples/blob.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include using std::cout; using std::endl; diff --git a/examples/chrono_binding.cpp b/examples/chrono_binding.cpp index 1adb2c7d0..f5c14ac97 100644 --- a/examples/chrono_binding.cpp +++ b/examples/chrono_binding.cpp @@ -10,7 +10,7 @@ #endif #ifdef ENABLE_THIS_EXAMPLE -#include +#include #include // std::remove #include #include diff --git a/examples/column_aliases.cpp b/examples/column_aliases.cpp index 0068586d3..4c6f43f98 100644 --- a/examples/column_aliases.cpp +++ b/examples/column_aliases.cpp @@ -1,6 +1,5 @@ #include #include -#include using std::cout; using std::endl; diff --git a/examples/core_functions.cpp b/examples/core_functions.cpp index b3edb2d9c..162a4fe98 100644 --- a/examples/core_functions.cpp +++ b/examples/core_functions.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include using std::cout; using std::endl; diff --git a/examples/custom_aliases.cpp b/examples/custom_aliases.cpp index 931f54589..e44797e7a 100644 --- a/examples/custom_aliases.cpp +++ b/examples/custom_aliases.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include #include diff --git a/examples/enum_binding.cpp b/examples/enum_binding.cpp index 70984f9a4..14bfbc380 100644 --- a/examples/enum_binding.cpp +++ b/examples/enum_binding.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include using std::cout; using std::endl; diff --git a/examples/foreign_key.cpp b/examples/foreign_key.cpp index ec7c92e9e..24f230499 100644 --- a/examples/foreign_key.cpp +++ b/examples/foreign_key.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #if SQLITE_VERSION_NUMBER >= 3006019 diff --git a/examples/in_memory.cpp b/examples/in_memory.cpp index 5799f967a..f10ccb3f4 100644 --- a/examples/in_memory.cpp +++ b/examples/in_memory.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include using namespace sqlite_orm; diff --git a/examples/private_class_members.cpp b/examples/private_class_members.cpp index 73d10a4ad..a071c4378 100644 --- a/examples/private_class_members.cpp +++ b/examples/private_class_members.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include using std::cout; using std::endl; diff --git a/examples/self_join.cpp b/examples/self_join.cpp index 8d103a9df..3ea98c1ec 100644 --- a/examples/self_join.cpp +++ b/examples/self_join.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #if SQLITE_VERSION_NUMBER >= 3006019 #define ENABLE_THIS_EXAMPLE diff --git a/examples/union.cpp b/examples/union.cpp index 68b229917..2bc0d4a61 100644 --- a/examples/union.cpp +++ b/examples/union.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 68d33bc30..8b70e78a7 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -17181,7 +17181,7 @@ namespace sqlite_orm { // #include "udf_proxy.h" #include -#include // assert macro +#include // assert macro #include // std::true_type, std::false_type #include // std::bad_alloc #include // std::allocator, std::allocator_traits, std::unique_ptr From 3f8619b306ae4e6f634f162cb33e625c13bac95d Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 18:40:43 +0200 Subject: [PATCH 047/170] Removed code used in local test environment --- tests/tests.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/tests.cpp b/tests/tests.cpp index 06e21b6d3..4b354657f 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -1,14 +1,6 @@ #include #include -#ifdef _DEBUG -#pragma comment(lib, "Catch2d.Lib") -#pragma comment(lib, "manual-link/Catch2Maind.Lib") -#else -#pragma comment(lib, "Catch2.Lib") -#pragma comment(lib, "manual-link/Catch2Main.Lib") -#endif - #include // std::vector #include // std::string #include // std::remove From fb0098c82095216908297f71d1e3f5f21a5c0c27 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 18:44:18 +0200 Subject: [PATCH 048/170] Used C++20 default comparisons where possible --- dev/arg_values.h | 2 ++ include/sqlite_orm/sqlite_orm.h | 2 ++ tests/backup_tests.cpp | 12 +++++++---- tests/iterate.cpp | 4 ++++ tests/operators/in.cpp | 4 ++++ .../prepared_common.h | 20 +++++++++++-------- tests/prepared_statement_tests/select.cpp | 6 ++++++ tests/schema/virtual_table.cpp | 4 ++++ tests/select_constraints_tests.cpp | 8 ++++++++ tests/storage_tests.cpp | 4 ++++ tests/sync_schema_tests.cpp | 8 ++++++-- tests/transaction_tests.cpp | 4 ++++ .../unique_cases/get_all_with_two_tables.cpp | 12 +++++++---- tests/unique_cases/issue663.cpp | 14 +++++++------ 14 files changed, 80 insertions(+), 24 deletions(-) diff --git a/dev/arg_values.h b/dev/arg_values.h index 0e8260790..ab10d1ec5 100644 --- a/dev/arg_values.h +++ b/dev/arg_values.h @@ -98,9 +98,11 @@ namespace sqlite_orm { return &other.container == &this->container && other.index == this->index; } +#ifndef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED bool operator!=(const iterator& other) const { return !(*this == other); } +#endif private: const arg_values& container; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 8b70e78a7..c4fccd4c0 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -17092,9 +17092,11 @@ namespace sqlite_orm { return &other.container == &this->container && other.index == this->index; } +#ifndef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED bool operator!=(const iterator& other) const { return !(*this == other); } +#endif private: const arg_values& container; diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index 72be5ea64..dc8215c66 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -8,11 +8,15 @@ namespace { struct User { int id = 0; std::string name; - }; - bool operator==(const User& lhs, const User& rhs) { - return lhs.id == rhs.id && lhs.name == rhs.name; - } +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator==(const User&, const User&) = default; +#else + friend bool operator==(const User& lhs, const User& rhs) { + return lhs.id == rhs.id && lhs.name == rhs.name; + } +#endif + }; struct MarvelHero { int id = 0; diff --git a/tests/iterate.cpp b/tests/iterate.cpp index 9c62a300e..5b4521006 100644 --- a/tests/iterate.cpp +++ b/tests/iterate.cpp @@ -9,9 +9,13 @@ TEST_CASE("Iterate mapped") { int64_t id = 0; std::vector key; +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Test&) const = default; +#else bool operator==(const Test& rhs) const { return this->id == rhs.id && this->key == rhs.key; } +#endif }; auto db = diff --git a/tests/operators/in.cpp b/tests/operators/in.cpp index 0f8d133fe..bf5931119 100644 --- a/tests/operators/in.cpp +++ b/tests/operators/in.cpp @@ -9,9 +9,13 @@ TEST_CASE("In") { struct User { int id = 0; +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User& other) const = default; +#else bool operator==(const User& other) const { return this->id == other.id; } +#endif std::ostream& operator<<(std::ostream& os) const { return os << this->id; diff --git a/tests/prepared_statement_tests/prepared_common.h b/tests/prepared_statement_tests/prepared_common.h index e69830347..8803d5ec8 100644 --- a/tests/prepared_statement_tests/prepared_common.h +++ b/tests/prepared_statement_tests/prepared_common.h @@ -9,6 +9,18 @@ namespace PreparedStatementTests { struct User { int id = 0; std::string name; + +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator==(const User&, const User&) = default; +#else + friend bool operator==(const User& lhs, const User& rhs) { + return lhs.id == rhs.id && lhs.name == rhs.name; + } + + friend bool operator!=(const User& lhs, const User& rhs) { + return !(lhs == rhs); + } +#endif }; struct Visit { @@ -23,14 +35,6 @@ namespace PreparedStatementTests { std::string description; }; - inline bool operator==(const User& lhs, const User& rhs) { - return lhs.id == rhs.id && lhs.name == rhs.name; - } - - inline bool operator!=(const User& lhs, const User& rhs) { - return !(lhs == rhs); - } - inline void testSerializing(const sqlite_orm::internal::prepared_statement_base& statement) { auto sql = statement.sql(); std::ignore = sql; diff --git a/tests/prepared_statement_tests/select.cpp b/tests/prepared_statement_tests/select.cpp index 47b090bb5..2c4950e84 100644 --- a/tests/prepared_statement_tests/select.cpp +++ b/tests/prepared_statement_tests/select.cpp @@ -373,11 +373,17 @@ TEST_CASE("Prepared select") { decltype(User::id) id = 0; decltype(User::name) name; +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED Z(decltype(id) id, decltype(name) name) : id{id}, name{std::move(name)} {} +#endif +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Z&) const = default; +#else bool operator==(const Z& z) const { return this->id == z.id && this->name == z.name; } +#endif }; constexpr auto z_struct = struct_(&User::id, &User::name); auto statement = storage.prepare(select(z_struct)); diff --git a/tests/schema/virtual_table.cpp b/tests/schema/virtual_table.cpp index 03bb73299..1e01bbdaf 100644 --- a/tests/schema/virtual_table.cpp +++ b/tests/schema/virtual_table.cpp @@ -11,9 +11,13 @@ TEST_CASE("virtual table") { std::string title; std::string body; +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Post&) const = default; +#else bool operator==(const Post& other) const { return this->title == other.title && this->body == other.body; } +#endif }; /// CREATE VIRTUAL TABLE posts diff --git a/tests/select_constraints_tests.cpp b/tests/select_constraints_tests.cpp index 64cee2665..e13910e58 100644 --- a/tests/select_constraints_tests.cpp +++ b/tests/select_constraints_tests.cpp @@ -11,10 +11,14 @@ namespace { std::string address; // optional double salary; // optional +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Employee&) const = default; +#else bool operator==(const Employee& other) const { return this->id == other.id && this->name == other.name && this->age == other.age && this->address == other.address && this->salary == other.salary; } +#endif }; } TEST_CASE("select constraints") { @@ -396,9 +400,13 @@ namespace { int id = 0; std::string firstName; +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User4&) const = default; +#else bool operator==(const User4& user) const { return this->id == user.id && this->firstName == user.firstName; } +#endif }; } TEST_CASE("collate") { diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index ee96605db..c46cd28ad 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -454,10 +454,14 @@ TEST_CASE("insert with generated column") { double tax = 0; double netPrice = 0; +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Product&) const = default; +#else bool operator==(const Product& other) const { return this->name == other.name && this->price == other.price && this->discount == other.discount && this->tax == other.tax && this->netPrice == other.netPrice; } +#endif }; auto storage = make_storage({}, diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index ebe875f79..1df6109f0 100644 --- a/tests/sync_schema_tests.cpp +++ b/tests/sync_schema_tests.cpp @@ -28,8 +28,8 @@ TEST_CASE("Sync schema") { int id = 0; std::string name; - bool operator==(const UserBefore& userBefore) const { - return this->id == userBefore.id && this->name == userBefore.name; + bool operator==(const UserBefore& before) const { + return this->id == before.id && this->name == before.name; } }; @@ -487,9 +487,13 @@ TEST_CASE("sync_schema with generated columns") { int id = 0; int hash = 0; +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User& other) const = default; +#else bool operator==(const User& other) const { return this->id == other.id && this->hash == other.hash; } +#endif }; auto storagePath = "sync_schema_with_generated.sqlite"; std::remove(storagePath); diff --git a/tests/transaction_tests.cpp b/tests/transaction_tests.cpp index 1b37606b9..129c08146 100644 --- a/tests/transaction_tests.cpp +++ b/tests/transaction_tests.cpp @@ -10,9 +10,13 @@ namespace { int id = 0; std::string name; +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Object&) const = default; +#else bool operator==(const Object& other) const { return this->id == other.id && this->name == other.name; } +#endif }; } diff --git a/tests/unique_cases/get_all_with_two_tables.cpp b/tests/unique_cases/get_all_with_two_tables.cpp index b5e69494e..4172cbc17 100644 --- a/tests/unique_cases/get_all_with_two_tables.cpp +++ b/tests/unique_cases/get_all_with_two_tables.cpp @@ -15,11 +15,15 @@ namespace { struct Item { int id = 0; std::string attributes; - }; - inline bool operator==(const Item& lhs, const Item& rhs) { - return lhs.id == rhs.id && lhs.attributes == rhs.attributes; - } +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator==(const Item&, const Item&) = default; +#else + friend bool operator==(const Item& lhs, const Item& rhs) { + return lhs.id == rhs.id && lhs.attributes == rhs.attributes; + } +#endif + }; } TEST_CASE("get_all with two tables") { diff --git a/tests/unique_cases/issue663.cpp b/tests/unique_cases/issue663.cpp index cf2646659..19160a17d 100644 --- a/tests/unique_cases/issue663.cpp +++ b/tests/unique_cases/issue663.cpp @@ -65,16 +65,18 @@ namespace { namespace { struct User1 { - bool operator==(const User1& rhs) const { - return std::tie(id, name, age, email) == std::tie(rhs.id, rhs.name, rhs.age, rhs.email); - } - bool operator!=(const User1& rhs) const { - return !(rhs == *this); - } int id = 0; std::string name; int age = 0; std::string email; + +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User1&) const = default; +#else + bool operator==(const User1& rhs) const { + return std::tie(id, name, age, email) == std::tie(rhs.id, rhs.name, rhs.age, rhs.email); + } +#endif }; } From 381d129852d98db8b4f102d21b67579b5bb027c1 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 18:50:09 +0200 Subject: [PATCH 049/170] Updated comment in internal C++ feature check headers --- dev/functional/cxx_check_prerequisites.h | 4 ++-- dev/functional/cxx_core_features.h | 2 +- include/sqlite_orm/sqlite_orm.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/functional/cxx_check_prerequisites.h b/dev/functional/cxx_check_prerequisites.h index 0531d656e..e79cf6130 100644 --- a/dev/functional/cxx_check_prerequisites.h +++ b/dev/functional/cxx_check_prerequisites.h @@ -5,9 +5,9 @@ */ #if __cpp_aggregate_nsdmi < 201304L -#error A C++14 conforming compiler is required +#error A fully C++14-compliant compiler is required. #endif #if __cpp_constexpr < 201304L -#error A C++14 conforming compiler is required +#error A fully C++14-compliant compiler is required. #endif diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index 45363b7bf..b3123438c 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -1,7 +1,7 @@ #pragma once /* - * This header detects core C++ language features on which sqlite_orm depends. + * This header detects core C++ language features. * May be updated/overwritten by cxx_compiler_quirks.h */ diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index c4fccd4c0..09cdec2f9 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -36,17 +36,17 @@ using std::nullptr_t; */ #if __cpp_aggregate_nsdmi < 201304L -#error A C++14 conforming compiler is required +#error A fully C++14-compliant compiler is required. #endif #if __cpp_constexpr < 201304L -#error A C++14 conforming compiler is required +#error A fully C++14-compliant compiler is required. #endif // #include "cxx_core_features.h" /* - * This header detects core C++ language features on which sqlite_orm depends. + * This header detects core C++ language features. * May be updated/overwritten by cxx_compiler_quirks.h */ From 0738f17a3996e09fb19149e478b77f5263d43875 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 18:56:38 +0200 Subject: [PATCH 050/170] Runner-up: Used qualified `std::remove()` Using named modules the namespace scope will be much stricter, so adhere to it. --- examples/any.cpp | 4 ++-- examples/chrono_binding.cpp | 6 +++--- tests/backup_tests.cpp | 2 +- tests/constraints/default.cpp | 3 ++- tests/sync_schema_tests.cpp | 9 +++++---- tests/transaction_tests.cpp | 7 ++++--- tests/unique_cases/issue937.cpp | 3 ++- tests/user_defined_functions.cpp | 3 ++- 8 files changed, 21 insertions(+), 16 deletions(-) diff --git a/examples/any.cpp b/examples/any.cpp index 8f7815c39..01843a50e 100644 --- a/examples/any.cpp +++ b/examples/any.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include // std::remove using namespace sqlite_orm; using std::cout; @@ -135,7 +135,7 @@ int main() { std::any value; }; auto filename = "any.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage( filename, make_table("test", make_column("id", &Value::id, primary_key()), make_column("value", &Value::value))); diff --git a/examples/chrono_binding.cpp b/examples/chrono_binding.cpp index 8307b18a9..1adb2c7d0 100644 --- a/examples/chrono_binding.cpp +++ b/examples/chrono_binding.cpp @@ -11,6 +11,7 @@ #ifdef ENABLE_THIS_EXAMPLE #include +#include // std::remove #include #include #include @@ -30,8 +31,7 @@ // also we need transform functions to make string from enum.. static std::string sysDaysToString(std::chrono::sys_days pt) { - auto r = std::format("{:%F}", pt); - return r; + return std::format("{:%F}", pt); } /** @@ -149,7 +149,7 @@ struct Person { int main(int, char**) { #ifdef ENABLE_THIS_EXAMPLE const std::string db_name = "sys_days.sqlite"; - ::remove(db_name.c_str()); + std::remove(db_name.c_str()); auto storage = make_storage(db_name, make_table("Persons", diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index dde1f252c..19f39611d 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -87,7 +87,7 @@ TEST_CASE("backup") { REQUIRE_THAT(rowsFromBackup, UnorderedEquals(expectedRows)); } SECTION("from") { - ::remove(backupFilename.c_str()); + std::remove(backupFilename.c_str()); auto storage = makeStorage(backupFilename); storage.sync_schema(); storage.replace(User{1, "Sharon"}); diff --git a/tests/constraints/default.cpp b/tests/constraints/default.cpp index d35c3347c..14979883d 100644 --- a/tests/constraints/default.cpp +++ b/tests/constraints/default.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove using namespace sqlite_orm; @@ -14,7 +15,7 @@ TEST_CASE("Default value") { auto filename = "test_db.sqlite"; - ::remove(filename); + std::remove(filename); auto storage1 = make_storage(filename, make_table("User", diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index 6c0aab30f..942746be2 100644 --- a/tests/sync_schema_tests.cpp +++ b/tests/sync_schema_tests.cpp @@ -1,4 +1,5 @@ #include +#include // std::remove #include #include @@ -34,7 +35,7 @@ TEST_CASE("Sync schema") { // create an old storage auto filename = "sync_schema_text.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage(filename, make_table("users", make_column("id", &UserBefore::id, primary_key()), @@ -150,7 +151,7 @@ TEST_CASE("issue521") { }; std::vector pocosToInsert; - ::remove(storagePath); + std::remove(storagePath); { // --- Create the initial database auto storage = sqlite_orm::make_storage( @@ -310,7 +311,7 @@ TEST_CASE("sync_schema") { const std::string name = "name"; const std::string age = "age"; } columnNames; - ::remove(storagePath); + std::remove(storagePath); { auto storage = make_storage(storagePath, make_table(tableName, @@ -501,7 +502,7 @@ TEST_CASE("sync_schema with generated columns") { } }; auto storagePath = "sync_schema_with_generated.sqlite"; - ::remove(storagePath); + std::remove(storagePath); auto storage1 = make_storage(storagePath, make_table("users", make_column("id", &User::id))); storage1.sync_schema(); storage1.insert(User{5}); diff --git a/tests/transaction_tests.cpp b/tests/transaction_tests.cpp index a23fdba03..7126b4ea8 100644 --- a/tests/transaction_tests.cpp +++ b/tests/transaction_tests.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove #include "catch_matchers.h" using namespace sqlite_orm; @@ -22,7 +23,7 @@ namespace { TEST_CASE("transaction") { auto filename = "transaction_test.sqlite"; - ::remove(filename); + std::remove(filename); auto storage = make_storage( "test_transaction_guard.sqlite", make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name))); @@ -64,7 +65,7 @@ TEST_CASE("begin_transaction") { } TEST_CASE("Transaction guard") { - ::remove("guard.sqlite"); + std::remove("guard.sqlite"); auto table = make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name)); auto storage = make_storage("guard.sqlite", table); @@ -342,5 +343,5 @@ TEST_CASE("Transaction guard") { guard->commit_on_destroy = true; REQUIRE_THROWS_WITH(guard->~transaction_guard_t(), ContainsSubstring("database is locked")); } - ::remove("guard.sqlite"); + std::remove("guard.sqlite"); } diff --git a/tests/unique_cases/issue937.cpp b/tests/unique_cases/issue937.cpp index b0baf876a..19f7370a0 100644 --- a/tests/unique_cases/issue937.cpp +++ b/tests/unique_cases/issue937.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove #if SQLITE_VERSION_NUMBER >= 3006019 using namespace sqlite_orm; @@ -32,7 +33,7 @@ TEST_CASE("issue937") { using namespace sqlite_orm; - ::remove("SQLCookbook.sqlite"); + std::remove("SQLCookbook.sqlite"); auto storage = make_storage("SQLCookbook.sqlite", make_table("Emp", make_column("empno", &Employee::m_empno, primary_key().autoincrement()), diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index 45548f607..1bd70ef5e 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::remove #include "catch_matchers.h" using namespace sqlite_orm; @@ -291,7 +292,7 @@ TEST_CASE("custom functions") { } SECTION("file") { path = "custom_function.sqlite"; - ::remove(path.c_str()); + std::remove(path.c_str()); } struct User { int id = 0; From dd6441430ad087457631f6b19a69b3fe7014695c Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 18:57:09 +0200 Subject: [PATCH 051/170] Correctly observe availability of `std::any` `std::any` is only available starting with a C++17 library. --- examples/any.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/examples/any.cpp b/examples/any.cpp index 01843a50e..4d6f30bb0 100644 --- a/examples/any.cpp +++ b/examples/any.cpp @@ -14,7 +14,17 @@ * BLOB is mapped to std::vector. */ #include +#ifdef __has_include +#if __has_include() #include +#endif +#endif + +#if __cpp_lib_any >= 201606L +#define ENABLE_THIS_EXAMPLE +#endif + +#ifdef ENABLE_THIS_EXAMPLE #include #include // std::remove @@ -128,8 +138,10 @@ namespace sqlite_orm { } }; } +#endif int main() { +#ifdef ENABLE_THIS_EXAMPLE struct Value { int id = 0; std::any value; @@ -151,5 +163,6 @@ int main() { cout << storage.dump(test) << endl; } cout << endl; +#endif return 0; } From 6feafc229e716900941fb5138abc3f85d8805bae Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 18:57:54 +0200 Subject: [PATCH 052/170] Replaced #include of `` with `` Macros will not be available when importing the Standard Library as a named module. The recommended and only fallback is to include the C header file for `assert()`. --- dev/udf_proxy.h | 28 ++++++++++++++-------------- examples/blob.cpp | 2 +- examples/chrono_binding.cpp | 2 +- examples/column_aliases.cpp | 1 - examples/core_functions.cpp | 2 +- examples/custom_aliases.cpp | 2 +- examples/enum_binding.cpp | 2 +- examples/foreign_key.cpp | 2 +- examples/in_memory.cpp | 2 +- examples/private_class_members.cpp | 2 +- examples/self_join.cpp | 2 +- examples/union.cpp | 2 +- include/sqlite_orm/sqlite_orm.h | 2 +- 13 files changed, 25 insertions(+), 26 deletions(-) diff --git a/dev/udf_proxy.h b/dev/udf_proxy.h index bd14a5023..2d49a84b1 100644 --- a/dev/udf_proxy.h +++ b/dev/udf_proxy.h @@ -1,7 +1,7 @@ #pragma once #include -#include // assert macro +#include // assert macro #include // std::true_type, std::false_type #include // std::bad_alloc #include // std::allocator, std::allocator_traits, std::unique_ptr @@ -13,9 +13,9 @@ namespace sqlite_orm { namespace internal { - /* - * Returns properly allocated memory space for the specified application-defined function object - * paired with an accompanying deallocation function. + /* + * Returns properly allocated memory space for the specified application-defined function object + * paired with an accompanying deallocation function. */ template std::pair preallocate_udf_memory() { @@ -31,8 +31,8 @@ namespace sqlite_orm { return {traits::allocate(allocator, 1), deallocate}; } - /* - * Returns a pair of functions to allocate/deallocate properly aligned memory space for the specified application-defined function object. + /* + * Returns a pair of functions to allocate/deallocate properly aligned memory space for the specified application-defined function object. */ template std::pair obtain_udf_allocator() { @@ -51,8 +51,8 @@ namespace sqlite_orm { return {allocate, deallocate}; } - /* - * A deleter that only destroys the application-defined function object. + /* + * A deleter that only destroys the application-defined function object. */ struct udf_destruct_only_deleter { template @@ -63,12 +63,12 @@ namespace sqlite_orm { } }; - /* - * Stores type-erased information in relation to an application-defined scalar or aggregate function object: - * - name and argument count - * - function dispatch (step, final) - * - either preallocated memory with a possibly a priori constructed function object [scalar], - * - or memory allocation/deallocation functions [aggregate] + /* + * Stores type-erased information in relation to an application-defined scalar or aggregate function object: + * - name and argument count + * - function dispatch (step, final) + * - either preallocated memory with a possibly a priori constructed function object [scalar], + * - or memory allocation/deallocation functions [aggregate] */ struct udf_proxy { using sqlite_func_t = void (*)(sqlite3_context* context, int argsCount, sqlite3_value** values); diff --git a/examples/blob.cpp b/examples/blob.cpp index 279ef842d..3ad94cf6d 100644 --- a/examples/blob.cpp +++ b/examples/blob.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include using std::cout; using std::endl; diff --git a/examples/chrono_binding.cpp b/examples/chrono_binding.cpp index 1adb2c7d0..f5c14ac97 100644 --- a/examples/chrono_binding.cpp +++ b/examples/chrono_binding.cpp @@ -10,7 +10,7 @@ #endif #ifdef ENABLE_THIS_EXAMPLE -#include +#include #include // std::remove #include #include diff --git a/examples/column_aliases.cpp b/examples/column_aliases.cpp index 8fdb55cc1..02ee647bf 100644 --- a/examples/column_aliases.cpp +++ b/examples/column_aliases.cpp @@ -1,6 +1,5 @@ #include #include -#include using std::cout; using std::endl; diff --git a/examples/core_functions.cpp b/examples/core_functions.cpp index ce6dc4149..37aa5f19d 100644 --- a/examples/core_functions.cpp +++ b/examples/core_functions.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include using std::cout; using std::endl; diff --git a/examples/custom_aliases.cpp b/examples/custom_aliases.cpp index fcd44189f..476f737a6 100644 --- a/examples/custom_aliases.cpp +++ b/examples/custom_aliases.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include #include diff --git a/examples/enum_binding.cpp b/examples/enum_binding.cpp index 70984f9a4..14bfbc380 100644 --- a/examples/enum_binding.cpp +++ b/examples/enum_binding.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include using std::cout; using std::endl; diff --git a/examples/foreign_key.cpp b/examples/foreign_key.cpp index ec7c92e9e..24f230499 100644 --- a/examples/foreign_key.cpp +++ b/examples/foreign_key.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #if SQLITE_VERSION_NUMBER >= 3006019 diff --git a/examples/in_memory.cpp b/examples/in_memory.cpp index 5799f967a..f10ccb3f4 100644 --- a/examples/in_memory.cpp +++ b/examples/in_memory.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include using namespace sqlite_orm; diff --git a/examples/private_class_members.cpp b/examples/private_class_members.cpp index 73d10a4ad..a071c4378 100644 --- a/examples/private_class_members.cpp +++ b/examples/private_class_members.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include using std::cout; using std::endl; diff --git a/examples/self_join.cpp b/examples/self_join.cpp index 8d103a9df..3ea98c1ec 100644 --- a/examples/self_join.cpp +++ b/examples/self_join.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #if SQLITE_VERSION_NUMBER >= 3006019 #define ENABLE_THIS_EXAMPLE diff --git a/examples/union.cpp b/examples/union.cpp index 68b229917..2bc0d4a61 100644 --- a/examples/union.cpp +++ b/examples/union.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 795a57e70..f4efa1fa8 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -17278,7 +17278,7 @@ namespace sqlite_orm { // #include "udf_proxy.h" #include -#include // assert macro +#include // assert macro #include // std::true_type, std::false_type #include // std::bad_alloc #include // std::allocator, std::allocator_traits, std::unique_ptr From a162bfc4a9ce25172324daa4a4dbcc78dc9b1d0c Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 29 Jan 2025 19:41:45 +0200 Subject: [PATCH 053/170] Used C++20 default comparisons where possible --- dev/arg_values.h | 2 ++ include/sqlite_orm/sqlite_orm.h | 2 ++ tests/backup_tests.cpp | 12 +++++++---- tests/iterate.cpp | 4 ++++ tests/operators/in.cpp | 4 ++++ .../prepared_common.h | 20 +++++++++++-------- tests/prepared_statement_tests/select.cpp | 6 ++++++ tests/schema/virtual_table.cpp | 4 ++++ tests/select_constraints_tests.cpp | 8 ++++++++ tests/storage_tests.cpp | 4 ++++ tests/sync_schema_tests.cpp | 8 ++++++-- tests/transaction_tests.cpp | 4 ++++ .../unique_cases/get_all_with_two_tables.cpp | 12 +++++++---- tests/unique_cases/issue663.cpp | 14 +++++++------ 14 files changed, 80 insertions(+), 24 deletions(-) diff --git a/dev/arg_values.h b/dev/arg_values.h index 0e8260790..ab10d1ec5 100644 --- a/dev/arg_values.h +++ b/dev/arg_values.h @@ -98,9 +98,11 @@ namespace sqlite_orm { return &other.container == &this->container && other.index == this->index; } +#ifndef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED bool operator!=(const iterator& other) const { return !(*this == other); } +#endif private: const arg_values& container; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index f4efa1fa8..8649e1ed7 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -17189,9 +17189,11 @@ namespace sqlite_orm { return &other.container == &this->container && other.index == this->index; } +#ifndef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED bool operator!=(const iterator& other) const { return !(*this == other); } +#endif private: const arg_values& container; diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index 19f39611d..d2e973a97 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -13,11 +13,15 @@ namespace { User() = default; User(int id, std::string name) : id{id}, name{std::move(name)} {} #endif - }; - bool operator==(const User& lhs, const User& rhs) { - return lhs.id == rhs.id && lhs.name == rhs.name; - } +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator==(const User&, const User&) = default; +#else + friend bool operator==(const User& lhs, const User& rhs) { + return lhs.id == rhs.id && lhs.name == rhs.name; + } +#endif + }; struct MarvelHero { int id; diff --git a/tests/iterate.cpp b/tests/iterate.cpp index 8e70e4e56..e24e28b1b 100644 --- a/tests/iterate.cpp +++ b/tests/iterate.cpp @@ -9,9 +9,13 @@ TEST_CASE("Iterate mapped") { int64_t id; std::vector key; +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Test&) const = default; +#else bool operator==(const Test& rhs) const { return this->id == rhs.id && this->key == rhs.key; } +#endif }; auto db = diff --git a/tests/operators/in.cpp b/tests/operators/in.cpp index 2997403fa..48294c213 100644 --- a/tests/operators/in.cpp +++ b/tests/operators/in.cpp @@ -14,9 +14,13 @@ TEST_CASE("In") { User(int id) : id{id} {} #endif +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User& other) const = default; +#else bool operator==(const User& other) const { return this->id == other.id; } +#endif std::ostream& operator<<(std::ostream& os) const { return os << this->id; diff --git a/tests/prepared_statement_tests/prepared_common.h b/tests/prepared_statement_tests/prepared_common.h index 1d4f81927..cd7ac41d9 100644 --- a/tests/prepared_statement_tests/prepared_common.h +++ b/tests/prepared_statement_tests/prepared_common.h @@ -14,6 +14,18 @@ namespace PreparedStatementTests { User() = default; User(int id, std::string name) : id{id}, name{std::move(name)} {} #endif + +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator==(const User&, const User&) = default; +#else + friend bool operator==(const User& lhs, const User& rhs) { + return lhs.id == rhs.id && lhs.name == rhs.name; + } + + friend bool operator!=(const User& lhs, const User& rhs) { + return !(lhs == rhs); + } +#endif }; struct Visit { @@ -40,14 +52,6 @@ namespace PreparedStatementTests { #endif }; - inline bool operator==(const User& lhs, const User& rhs) { - return lhs.id == rhs.id && lhs.name == rhs.name; - } - - inline bool operator!=(const User& lhs, const User& rhs) { - return !(lhs == rhs); - } - inline void testSerializing(const sqlite_orm::internal::prepared_statement_base& statement) { auto sql = statement.sql(); std::ignore = sql; diff --git a/tests/prepared_statement_tests/select.cpp b/tests/prepared_statement_tests/select.cpp index 47b090bb5..2c4950e84 100644 --- a/tests/prepared_statement_tests/select.cpp +++ b/tests/prepared_statement_tests/select.cpp @@ -373,11 +373,17 @@ TEST_CASE("Prepared select") { decltype(User::id) id = 0; decltype(User::name) name; +#ifndef SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED Z(decltype(id) id, decltype(name) name) : id{id}, name{std::move(name)} {} +#endif +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Z&) const = default; +#else bool operator==(const Z& z) const { return this->id == z.id && this->name == z.name; } +#endif }; constexpr auto z_struct = struct_(&User::id, &User::name); auto statement = storage.prepare(select(z_struct)); diff --git a/tests/schema/virtual_table.cpp b/tests/schema/virtual_table.cpp index 03bb73299..1e01bbdaf 100644 --- a/tests/schema/virtual_table.cpp +++ b/tests/schema/virtual_table.cpp @@ -11,9 +11,13 @@ TEST_CASE("virtual table") { std::string title; std::string body; +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Post&) const = default; +#else bool operator==(const Post& other) const { return this->title == other.title && this->body == other.body; } +#endif }; /// CREATE VIRTUAL TABLE posts diff --git a/tests/select_constraints_tests.cpp b/tests/select_constraints_tests.cpp index 034b8869d..4367905d5 100644 --- a/tests/select_constraints_tests.cpp +++ b/tests/select_constraints_tests.cpp @@ -11,10 +11,14 @@ namespace { std::string address; // optional double salary; // optional +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Employee&) const = default; +#else bool operator==(const Employee& other) const { return this->id == other.id && this->name == other.name && this->age == other.age && this->address == other.address && this->salary == other.salary; } +#endif }; } TEST_CASE("select constraints") { @@ -428,9 +432,13 @@ namespace { User4(int id, std::string firstName) : id{id}, firstName{std::move(firstName)} {} #endif +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User4&) const = default; +#else bool operator==(const User4& user) const { return this->id == user.id && this->firstName == user.firstName; } +#endif }; } TEST_CASE("collate") { diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index 437eced79..11a9fa5ae 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -460,10 +460,14 @@ TEST_CASE("insert with generated column") { name{std::move(name)}, price{price}, discount{discount}, tax{tax}, netPrice{netPrice} {} #endif +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Product&) const = default; +#else bool operator==(const Product& other) const { return this->name == other.name && this->price == other.price && this->discount == other.discount && this->tax == other.tax && this->netPrice == other.netPrice; } +#endif }; auto storage = make_storage({}, diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index 942746be2..49dd194e6 100644 --- a/tests/sync_schema_tests.cpp +++ b/tests/sync_schema_tests.cpp @@ -28,8 +28,8 @@ TEST_CASE("Sync schema") { int id; std::string name; - bool operator==(const UserBefore& userBefore) const { - return this->id == userBefore.id && this->name == userBefore.name; + bool operator==(const UserBefore& before) const { + return this->id == before.id && this->name == before.name; } }; @@ -497,9 +497,13 @@ TEST_CASE("sync_schema with generated columns") { User(int id, int hash = 0) : id{id}, hash{hash} {} #endif +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User& other) const = default; +#else bool operator==(const User& other) const { return this->id == other.id && this->hash == other.hash; } +#endif }; auto storagePath = "sync_schema_with_generated.sqlite"; std::remove(storagePath); diff --git a/tests/transaction_tests.cpp b/tests/transaction_tests.cpp index 7126b4ea8..7c4ee580d 100644 --- a/tests/transaction_tests.cpp +++ b/tests/transaction_tests.cpp @@ -15,9 +15,13 @@ namespace { Object(int id, std::string name) : id{id}, name{std::move(name)} {} #endif +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const Object&) const = default; +#else bool operator==(const Object& other) const { return this->id == other.id && this->name == other.name; } +#endif }; } diff --git a/tests/unique_cases/get_all_with_two_tables.cpp b/tests/unique_cases/get_all_with_two_tables.cpp index ddf7d6ad2..8bdb2970f 100644 --- a/tests/unique_cases/get_all_with_two_tables.cpp +++ b/tests/unique_cases/get_all_with_two_tables.cpp @@ -20,11 +20,15 @@ namespace { Item() = default; Item(int id, std::string attributes) : id{id}, attributes{std::move(attributes)} {} #endif - }; - inline bool operator==(const Item& lhs, const Item& rhs) { - return lhs.id == rhs.id && lhs.attributes == rhs.attributes; - } +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator==(const Item&, const Item&) = default; +#else + friend bool operator==(const Item& lhs, const Item& rhs) { + return lhs.id == rhs.id && lhs.attributes == rhs.attributes; + } +#endif + }; } TEST_CASE("get_all with two tables") { diff --git a/tests/unique_cases/issue663.cpp b/tests/unique_cases/issue663.cpp index 1a2343cdd..2509c3b47 100644 --- a/tests/unique_cases/issue663.cpp +++ b/tests/unique_cases/issue663.cpp @@ -65,16 +65,18 @@ namespace { namespace { struct User1 { - bool operator==(const User1& rhs) const { - return std::tie(id, name, age, email) == std::tie(rhs.id, rhs.name, rhs.age, rhs.email); - } - bool operator!=(const User1& rhs) const { - return !(rhs == *this); - } int id; std::string name; int age; std::string email; + +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + bool operator==(const User1&) const = default; +#else + bool operator==(const User1& rhs) const { + return std::tie(id, name, age, email) == std::tie(rhs.id, rhs.name, rhs.age, rhs.email); + } +#endif }; } From c88a5229078c7415f70e971409dbf117ce129cd3 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 30 Jan 2025 08:59:36 +0200 Subject: [PATCH 054/170] Corrected merge errors --- tests/backup_tests.cpp | 8 -------- tests/unique_cases/get_all_with_two_tables.cpp | 8 -------- 2 files changed, 16 deletions(-) diff --git a/tests/backup_tests.cpp b/tests/backup_tests.cpp index 8b0117e75..dc8215c66 100644 --- a/tests/backup_tests.cpp +++ b/tests/backup_tests.cpp @@ -9,14 +9,6 @@ namespace { int id = 0; std::string name; -#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED - friend bool operator==(const User&, const User&) = default; -#else - friend bool operator==(const User& lhs, const User& rhs) { - return lhs.id == rhs.id && lhs.name == rhs.name; - } -#endif - #ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED friend bool operator==(const User&, const User&) = default; #else diff --git a/tests/unique_cases/get_all_with_two_tables.cpp b/tests/unique_cases/get_all_with_two_tables.cpp index 33420d3f8..4172cbc17 100644 --- a/tests/unique_cases/get_all_with_two_tables.cpp +++ b/tests/unique_cases/get_all_with_two_tables.cpp @@ -16,14 +16,6 @@ namespace { int id = 0; std::string attributes; -#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED - friend bool operator==(const Item&, const Item&) = default; -#else - friend bool operator==(const Item& lhs, const Item& rhs) { - return lhs.id == rhs.id && lhs.attributes == rhs.attributes; - } -#endif - #ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED friend bool operator==(const Item&, const Item&) = default; #else From 12a911a1168cf4f89c41f965c9235d6bfe0857a1 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 30 Jan 2025 18:55:54 +0200 Subject: [PATCH 055/170] Reverted removing underscore of a few member variables Most importantly, these members are listed after public member functions in a suggestion list of an IDE. --- dev/conditions.h | 12 ++++++------ dev/indexed_column.h | 6 +++--- dev/order_by_serializer.h | 8 ++++---- dev/statement_serializer.h | 6 +++--- include/sqlite_orm/sqlite_orm.h | 32 ++++++++++++++++---------------- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/dev/conditions.h b/dev/conditions.h index 3d33fe65b..ff7bff048 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -431,7 +431,7 @@ namespace sqlite_orm { struct order_by_base { int asc_desc = 0; // 1: asc, -1: desc - std::string collate_argument; + std::string _collate_argument; }; struct order_by_string { @@ -466,25 +466,25 @@ namespace sqlite_orm { self collate_binary() const { auto res = *this; - res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); return res; } self collate_nocase() const { auto res = *this; - res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); return res; } self collate_rtrim() const { auto res = *this; - res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); return res; } self collate(std::string name) const { auto res = *this; - res.collate_argument = std::move(name); + res._collate_argument = std::move(name); return res; } @@ -533,7 +533,7 @@ namespace sqlite_orm { auto columnName = serialize(order_by.expression, newContext); this->entries.emplace_back(std::move(columnName), order_by.asc_desc, - std::move(order_by.collate_argument)); + std::move(order_by._collate_argument)); } const_iterator begin() const { diff --git a/dev/indexed_column.h b/dev/indexed_column.h index eb8221da0..98a48f4bd 100644 --- a/dev/indexed_column.h +++ b/dev/indexed_column.h @@ -13,13 +13,13 @@ namespace sqlite_orm { struct indexed_column_t { using column_type = C; - column_type column_or_expression; - std::string collation_name; + column_type _column_or_expression; + std::string _collation_name; int _order = 0; // -1 = desc, 1 = asc, 0 = not specified indexed_column_t collate(std::string name) { auto res = std::move(*this); - res.collation_name = std::move(name); + res._collation_name = std::move(name); return res; } diff --git a/dev/order_by_serializer.h b/dev/order_by_serializer.h index e6addabf2..bc2d418ac 100644 --- a/dev/order_by_serializer.h +++ b/dev/order_by_serializer.h @@ -27,8 +27,8 @@ namespace sqlite_orm { newContext.skip_table_name = false; ss << serialize(orderBy.expression, newContext); - if (!orderBy.collate_argument.empty()) { - ss << " COLLATE " << orderBy.collate_argument; + if (!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; } switch (orderBy.asc_desc) { case 1: @@ -57,8 +57,8 @@ namespace sqlite_orm { } ss << entry.name; - if (!entry.collate_argument.empty()) { - ss << " COLLATE " << entry.collate_argument; + if (!entry._collate_argument.empty()) { + ss << " COLLATE " << entry._collate_argument; } switch (entry.asc_desc) { case 1: diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 314325728..c44562eae 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1834,9 +1834,9 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << serialize(statement.column_or_expression, context); - if (!statement.collation_name.empty()) { - ss << " COLLATE " << statement.collation_name; + ss << serialize(statement._column_or_expression, context); + if (!statement._collation_name.empty()) { + ss << " COLLATE " << statement._collation_name; } if (statement._order) { switch (statement._order) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 73674126b..bd5afc158 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -5180,7 +5180,7 @@ namespace sqlite_orm { struct order_by_base { int asc_desc = 0; // 1: asc, -1: desc - std::string collate_argument; + std::string _collate_argument; }; struct order_by_string { @@ -5215,25 +5215,25 @@ namespace sqlite_orm { self collate_binary() const { auto res = *this; - res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); return res; } self collate_nocase() const { auto res = *this; - res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); return res; } self collate_rtrim() const { auto res = *this; - res.collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); return res; } self collate(std::string name) const { auto res = *this; - res.collate_argument = std::move(name); + res._collate_argument = std::move(name); return res; } @@ -5282,7 +5282,7 @@ namespace sqlite_orm { auto columnName = serialize(order_by.expression, newContext); this->entries.emplace_back(std::move(columnName), order_by.asc_desc, - std::move(order_by.collate_argument)); + std::move(order_by._collate_argument)); } const_iterator begin() const { @@ -11844,13 +11844,13 @@ namespace sqlite_orm { struct indexed_column_t { using column_type = C; - column_type column_or_expression; - std::string collation_name; + column_type _column_or_expression; + std::string _collation_name; int _order = 0; // -1 = desc, 1 = asc, 0 = not specified indexed_column_t collate(std::string name) { auto res = std::move(*this); - res.collation_name = std::move(name); + res._collation_name = std::move(name); return res; } @@ -18998,8 +18998,8 @@ namespace sqlite_orm { newContext.skip_table_name = false; ss << serialize(orderBy.expression, newContext); - if (!orderBy.collate_argument.empty()) { - ss << " COLLATE " << orderBy.collate_argument; + if (!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; } switch (orderBy.asc_desc) { case 1: @@ -19028,8 +19028,8 @@ namespace sqlite_orm { } ss << entry.name; - if (!entry.collate_argument.empty()) { - ss << " COLLATE " << entry.collate_argument; + if (!entry._collate_argument.empty()) { + ss << " COLLATE " << entry._collate_argument; } switch (entry.asc_desc) { case 1: @@ -21134,9 +21134,9 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << serialize(statement.column_or_expression, context); - if (!statement.collation_name.empty()) { - ss << " COLLATE " << statement.collation_name; + ss << serialize(statement._column_or_expression, context); + if (!statement._collation_name.empty()) { + ss << " COLLATE " << statement._collation_name; } if (statement._order) { switch (statement._order) { From 1f4cbb7e4d237af46cb19afd412ec942ad385589 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 30 Jan 2025 22:04:09 +0200 Subject: [PATCH 056/170] Various things in respect to 'order by' and 'indexed columns' * Fixed indexed column grammar: sorting can be left unspecified. * Aligned 'order by' and 'indexed column' structs and serialization. * Streaming function for the common parts of 'order by' and 'dynamic order by' * Prefixed ordering flag member variable of 'order by' for better sorting in IDE suggestion lists. --- dev/ast_iterator.h | 2 +- dev/conditions.h | 22 ++++---- dev/indexed_column.h | 2 +- dev/order_by_serializer.h | 62 +++++++++++---------- dev/statement_serializer.h | 8 ++- include/sqlite_orm/sqlite_orm.h | 96 ++++++++++++++++----------------- tests/schema/index_tests.cpp | 4 ++ 7 files changed, 94 insertions(+), 102 deletions(-) diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index b48debde8..5fdc45644 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -701,7 +701,7 @@ namespace sqlite_orm { template void operator()(const node_type& node, L& lambda) const { - iterate_ast(node.expression, lambda); + iterate_ast(node._expression, lambda); } }; diff --git a/dev/conditions.h b/dev/conditions.h index ff7bff048..b7fc23c8b 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -430,8 +430,8 @@ namespace sqlite_orm { }; struct order_by_base { - int asc_desc = 0; // 1: asc, -1: desc std::string _collate_argument; + int _order = 0; // -1 = desc, 1 = asc, 0 = unspecified }; struct order_by_string { @@ -448,19 +448,19 @@ namespace sqlite_orm { using expression_type = O; using self = order_by_t; - expression_type expression; + expression_type _expression; - order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} + order_by_t(expression_type expression) : order_by_base(), _expression(std::move(expression)) {} self asc() const { auto res = *this; - res.asc_desc = 1; + res._order = 1; return res; } self desc() const { auto res = *this; - res.asc_desc = -1; + res._order = -1; return res; } @@ -511,8 +511,8 @@ namespace sqlite_orm { struct dynamic_order_by_entry_t : order_by_base { std::string name; - dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : - order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} + dynamic_order_by_entry_t(decltype(name) name_, std::string collate_argument_, int asc_desc_) : + order_by_base{std::move(collate_argument_), asc_desc_}, name(std::move(name_)) {} }; /** @@ -527,13 +527,11 @@ namespace sqlite_orm { dynamic_order_by_t(const context_t& context_) : context(context_) {} template - void push_back(order_by_t order_by) { + void push_back(order_by_t orderBy) { auto newContext = this->context; newContext.skip_table_name = false; - auto columnName = serialize(order_by.expression, newContext); - this->entries.emplace_back(std::move(columnName), - order_by.asc_desc, - std::move(order_by._collate_argument)); + auto columnName = serialize(orderBy._expression, newContext); + this->entries.emplace_back(std::move(columnName), std::move(orderBy._collate_argument), orderBy._order); } const_iterator begin() const { diff --git a/dev/indexed_column.h b/dev/indexed_column.h index 98a48f4bd..6385a4ca1 100644 --- a/dev/indexed_column.h +++ b/dev/indexed_column.h @@ -15,7 +15,7 @@ namespace sqlite_orm { column_type _column_or_expression; std::string _collation_name; - int _order = 0; // -1 = desc, 1 = asc, 0 = not specified + int _order = 0; // -1 = desc, 1 = asc, 0 = unspecified indexed_column_t collate(std::string name) { auto res = std::move(*this); diff --git a/dev/order_by_serializer.h b/dev/order_by_serializer.h index bc2d418ac..68209c02f 100644 --- a/dev/order_by_serializer.h +++ b/dev/order_by_serializer.h @@ -1,7 +1,9 @@ #pragma once +#include #include // std::string #include // std::stringstream +#include // std::exchange namespace sqlite_orm { @@ -16,6 +18,20 @@ namespace sqlite_orm { return serializer(t, context); } + inline void seralize_collate(std::ostream& ss, const order_by_base& orderBy) { + if (!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; + } + switch (orderBy._order) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + } + template struct order_by_serializer, void> { using statement_type = order_by_t; @@ -26,18 +42,8 @@ namespace sqlite_orm { auto newContext = context; newContext.skip_table_name = false; - ss << serialize(orderBy.expression, newContext); - if (!orderBy._collate_argument.empty()) { - ss << " COLLATE " << orderBy._collate_argument; - } - switch (orderBy.asc_desc) { - case 1: - ss << " ASC"; - break; - case -1: - ss << " DESC"; - break; - } + ss << serialize(orderBy._expression, newContext); + seralize_collate(ss, orderBy); return ss.str(); } }; @@ -50,29 +56,21 @@ namespace sqlite_orm { std::string operator()(const statement_type& orderBy, const Ctx&) const { std::stringstream ss; ss << static_cast(orderBy) << " "; - int index = 0; + static constexpr std::array sep = {", ", ""}; +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED + for (bool first = true; const dynamic_order_by_entry_t& entry: orderBy) { + ss << sep[std::exchange(first, false)] << entry.name; + seralize_collate(ss, entry); + } +#else + bool first = true; for (const dynamic_order_by_entry_t& entry: orderBy) { - if (index > 0) { - ss << ", "; - } - - ss << entry.name; - if (!entry._collate_argument.empty()) { - ss << " COLLATE " << entry._collate_argument; - } - switch (entry.asc_desc) { - case 1: - ss << " ASC"; - break; - case -1: - ss << " DESC"; - break; - } - ++index; - }; + ss << sep[std::exchange(first, false)] << entry.name; + seralize_collate(ss, entry); + } +#endif return ss.str(); } }; - } } diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index c44562eae..d93a4c12d 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1840,14 +1840,12 @@ namespace sqlite_orm { } if (statement._order) { switch (statement._order) { - case -1: - ss << " DESC"; - break; case 1: ss << " ASC"; break; - default: - throw std::system_error{orm_error_code::incorrect_order}; + case -1: + ss << " DESC"; + break; } } return ss.str(); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index bd5afc158..cbd8695b6 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -5179,8 +5179,8 @@ namespace sqlite_orm { }; struct order_by_base { - int asc_desc = 0; // 1: asc, -1: desc std::string _collate_argument; + int _order = 0; // -1 = desc, 1 = asc, 0 = unspecified }; struct order_by_string { @@ -5197,19 +5197,19 @@ namespace sqlite_orm { using expression_type = O; using self = order_by_t; - expression_type expression; + expression_type _expression; - order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} + order_by_t(expression_type expression) : order_by_base(), _expression(std::move(expression)) {} self asc() const { auto res = *this; - res.asc_desc = 1; + res._order = 1; return res; } self desc() const { auto res = *this; - res.asc_desc = -1; + res._order = -1; return res; } @@ -5260,8 +5260,8 @@ namespace sqlite_orm { struct dynamic_order_by_entry_t : order_by_base { std::string name; - dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : - order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} + dynamic_order_by_entry_t(decltype(name) name_, std::string collate_argument_, int asc_desc_) : + order_by_base{std::move(collate_argument_), asc_desc_}, name(std::move(name_)) {} }; /** @@ -5276,13 +5276,11 @@ namespace sqlite_orm { dynamic_order_by_t(const context_t& context_) : context(context_) {} template - void push_back(order_by_t order_by) { + void push_back(order_by_t orderBy) { auto newContext = this->context; newContext.skip_table_name = false; - auto columnName = serialize(order_by.expression, newContext); - this->entries.emplace_back(std::move(columnName), - order_by.asc_desc, - std::move(order_by._collate_argument)); + auto columnName = serialize(orderBy._expression, newContext); + this->entries.emplace_back(std::move(columnName), std::move(orderBy._collate_argument), orderBy._order); } const_iterator begin() const { @@ -11846,7 +11844,7 @@ namespace sqlite_orm { column_type _column_or_expression; std::string _collation_name; - int _order = 0; // -1 = desc, 1 = asc, 0 = not specified + int _order = 0; // -1 = desc, 1 = asc, 0 = unspecified indexed_column_t collate(std::string name) { auto res = std::move(*this); @@ -15639,7 +15637,7 @@ namespace sqlite_orm { template void operator()(const node_type& node, L& lambda) const { - iterate_ast(node.expression, lambda); + iterate_ast(node._expression, lambda); } }; @@ -18971,8 +18969,10 @@ namespace sqlite_orm { // #include "order_by_serializer.h" +#include #include // std::string #include // std::stringstream +#include // std::exchange namespace sqlite_orm { @@ -18987,6 +18987,20 @@ namespace sqlite_orm { return serializer(t, context); } + inline void seralize_collate(std::ostream& ss, const order_by_base& orderBy) { + if (!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; + } + switch (orderBy._order) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + } + template struct order_by_serializer, void> { using statement_type = order_by_t; @@ -18997,18 +19011,8 @@ namespace sqlite_orm { auto newContext = context; newContext.skip_table_name = false; - ss << serialize(orderBy.expression, newContext); - if (!orderBy._collate_argument.empty()) { - ss << " COLLATE " << orderBy._collate_argument; - } - switch (orderBy.asc_desc) { - case 1: - ss << " ASC"; - break; - case -1: - ss << " DESC"; - break; - } + ss << serialize(orderBy._expression, newContext); + seralize_collate(ss, orderBy); return ss.str(); } }; @@ -19021,30 +19025,22 @@ namespace sqlite_orm { std::string operator()(const statement_type& orderBy, const Ctx&) const { std::stringstream ss; ss << static_cast(orderBy) << " "; - int index = 0; + static constexpr std::array sep = {", ", ""}; +#ifdef SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED + for (bool first = true; const dynamic_order_by_entry_t& entry: orderBy) { + ss << sep[std::exchange(first, false)] << entry.name; + seralize_collate(ss, entry); + } +#else + bool first = true; for (const dynamic_order_by_entry_t& entry: orderBy) { - if (index > 0) { - ss << ", "; - } - - ss << entry.name; - if (!entry._collate_argument.empty()) { - ss << " COLLATE " << entry._collate_argument; - } - switch (entry.asc_desc) { - case 1: - ss << " ASC"; - break; - case -1: - ss << " DESC"; - break; - } - ++index; - }; + ss << sep[std::exchange(first, false)] << entry.name; + seralize_collate(ss, entry); + } +#endif return ss.str(); } }; - } } @@ -21140,14 +21136,12 @@ namespace sqlite_orm { } if (statement._order) { switch (statement._order) { - case -1: - ss << " DESC"; - break; case 1: ss << " ASC"; break; - default: - throw std::system_error{orm_error_code::incorrect_order}; + case -1: + ss << " DESC"; + break; } } return ss.str(); diff --git a/tests/schema/index_tests.cpp b/tests/schema/index_tests.cpp index f0a23485f..01328b3f1 100644 --- a/tests/schema/index_tests.cpp +++ b/tests/schema/index_tests.cpp @@ -14,6 +14,10 @@ TEST_CASE("index") { auto storage = make_storage({}, make_index("id_index", &User::id), table); REQUIRE_NOTHROW(storage.sync_schema()); } + SECTION("unspecified") { + auto storage = make_storage({}, make_index("id_index", indexed_column(&User::id)), table); + REQUIRE_NOTHROW(storage.sync_schema()); + } SECTION("asc") { auto storage = make_storage({}, make_index("id_index", indexed_column(&User::id).asc()), table); REQUIRE_NOTHROW(storage.sync_schema()); From 86cf6cd9f94102139b6092698cbf81291f5843c0 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 31 Jan 2025 10:05:14 +0200 Subject: [PATCH 057/170] appveyor: trying without C++23 build --- appveyor.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 0fb3f7dc3..e9ca0d7e4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,10 +20,10 @@ configuration: environment: appveyor_yml_disable_ps_linux: true matrix: - - job_name: Visual Studio 2022, x64, C++23 - appveyor_build_worker_image: Visual Studio 2022 - platform: x64 - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" + # - job_name: Visual Studio 2022, x64, C++23 + # appveyor_build_worker_image: Visual Studio 2022 + # platform: x64 + # SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" - job_name: clang, C++14 appveyor_build_worker_image: Ubuntu From d8d4064700cc43be2fa728cf419fcc20692f0ea3 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 31 Jan 2025 13:35:13 +0200 Subject: [PATCH 058/170] Corrected mismatching #ifdef --- dev/functional/mpl.h | 1 + include/sqlite_orm/sqlite_orm.h | 1 + 2 files changed, 2 insertions(+) diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index f69e44ae3..9f208920b 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -31,6 +31,7 @@ #ifndef _IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::is_same, std::negation, std::conjunction, std::disjunction #include +#endif #include "cxx_type_traits_polyfill.h" #include "mpl/conditional.h" diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 1ca613202..596390c3f 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -812,6 +812,7 @@ namespace sqlite_orm { #ifndef _IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::is_same, std::negation, std::conjunction, std::disjunction #include +#endif // #include "cxx_type_traits_polyfill.h" From db32c5d67c3f6c457da8c823c186ef141bfe19bb Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 2 Feb 2025 10:42:10 +0200 Subject: [PATCH 059/170] Remove named module UT file from regular unit test files --- tests/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 508642a9c..9a4bb16ec 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,6 +5,8 @@ FetchContent_MakeAvailable(Catch2) option(SQLITE_ORM_OMITS_CODECVT "Omits codec testing" OFF) +include(ucm) + # Consuming the standard library as C++ named module is a C++23 standard library feature and additionally requires support from build systems. # At the time of adding support for sqlite_orm as a C++ named module, CMake has still only experimental features to @@ -44,6 +46,7 @@ endif() # Glob all .cpp files recursively in the current directory file(GLOB_RECURSE UNIT_TEST_SOURCES "*.cpp") +ucm_remove_files(named_module.cpp FROM UNIT_TEST_SOURCES) add_executable(unit_tests ${UNIT_TEST_SOURCES}) From 8794196be9a840ac77bffa7b564cc1bcc51d5a8b Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Mon, 3 Feb 2025 17:59:45 +0200 Subject: [PATCH 060/170] Moved C++ named modules test under a dedicated subfolder --- tests/CMakeLists.txt | 4 ++-- tests/{ => named_module}/named_module.cpp | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/{ => named_module}/named_module.cpp (100%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9a4bb16ec..34ece366e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,7 +17,7 @@ include(ucm) # "Build ISO C++23 Standard Library Modules", MSBuild property `BuildStlModules`. if(SQLITE_ORM_ENABLE_CXX_23 AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.28.2 AND MSVC AND MSVC_VERSION GREATER_EQUAL 1930) add_executable(module_tests - named_module.cpp + named_module/named_module.cpp ) target_sources(module_tests @@ -46,7 +46,7 @@ endif() # Glob all .cpp files recursively in the current directory file(GLOB_RECURSE UNIT_TEST_SOURCES "*.cpp") -ucm_remove_files(named_module.cpp FROM UNIT_TEST_SOURCES) +ucm_remove_directories(named_module FROM UNIT_TEST_SOURCES) add_executable(unit_tests ${UNIT_TEST_SOURCES}) diff --git a/tests/named_module.cpp b/tests/named_module/named_module.cpp similarity index 100% rename from tests/named_module.cpp rename to tests/named_module/named_module.cpp From c5160226462dec1276579deb400fbe50a8730031 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 4 Feb 2025 00:33:12 +0200 Subject: [PATCH 061/170] Revert "appveyor: trying without C++23 build" This reverts commit 86cf6cd9f94102139b6092698cbf81291f5843c0. --- appveyor.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e9ca0d7e4..0fb3f7dc3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,10 +20,10 @@ configuration: environment: appveyor_yml_disable_ps_linux: true matrix: - # - job_name: Visual Studio 2022, x64, C++23 - # appveyor_build_worker_image: Visual Studio 2022 - # platform: x64 - # SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" + - job_name: Visual Studio 2022, x64, C++23 + appveyor_build_worker_image: Visual Studio 2022 + platform: x64 + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" - job_name: clang, C++14 appveyor_build_worker_image: Ubuntu From 1fd53b99475ccf517d35044f49a0a15afe3d53a8 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 4 Feb 2025 09:02:41 +0200 Subject: [PATCH 062/170] Renamed macros for named module export A leading underscore is reserved by the C++ Standard. --- .clang-format | 2 +- dev/alias.h | 4 +- dev/alias_traits.h | 6 +- dev/arg_values.h | 2 +- dev/arithmetic_tag.h | 4 +- dev/ast/excluded.h | 4 +- dev/ast/exists.h | 4 +- dev/ast/group_by.h | 4 +- dev/ast/into.h | 2 +- dev/ast/match.h | 4 +- dev/ast/rank.h | 2 +- dev/ast/set.h | 4 +- dev/ast/special_keywords.h | 2 +- dev/ast/upsert_clause.h | 4 +- dev/ast/where.h | 4 +- dev/ast_iterator.h | 2 +- dev/backup.h | 2 +- dev/carray.h | 20 +- dev/column_expression.h | 2 +- dev/column_names_getter.h | 2 +- dev/column_pointer.h | 4 +- dev/column_result.h | 2 +- dev/conditions.h | 8 +- dev/connection_holder.h | 2 +- dev/constraints.h | 4 +- dev/core_functions.h | 16 +- dev/cte_column_names_collector.h | 2 +- dev/cte_moniker.h | 4 +- dev/cte_storage.h | 2 +- dev/cte_types.h | 2 +- dev/default_value_extractor.h | 2 +- dev/eponymous_vtabs/dbstat.h | 4 +- dev/error_code.h | 16 +- dev/expression.h | 4 +- dev/expression_object_type.h | 2 +- dev/field_printer.h | 4 +- dev/function.h | 6 +- dev/functional/config.h | 6 +- dev/functional/cstring_literal.h | 2 +- dev/functional/cxx_functional_polyfill.h | 2 +- dev/functional/cxx_optional.h | 2 +- dev/functional/cxx_string_view.h | 2 +- dev/functional/cxx_tuple_polyfill.h | 2 +- dev/functional/cxx_type_traits_polyfill.h | 2 +- dev/functional/cxx_universal.h | 2 +- dev/functional/index_sequence_util.h | 2 +- dev/functional/mpl.h | 2 +- dev/functional/static_magic.h | 2 +- dev/get_prepared_statement.h | 26 +- dev/implementations/column_definitions.h | 2 +- dev/implementations/storage_definitions.h | 2 +- dev/implementations/table_definitions.h | 2 +- dev/indexed_column.h | 4 +- dev/is_base_of_template.h | 2 +- dev/is_std_ptr.h | 4 +- dev/journal_mode.h | 4 +- dev/limit_accessor.h | 2 +- dev/locking_mode.h | 4 +- dev/mapped_iterator.h | 2 +- dev/mapped_type_proxy.h | 2 +- dev/mapped_view.h | 2 +- dev/member_traits/member_traits.h | 2 +- dev/node_tuple.h | 2 +- dev/object_from_column_builder.h | 2 +- dev/operators.h | 4 +- dev/order_by_serializer.h | 2 +- dev/pointer_value.h | 40 +- dev/pragma.h | 2 +- dev/prepared_statement.h | 8 +- dev/result_set_iterator.h | 2 +- dev/result_set_view.h | 2 +- dev/row_extractor.h | 6 +- dev/rowid.h | 4 +- dev/schema/column.h | 9 +- dev/schema/index.h | 4 +- dev/schema/table.h | 4 +- dev/schema/triggers.h | 4 +- dev/select_constraints.h | 9 +- dev/serialize_result_type.h | 2 +- dev/serializing_util.h | 2 +- dev/sqlite_schema_table.h | 4 +- dev/statement_binder.h | 4 +- dev/statement_finalizer.h | 4 +- dev/statement_serializer.h | 2 +- dev/storage.h | 4 +- dev/storage_base.h | 2 +- dev/storage_impl.h | 2 +- dev/storage_lookup.h | 2 +- dev/storage_traits.h | 2 +- dev/sync_schema_result.h | 4 +- dev/table_info.h | 4 +- dev/table_name_collector.h | 2 +- dev/table_reference.h | 4 +- dev/table_type_of.h | 2 +- dev/transaction_guard.h | 2 +- dev/tuple_helper/same_or_void.h | 2 +- dev/tuple_helper/tuple_filter.h | 2 +- dev/tuple_helper/tuple_fy.h | 2 +- dev/tuple_helper/tuple_iteration.h | 2 +- dev/tuple_helper/tuple_transformer.h | 2 +- dev/type_is_nullable.h | 4 +- dev/type_printer.h | 4 +- dev/type_traits.h | 4 +- dev/udf_proxy.h | 2 +- dev/util.h | 4 +- dev/values.h | 4 +- dev/values_to_tuple.h | 2 +- dev/xdestroy_handling.h | 6 +- include/sqlite_orm/sqlite_orm.h | 440 +++++++++++----------- modules/sqlite_orm.cppm | 8 +- modules/sqlite_orm.cxx | 8 +- modules/sqlite_orm.ixx | 8 +- 112 files changed, 445 insertions(+), 461 deletions(-) diff --git a/.clang-format b/.clang-format index ccd4b5c73..558c0d0ad 100644 --- a/.clang-format +++ b/.clang-format @@ -118,7 +118,7 @@ SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 -AttributeMacros: [SQLITE_ORM_CPP_LIKELY, SQLITE_ORM_CPP_UNLIKELY] +AttributeMacros: [SQLITE_ORM_CPP_LIKELY, SQLITE_ORM_CPP_UNLIKELY, SQLITE_ORM_EXPORT] StatementMacros: - __pragma - _Pragma diff --git a/dev/alias.h b/dev/alias.h index ede2e3a87..3719c2498 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same #include // std::make_index_sequence, std::move #include // std::string @@ -189,7 +189,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Using a column pointer, create a column reference to an aliased table column. * diff --git a/dev/alias_traits.h b/dev/alias_traits.h index dc931f85a..8612c1875 100644 --- a/dev/alias_traits.h +++ b/dev/alias_traits.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::is_same #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include @@ -11,7 +11,7 @@ #include "type_traits.h" #include "table_reference.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Base class for a custom table alias, column alias or expression alias. */ @@ -71,7 +71,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template concept orm_alias = std::derived_from; diff --git a/dev/arg_values.h b/dev/arg_values.h index b8b34f233..2fb92ea4c 100644 --- a/dev/arg_values.h +++ b/dev/arg_values.h @@ -4,7 +4,7 @@ #include "row_extractor.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Wrapper around a dynamically typed value object. */ diff --git a/dev/arithmetic_tag.h b/dev/arithmetic_tag.h index c7fd0cec1..63b4a9f63 100644 --- a/dev/arithmetic_tag.h +++ b/dev/arithmetic_tag.h @@ -1,12 +1,12 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_integral #endif #include "functional/mpl/conditional.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper classes used by statement_binder and row_extractor. diff --git a/dev/ast/excluded.h b/dev/ast/excluded.h index 85d81427c..69bc40ae2 100644 --- a/dev/ast/excluded.h +++ b/dev/ast/excluded.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #endif @@ -16,7 +16,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::excluded_t excluded(T expression) { return {std::move(expression)}; diff --git a/dev/ast/exists.h b/dev/ast/exists.h index 65e653ed8..5efb10cc6 100644 --- a/dev/ast/exists.h +++ b/dev/ast/exists.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #endif @@ -21,7 +21,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * EXISTS(condition). * Example: storage.select(columns(&Agent::code, &Agent::name, &Agent::workingArea, &Agent::comission), diff --git a/dev/ast/group_by.h b/dev/ast/group_by.h index 8872312a9..9be1b015b 100644 --- a/dev/ast/group_by.h +++ b/dev/ast/group_by.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple #include // std::true_type, std::false_type #include // std::forward, std::move @@ -41,7 +41,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * GROUP BY column. * Example: storage.get_all(group_by(&Employee::name)) diff --git a/dev/ast/into.h b/dev/ast/into.h index f513e37f4..2e961be25 100644 --- a/dev/ast/into.h +++ b/dev/ast/into.h @@ -15,7 +15,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::into_t into() { return {}; diff --git a/dev/ast/match.h b/dev/ast/match.h index 48eadd677..2059c38c1 100644 --- a/dev/ast/match.h +++ b/dev/ast/match.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif @@ -17,7 +17,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::match_t match(X argument) { return {std::move(argument)}; diff --git a/dev/ast/rank.h b/dev/ast/rank.h index 02d5aef45..28387d0a6 100644 --- a/dev/ast/rank.h +++ b/dev/ast/rank.h @@ -6,7 +6,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::rank_t rank() { return {}; } diff --git a/dev/ast/set.h b/dev/ast/set.h index 0077f3570..aa9c20846 100644 --- a/dev/ast/set.h +++ b/dev/ast/set.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::tuple_size #include // std::string #include // std::vector @@ -89,7 +89,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * SET keyword used in UPDATE ... SET queries. * Args must have `assign_t` type. E.g. set(assign(&User::id, 5)) or set(c(&User::id) = 5) diff --git a/dev/ast/special_keywords.h b/dev/ast/special_keywords.h index 2d08e6822..efbcb82a2 100644 --- a/dev/ast/special_keywords.h +++ b/dev/ast/special_keywords.h @@ -8,7 +8,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::current_time_t current_time() { return {}; } diff --git a/dev/ast/upsert_clause.h b/dev/ast/upsert_clause.h index f757ab929..e877d8a35 100644 --- a/dev/ast/upsert_clause.h +++ b/dev/ast/upsert_clause.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3024000 #include // std::tuple #include // std::forward, std::move @@ -55,7 +55,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3024000 /** * ON CONFLICT upsert clause builder function. diff --git a/dev/ast/where.h b/dev/ast/where.h index ddf8ef731..3100d1716 100644 --- a/dev/ast/where.h +++ b/dev/ast/where.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move #endif @@ -39,7 +39,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * WHERE clause. Use it to add WHERE conditions wherever you like. * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index 5ae9ffccf..74bb5af6e 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::vector #include // std::reference_wrapper #endif diff --git a/dev/backup.h b/dev/backup.h index 0312e2120..a2fc533ea 100644 --- a/dev/backup.h +++ b/dev/backup.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::system_error #include // std::string #include diff --git a/dev/carray.h b/dev/carray.h index 244ab66af..f352cda76 100644 --- a/dev/carray.h +++ b/dev/carray.h @@ -6,7 +6,7 @@ * Hence we make it only available for compilers supporting inline variables. */ -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3020000 #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED #include // std::move @@ -21,7 +21,7 @@ #if SQLITE_VERSION_NUMBER >= 3020000 #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES inline constexpr orm_pointer_type auto carray_pointer_tag = "carray"_pointer_type; @@ -43,12 +43,12 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - carray_pointer_binding bind_carray_pointer(P * p, D d) noexcept { + carray_pointer_binding bind_carray_pointer(P* p, D d) noexcept { return bind_pointer(p, std::move(d)); } template - static_carray_pointer_binding

bind_carray_pointer_statically(P * p) noexcept { + static_carray_pointer_binding

bind_carray_pointer_statically(P* p) noexcept { return bind_pointer_statically(p); } @@ -60,14 +60,14 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { */ template [[deprecated("Use the better named function `bind_carray_pointer(...)`")]] carray_pointer_binding - bindable_carray_pointer(P * p, D d) noexcept { + bindable_carray_pointer(P* p, D d) noexcept { return bind_pointer(p, std::move(d)); } template [[deprecated( "Use the better named function `bind_carray_pointer_statically(...)` ")]] static_carray_pointer_binding

- statically_bindable_carray_pointer(P * p) noexcept { + statically_bindable_carray_pointer(P* p) noexcept { return bind_pointer_statically(p); } #else @@ -91,7 +91,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - carray_pointer_binding bind_carray_pointer(P * p, D d) noexcept { + carray_pointer_binding bind_carray_pointer(P* p, D d) noexcept { return bind_pointer(p, std::move(d)); } @@ -102,20 +102,20 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. */ template - static_carray_pointer_binding

bind_carray_pointer_statically(P * p) noexcept { + static_carray_pointer_binding

bind_carray_pointer_statically(P* p) noexcept { return bind_pointer_statically(p); } template [[deprecated("Use the better named function `bind_carray_pointer(...)`")]] carray_pointer_binding - bindable_carray_pointer(P * p, D d) noexcept { + bindable_carray_pointer(P* p, D d) noexcept { return bind_carray_pointer(p, std::move(d)); } template [[deprecated( "Use the better named function `bind_carray_pointer_statically(...)` ")]] static_carray_pointer_binding

- statically_bindable_carray_pointer(P * p) noexcept { + statically_bindable_carray_pointer(P* p) noexcept { return bind_carray_pointer_statically(p); } #endif diff --git a/dev/column_expression.h b/dev/column_expression.h index 2dea62f10..af4c41439 100644 --- a/dev/column_expression.h +++ b/dev/column_expression.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic #include // std::tuple #include // std::reference_wrapper diff --git a/dev/column_names_getter.h b/dev/column_names_getter.h index b23cf20c1..453c17ffe 100644 --- a/dev/column_names_getter.h +++ b/dev/column_names_getter.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of #include // std::string #include // std::vector diff --git a/dev/column_pointer.h b/dev/column_pointer.h index 028466442..720f3f0b6 100644 --- a/dev/column_pointer.h +++ b/dev/column_pointer.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_convertible #include // std::move #endif @@ -44,7 +44,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Explicitly refer to a column, used in contexts * where the automatic object mapping deduction needs to be overridden. diff --git a/dev/column_result.h b/dev/column_result.h index d62f49a74..7848e6330 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of #include // std::reference_wrapper #endif diff --git a/dev/conditions.h b/dev/conditions.h index abc0c7ede..1ac5b4584 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::enable_if, std::is_same, std::remove_const #include // std::vector @@ -813,7 +813,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Explicit FROM function. Usage: * `storage.select(&User::id, from());` @@ -1227,8 +1227,8 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * } */ template - internal::dynamic_order_by_t> dynamic_order_by( - const S& storage) { + internal::dynamic_order_by_t> + dynamic_order_by(const S& storage) { internal::serializer_context_builder builder(storage); return builder(); } diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 4efbd9cc7..dd281b4a7 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::string #endif diff --git a/dev/constraints.h b/dev/constraints.h index e80203f2a..cbb476eb8 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::false_type, std::true_type #include // std::system_error #include // std::ostream @@ -510,7 +510,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3031000 template internal::generated_always_t generated_always_as(T expression) { diff --git a/dev/core_functions.h b/dev/core_functions.h index 514f65377..6d65f41c0 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::make_tuple, std::tuple_size #include // std::forward, std::is_base_of, std::enable_if @@ -630,7 +630,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { using int64 = sqlite_int64; using uint64 = sqlite_uint64; @@ -1888,10 +1888,8 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * The return type is the type of the first argument. */ template - internal::built_in_function_t, internal::max_string, X, Y, Rest...> max( - X x, - Y y, - Rest... rest) { + internal::built_in_function_t, internal::max_string, X, Y, Rest...> + max(X x, Y y, Rest... rest) { return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; } @@ -1900,10 +1898,8 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * The return type is the type of the first argument. */ template - internal::built_in_function_t, internal::min_string, X, Y, Rest...> min( - X x, - Y y, - Rest... rest) { + internal::built_in_function_t, internal::min_string, X, Y, Rest...> + min(X x, Y y, Rest... rest) { return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; } diff --git a/dev/cte_column_names_collector.h b/dev/cte_column_names_collector.h index beb7a1c30..548ec1b2e 100644 --- a/dev/cte_column_names_collector.h +++ b/dev/cte_column_names_collector.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include #include diff --git a/dev/cte_moniker.h b/dev/cte_moniker.h index a053668ed..6b6b1434d 100644 --- a/dev/cte_moniker.h +++ b/dev/cte_moniker.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include @@ -67,7 +67,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { inline namespace literals { /** * cte_moniker<'n'> from a numeric literal. diff --git a/dev/cte_storage.h b/dev/cte_storage.h index d7d4475f4..6cdf53d5f 100644 --- a/dev/cte_storage.h +++ b/dev/cte_storage.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include #include diff --git a/dev/cte_types.h b/dev/cte_types.h index 9436d8af2..a28557b81 100644 --- a/dev/cte_types.h +++ b/dev/cte_types.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include #include diff --git a/dev/default_value_extractor.h b/dev/default_value_extractor.h index de4782345..0b38587cb 100644 --- a/dev/default_value_extractor.h +++ b/dev/default_value_extractor.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif diff --git a/dev/eponymous_vtabs/dbstat.h b/dev/eponymous_vtabs/dbstat.h index 03d6b06f6..538ad60fe 100644 --- a/dev/eponymous_vtabs/dbstat.h +++ b/dev/eponymous_vtabs/dbstat.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ENABLE_DBSTAT_VTAB #include // std::string #endif @@ -10,7 +10,7 @@ #include "../schema/table.h" #include "../column_pointer.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB struct dbstat { std::string name; diff --git a/dev/error_code.h b/dev/error_code.h index 4c0563d45..4830b85bd 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::error_code, std::system_error #include // std::string #include @@ -9,7 +9,7 @@ #include #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Enables classifying sqlite error codes. @@ -52,7 +52,7 @@ namespace std { struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { class orm_error_category : public std::error_category { public: @@ -136,7 +136,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - std::string get_error_message(sqlite3 * db, T && ... args) { + std::string get_error_message(sqlite3* db, T&&... args) { std::ostringstream stream; using unpack = int[]; (void)unpack{0, (stream << args, 0)...}; @@ -145,7 +145,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - [[noreturn]] void throw_error(sqlite3 * db, T && ... args) { + [[noreturn]] void throw_error(sqlite3* db, T&&... args) { throw std::system_error{sqlite_errc(sqlite3_errcode(db)), get_error_message(db, std::forward(args)...)}; } @@ -153,7 +153,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { return {sqlite_errc(ev)}; } - inline std::system_error sqlite_to_system_error(sqlite3 * db) { + inline std::system_error sqlite_to_system_error(sqlite3* db) { return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(db)}; } @@ -161,11 +161,11 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { throw sqlite_to_system_error(ev); } - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3 * db) { + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { throw sqlite_to_system_error(db); } - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt * stmt) { + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { throw sqlite_to_system_error(sqlite3_db_handle(stmt)); } } diff --git a/dev/expression.h b/dev/expression.h index 47bf092e1..edef774c5 100644 --- a/dev/expression.h +++ b/dev/expression.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::enable_if #include // std::move, std::forward, std::declval @@ -94,7 +94,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or * `storage.update(set(c(&User::name) = "Dua Lipa")); diff --git a/dev/expression_object_type.h b/dev/expression_object_type.h index 407e0ed57..def363183 100644 --- a/dev/expression_object_type.h +++ b/dev/expression_object_type.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::decay, std::remove_reference #include // std::reference_wrapper #endif diff --git a/dev/field_printer.h b/dev/field_printer.h index 17eb1146c..4ba6e2763 100644 --- a/dev/field_printer.h +++ b/dev/field_printer.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::stringstream #include // std::vector @@ -16,7 +16,7 @@ #include "is_std_ptr.h" #include "type_traits.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Is used to print members mapped to objects in storage_t::dump member function. diff --git a/dev/function.h b/dev/function.h index 6e1288f69..6f30ccff9 100644 --- a/dev/function.h +++ b/dev/function.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_member_function_pointer, std::is_function, std::remove_const, std::decay, std::is_convertible, std::is_same, std::false_type, std::true_type #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include // std::copy_constructible @@ -64,7 +64,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** @short Specifies that a type is a function signature (i.e. a function in the C++ type system). */ @@ -483,7 +483,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Call a user-defined function. * * Note: Currently the number of call arguments is checked and whether the types of pointer values match, diff --git a/dev/functional/config.h b/dev/functional/config.h index 1c022d3ec..2478ee0bc 100644 --- a/dev/functional/config.h +++ b/dev/functional/config.h @@ -2,10 +2,10 @@ #include "cxx_universal.h" -#ifdef _BUILD_SQLITE_ORM_MODULE -#define _EXPORT_SQLITE_ORM export +#ifdef BUILD_SQLITE_ORM_MODULE +#define SQLITE_ORM_EXPORT export #else -#define _EXPORT_SQLITE_ORM +#define SQLITE_ORM_EXPORT #endif #if SQLITE_ORM_HAS_INCLUDE() diff --git a/dev/functional/cstring_literal.h b/dev/functional/cstring_literal.h index 6651a86e1..9492bf662 100644 --- a/dev/functional/cstring_literal.h +++ b/dev/functional/cstring_literal.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include // std::index_sequence #include // std::copy_n diff --git a/dev/functional/cxx_functional_polyfill.h b/dev/functional/cxx_functional_polyfill.h index bc31a25eb..74ec1add6 100644 --- a/dev/functional/cxx_functional_polyfill.h +++ b/dev/functional/cxx_functional_polyfill.h @@ -1,6 +1,6 @@ #pragma once -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else #include diff --git a/dev/functional/cxx_optional.h b/dev/functional/cxx_optional.h index 3b8609766..7d64060e0 100644 --- a/dev/functional/cxx_optional.h +++ b/dev/functional/cxx_optional.h @@ -2,7 +2,7 @@ #include "cxx_core_features.h" -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else #if SQLITE_ORM_HAS_INCLUDE() diff --git a/dev/functional/cxx_string_view.h b/dev/functional/cxx_string_view.h index 733ce3a8e..b33f1290c 100644 --- a/dev/functional/cxx_string_view.h +++ b/dev/functional/cxx_string_view.h @@ -2,7 +2,7 @@ #include "cxx_core_features.h" -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else #if SQLITE_ORM_HAS_INCLUDE() diff --git a/dev/functional/cxx_tuple_polyfill.h b/dev/functional/cxx_tuple_polyfill.h index 35d281949..ebfcb6dcd 100644 --- a/dev/functional/cxx_tuple_polyfill.h +++ b/dev/functional/cxx_tuple_polyfill.h @@ -1,6 +1,6 @@ #pragma once -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else #include // std::apply; std::tuple_size diff --git a/dev/functional/cxx_type_traits_polyfill.h b/dev/functional/cxx_type_traits_polyfill.h index 537aafb8b..2a45c458d 100644 --- a/dev/functional/cxx_type_traits_polyfill.h +++ b/dev/functional/cxx_type_traits_polyfill.h @@ -1,6 +1,6 @@ #pragma once -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else #include diff --git a/dev/functional/cxx_universal.h b/dev/functional/cxx_universal.h index 779293954..85f1682cd 100644 --- a/dev/functional/cxx_universal.h +++ b/dev/functional/cxx_universal.h @@ -10,7 +10,7 @@ */ #include // alternative operator representations -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // sqlite_orm is using ::size_t, ::ptrdiff_t, std::nullptr_t everywhere, pull it in early // earlier libcxx versions didn't make std::nullptr_t available in the global namespace via stddef.h, // though it should have according to C++ documentation (see https://en.cppreference.com/w/cpp/types/std::nullptr_t#Notes). diff --git a/dev/functional/index_sequence_util.h b/dev/functional/index_sequence_util.h index eaa7faf5f..dc2d2da03 100644 --- a/dev/functional/index_sequence_util.h +++ b/dev/functional/index_sequence_util.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence #endif diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index 9f208920b..f2d196dee 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -28,7 +28,7 @@ * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). */ -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::is_same, std::negation, std::conjunction, std::disjunction #include #endif diff --git a/dev/functional/static_magic.h b/dev/functional/static_magic.h index 5b8c92938..d9ec3c8cd 100644 --- a/dev/functional/static_magic.h +++ b/dev/functional/static_magic.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED #include // std::false_type, std::true_type, std::integral_constant #endif diff --git a/dev/get_prepared_statement.h b/dev/get_prepared_statement.h index cb17cc4ee..dabf61b56 100644 --- a/dev/get_prepared_statement.h +++ b/dev/get_prepared_statement.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_same, std::remove_reference, std::remove_cvref #include // std::get #endif @@ -13,10 +13,10 @@ #include "node_tuple.h" #include "expression_object_type.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return std::get(statement.expression.range); } @@ -26,7 +26,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return std::get(statement.expression.range); } @@ -36,7 +36,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -46,7 +46,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -57,7 +57,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -68,7 +68,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -78,7 +78,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); return internal::get_ref(statement.expression.object); } @@ -90,7 +90,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.expression.obj); } @@ -102,7 +102,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); return internal::get_ref(statement.expression.object); } @@ -114,7 +114,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.expression.object); } @@ -151,7 +151,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t & statement) { + auto& get(internal::prepared_statement_t& statement) { using statement_type = std::remove_reference_t; using expression_type = internal::expression_type_t; using node_tuple = internal::node_tuple_t; diff --git a/dev/implementations/column_definitions.h b/dev/implementations/column_definitions.h index 6d4ab347d..329fadc44 100644 --- a/dev/implementations/column_definitions.h +++ b/dev/implementations/column_definitions.h @@ -4,7 +4,7 @@ */ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::make_unique #endif diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index b01ca23f7..78e4302e6 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -4,7 +4,7 @@ */ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_same #include // std::stringstream #include // std::flush diff --git a/dev/implementations/table_definitions.h b/dev/implementations/table_definitions.h index 157dd2805..ae8dd0a59 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -4,7 +4,7 @@ */ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference #include // std::move #include // std::find_if, std::ranges::find diff --git a/dev/indexed_column.h b/dev/indexed_column.h index 8c9860288..f7d471219 100644 --- a/dev/indexed_column.h +++ b/dev/indexed_column.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move #endif @@ -55,7 +55,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Use this function to specify indexed column inside `make_index` function call. * Example: make_index("index_name", indexed_column(&User::id).asc()) diff --git a/dev/is_base_of_template.h b/dev/is_base_of_template.h index 96ffa74cc..6a77d7e51 100644 --- a/dev/is_base_of_template.h +++ b/dev/is_base_of_template.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::declval #endif diff --git a/dev/is_std_ptr.h b/dev/is_std_ptr.h index cd9b78e06..998cbdef8 100644 --- a/dev/is_std_ptr.h +++ b/dev/is_std_ptr.h @@ -1,11 +1,11 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Specialization for optional type (std::shared_ptr / std::unique_ptr). diff --git a/dev/journal_mode.h b/dev/journal_mode.h index 8f2f09030..fe799b8d1 100644 --- a/dev/journal_mode.h +++ b/dev/journal_mode.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string #include // std::pair @@ -16,7 +16,7 @@ #undef DELETE #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Caps case because of: diff --git a/dev/limit_accessor.h b/dev/limit_accessor.h index f5f3fda84..e52f84488 100644 --- a/dev/limit_accessor.h +++ b/dev/limit_accessor.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::map #include // std::function #include // std::shared_ptr diff --git a/dev/locking_mode.h b/dev/locking_mode.h index 945781e4a..00c575e4b 100644 --- a/dev/locking_mode.h +++ b/dev/locking_mode.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string #include // std::pair @@ -10,7 +10,7 @@ #include "serialize_result_type.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { enum class locking_mode : signed char { NORMAL = 0, EXCLUSIVE = 1, diff --git a/dev/mapped_iterator.h b/dev/mapped_iterator.h index 05b2b5170..9f527eea2 100644 --- a/dev/mapped_iterator.h +++ b/dev/mapped_iterator.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::shared_ptr, std::make_shared #include // std::move #include // std::input_iterator_tag diff --git a/dev/mapped_type_proxy.h b/dev/mapped_type_proxy.h index 3e58e045e..a043daf65 100644 --- a/dev/mapped_type_proxy.h +++ b/dev/mapped_type_proxy.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const #endif diff --git a/dev/mapped_view.h b/dev/mapped_view.h index 7fc2fd6c2..bd56901eb 100644 --- a/dev/mapped_view.h +++ b/dev/mapped_view.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::forward, std::move #endif diff --git a/dev/member_traits/member_traits.h b/dev/member_traits/member_traits.h index 28bfd958c..9265b4d21 100644 --- a/dev/member_traits/member_traits.h +++ b/dev/member_traits/member_traits.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_function, std::true_type, std::false_type #endif diff --git a/dev/node_tuple.h b/dev/node_tuple.h index 74b1ab172..b576c511a 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if #include // std::tuple #include // std::pair diff --git a/dev/object_from_column_builder.h b/dev/object_from_column_builder.h index d46790365..76d776492 100644 --- a/dev/object_from_column_builder.h +++ b/dev/object_from_column_builder.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_member_object_pointer #include // std::move #endif diff --git a/dev/operators.h b/dev/operators.h index 9a50be3c9..9244e3153 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move #endif @@ -213,7 +213,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT * name || '@gmail.com' FROM users diff --git a/dev/order_by_serializer.h b/dev/order_by_serializer.h index 66b500fff..a2fb6f1e4 100644 --- a/dev/order_by_serializer.h +++ b/dev/order_by_serializer.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::string #include // std::stringstream diff --git a/dev/pointer_value.h b/dev/pointer_value.h index 665ae018e..f44017030 100644 --- a/dev/pointer_value.h +++ b/dev/pointer_value.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3020000 #include #include @@ -27,7 +27,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES inline namespace literals { template @@ -190,7 +190,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #endif } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Wrap a pointer, its type and its deleter function for binding it to a statement. * @@ -199,7 +199,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - auto bind_pointer(P * p, D d) noexcept -> pointer_binding { + auto bind_pointer(P* p, D d) noexcept -> pointer_binding { return {p, std::move(d)}; } @@ -209,27 +209,25 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto bind_pointer(typename B::qualified_type * p, typename B::deleter_type d = {}) noexcept -> B { + auto bind_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept -> B { return B{p, std::move(d)}; } template - [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding bindable_pointer( - P * p, - D d) noexcept { + [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding + bindable_pointer(P* p, D d) noexcept { return bind_pointer(p, std::move(d)); } template - [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding bindable_pointer( - std::unique_ptr p) noexcept { + [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding + bindable_pointer(std::unique_ptr p) noexcept { return bind_pointer(p.release(), p.get_deleter()); } template - [[deprecated("Use the better named function `bind_pointer(...)`")]] B bindable_pointer( - typename B::qualified_type * p, - typename B::deleter_type d = {}) noexcept { + [[deprecated("Use the better named function `bind_pointer(...)`")]] B + bindable_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept { return bind_pointer(p, std::move(d)); } @@ -242,7 +240,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - auto bind_pointer(P * p, D d) noexcept -> pointer_binding { + auto bind_pointer(P* p, D d) noexcept -> pointer_binding { return {p, std::move(d)}; } @@ -259,26 +257,26 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. */ template - auto bind_pointer_statically(P * p) noexcept -> static_pointer_binding { + auto bind_pointer_statically(P* p) noexcept -> static_pointer_binding { return bind_pointer(p, null_xdestroy_f); } template - B bind_pointer_statically(typename B::qualified_type * p, + B bind_pointer_statically(typename B::qualified_type* p, typename B::deleter_type* /*exposition*/ = nullptr) noexcept { return bind_pointer(p); } template [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] static_pointer_binding - statically_bindable_pointer(P * p) noexcept { + statically_bindable_pointer(P* p) noexcept { return bind_pointer(p, null_xdestroy_f); } template - [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] B statically_bindable_pointer( - typename B::qualified_type * p, - typename B::deleter_type* /*exposition*/ = nullptr) noexcept { + [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] B + statically_bindable_pointer(typename B::qualified_type* p, + typename B::deleter_type* /*exposition*/ = nullptr) noexcept { return bind_pointer(p); } @@ -290,7 +288,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. */ template - auto bind_pointer_statically(P * p) noexcept -> static_pointer_binding { + auto bind_pointer_statically(P* p) noexcept -> static_pointer_binding { return bind_pointer(p, null_xdestroy_f); } #endif diff --git a/dev/pragma.h b/dev/pragma.h index 00e4b17e1..d1868fae2 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::function #include // std::shared_ptr diff --git a/dev/prepared_statement.h b/dev/prepared_statement.h index 098e5279b..a828f3ea5 100644 --- a/dev/prepared_statement.h +++ b/dev/prepared_statement.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr #include // std::iterator_traits #include // std::string @@ -318,7 +318,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::insert_constraint or_rollback() { return {internal::conflict_action::rollback}; } @@ -746,7 +746,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { template>, class... Args> - auto get_all(Args && ... conditions) { + auto get_all(Args&&... conditions) { return get_all, R>(std::forward(conditions)...); } #endif @@ -816,7 +816,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { template>, class... Args> - auto get_all_optional(Args && ... conditions) { + auto get_all_optional(Args&&... conditions) { return get_all_optional, R>(std::forward(conditions)...); } #endif diff --git a/dev/result_set_iterator.h b/dev/result_set_iterator.h index 7e01db7d4..ef4e35deb 100644 --- a/dev/result_set_iterator.h +++ b/dev/result_set_iterator.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #include // std::input_iterator_tag, std::default_sentinel_t #include // std::reference_wrapper diff --git a/dev/result_set_view.h b/dev/result_set_view.h index 58749230b..371489bbc 100644 --- a/dev/result_set_view.h +++ b/dev/result_set_view.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move, std::remove_cvref #include // std::reference_wrapper #if defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) && \ diff --git a/dev/row_extractor.h b/dev/row_extractor.h index f897f1abf..078088bc4 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if #include // ::atof, ::atoi, ::atoll #include // ::strlen @@ -32,7 +32,7 @@ #include "is_std_ptr.h" #include "type_traits.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper for casting values originating from SQL to C++ typed values, usually from rows of a result set. @@ -122,7 +122,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template int extract_single_value(void* data, int argc, char** argv, char**) { auto& res = *(R*)data; diff --git a/dev/rowid.h b/dev/rowid.h index 6d52ea5fd..d77bed8c9 100644 --- a/dev/rowid.h +++ b/dev/rowid.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -43,7 +43,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::rowid_t rowid() { return {}; } diff --git a/dev/schema/column.h b/dev/schema/column.h index 08c7ab88c..4a4f8470b 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple #include // std::string #include // std::unique_ptr @@ -145,14 +145,13 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Factory function for a column definition from a member object pointer of the object to be mapped. */ template = true> - internal::column_t make_column(std::string name, - M memberPointer, - Op... constraints) { + internal::column_t + make_column(std::string name, M memberPointer, Op... constraints) { static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, diff --git a/dev/schema/index.h b/dev/schema/index.h index 685cdc7e9..770ef402f 100644 --- a/dev/schema/index.h +++ b/dev/schema/index.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t #include // std::string #include // std::forward @@ -35,7 +35,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::index_t()))...> make_index(std::string name, Cols... cols) { diff --git a/dev/schema/table.h b/dev/schema/table.h index deec970b7..27826dc9c 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#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::vector @@ -419,7 +419,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3009000 template>::object_type> internal::using_fts5_t using_fts5(Cs... columns) { diff --git a/dev/schema/triggers.h b/dev/schema/triggers.h index 571a27b00..2873116cf 100644 --- a/dev/schema/triggers.h +++ b/dev/schema/triggers.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include #include @@ -223,7 +223,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * NEW.expression function used within TRIGGER expressions */ diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 376dc3faf..f1048f6db 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include #endif @@ -411,7 +411,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template internal::as_optional_t as_optional(T value) { @@ -685,9 +685,8 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * @note The use of RECURSIVE does not force common table expressions to be recursive. */ template = true> - internal::with_t, CTEs...> with_recursive( - internal::common_table_expressions ctes, - Compound sel) { + internal::with_t, CTEs...> + with_recursive(internal::common_table_expressions ctes, Compound sel) { return {true, std::move(ctes), sqlite_orm::select(std::move(sel))}; } diff --git a/dev/serialize_result_type.h b/dev/serialize_result_type.h index 271b5a868..41a361c15 100644 --- a/dev/serialize_result_type.h +++ b/dev/serialize_result_type.h @@ -1,7 +1,7 @@ #pragma once #include "functional/cxx_string_view.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED #include // std::string #endif diff --git a/dev/serializing_util.h b/dev/serializing_util.h index f879c51b2..c7aa5e0de 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence, std::remove_cvref #include #include diff --git a/dev/sqlite_schema_table.h b/dev/sqlite_schema_table.h index f1829d670..1dd48dab5 100644 --- a/dev/sqlite_schema_table.h +++ b/dev/sqlite_schema_table.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -9,7 +9,7 @@ #include "column_pointer.h" #include "alias.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * SQLite's "schema table" that stores the schema for a database. * diff --git a/dev/statement_binder.h b/dev/statement_binder.h index 237c86c05..5ce7b949e 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence #include // std::default_delete #include // std::string, std::wstring @@ -27,7 +27,7 @@ #include "xdestroy_handling.h" #include "pointer_value.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper class used for binding fields to sqlite3 statements. diff --git a/dev/statement_finalizer.h b/dev/statement_finalizer.h index 8a72c469e..1de4575ff 100644 --- a/dev/statement_finalizer.h +++ b/dev/statement_finalizer.h @@ -1,12 +1,12 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr #include // std::integral_constant #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Guard class which finalizes `sqlite3_stmt` in dtor diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 4d1fed752..0d9f48bcb 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::remove_pointer, std::remove_reference, std::remove_cvref, std::disjunction #include // std::stringstream #include // std::string diff --git a/dev/storage.h b/dev/storage.h index 55890665f..b9bedbf82 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr/shared_ptr, std::make_unique #include // std::system_error #include // std::string @@ -1705,7 +1705,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /* * Factory function for a storage, from a database file and a bunch of database object definitions. */ diff --git a/dev/storage_base.h b/dev/storage_base.h index bb4f3f2f4..cfc0638ad 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // atoi #include // std::allocator #include // std::function, std::bind, std::bind_front diff --git a/dev/storage_impl.h b/dev/storage_impl.h index 4bfe257c9..81537fdcc 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif diff --git a/dev/storage_lookup.h b/dev/storage_lookup.h index 84dc939a4..2388db0c2 100644 --- a/dev/storage_lookup.h +++ b/dev/storage_lookup.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#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::index_sequence, std::make_index_sequence diff --git a/dev/storage_traits.h b/dev/storage_traits.h index f5e8df93e..7794d59f5 100644 --- a/dev/storage_traits.h +++ b/dev/storage_traits.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple #endif diff --git a/dev/sync_schema_result.h b/dev/sync_schema_result.h index c7cc02ec7..0f0e59c8f 100644 --- a/dev/sync_schema_result.h +++ b/dev/sync_schema_result.h @@ -1,10 +1,10 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { enum class sync_schema_result { diff --git a/dev/table_info.h b/dev/table_info.h index d1206a5bc..21a7a95bd 100644 --- a/dev/table_info.h +++ b/dev/table_info.h @@ -1,11 +1,11 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { struct table_info { int cid = 0; diff --git a/dev/table_name_collector.h b/dev/table_name_collector.h index 3b5134534..fd0939410 100644 --- a/dev/table_name_collector.h +++ b/dev/table_name_collector.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::set #include // std::string #include // std::pair, std::move diff --git a/dev/table_reference.h b/dev/table_reference.h index 72b45e9ca..aa968f4f5 100644 --- a/dev/table_reference.h +++ b/dev/table_reference.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const, std::type_identity #endif @@ -37,7 +37,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. * diff --git a/dev/table_type_of.h b/dev/table_type_of.h index 16262bfdd..050a23022 100644 --- a/dev/table_type_of.h +++ b/dev/table_type_of.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_convertible #endif diff --git a/dev/transaction_guard.h b/dev/transaction_guard.h index 895e75746..380886478 100644 --- a/dev/transaction_guard.h +++ b/dev/transaction_guard.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::function #include // std::move #endif diff --git a/dev/tuple_helper/same_or_void.h b/dev/tuple_helper/same_or_void.h index 5979b856a..002b7f581 100644 --- a/dev/tuple_helper/same_or_void.h +++ b/dev/tuple_helper/same_or_void.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::common_type #endif diff --git a/dev/tuple_helper/tuple_filter.h b/dev/tuple_helper/tuple_filter.h index e0f8b99f8..116c6dfb0 100644 --- a/dev/tuple_helper/tuple_filter.h +++ b/dev/tuple_helper/tuple_filter.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant, std::index_sequence, std::conditional, std::declval #include // std::tuple, std::tuple_cat, std::tuple_element #endif diff --git a/dev/tuple_helper/tuple_fy.h b/dev/tuple_helper/tuple_fy.h index 0001a2710..ceaee0124 100644 --- a/dev/tuple_helper/tuple_fy.h +++ b/dev/tuple_helper/tuple_fy.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index 701a7632d..e3f83ef75 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::get, std::tuple_element, std::tuple_size #include // std::index_sequence, std::make_index_sequence #include // std::forward, std::move diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index e119e9d62..55d940d5a 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval #include // std::tuple_size, std::get #endif diff --git a/dev/type_is_nullable.h b/dev/type_is_nullable.h index 5bed52890..1847f697f 100644 --- a/dev/type_is_nullable.h +++ b/dev/type_is_nullable.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type, std::enable_if #include // std::shared_ptr, std::unique_ptr #endif @@ -8,7 +8,7 @@ #include "functional/cxx_type_traits_polyfill.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * This is class that tells `sqlite_orm` that type is nullable. Nullable types diff --git a/dev/type_printer.h b/dev/type_printer.h index 5eb9083de..fe3c38779 100644 --- a/dev/type_printer.h +++ b/dev/type_printer.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::shared_ptr, std::unique_ptr #include // std::vector @@ -11,7 +11,7 @@ #include "type_traits.h" #include "is_std_ptr.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) diff --git a/dev/type_traits.h b/dev/type_traits.h index 3ef322268..345f445d6 100644 --- a/dev/type_traits.h +++ b/dev/type_traits.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::is_empty, std::is_aggregate #if __cpp_lib_unwrap_ref >= 201811L #include // std::reference_wrapper @@ -140,7 +140,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED template concept orm_names_type = requires { typename T::type; }; diff --git a/dev/udf_proxy.h b/dev/udf_proxy.h index 293ca3a00..73710ee21 100644 --- a/dev/udf_proxy.h +++ b/dev/udf_proxy.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // assert macro #include // std::true_type, std::false_type #include // std::bad_alloc diff --git a/dev/util.h b/dev/util.h index 51b2258bf..cff59dfc5 100644 --- a/dev/util.h +++ b/dev/util.h @@ -1,14 +1,14 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move #endif #include "error_code.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Escape the provided character in the given string by doubling it. diff --git a/dev/values.h b/dev/values.h index b2335d8df..07891788b 100644 --- a/dev/values.h +++ b/dev/values.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::vector #include // std::tuple #include // std::forward, std::move @@ -33,7 +33,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::values_t values(Args... args) { return {{std::forward(args)...}}; diff --git a/dev/values_to_tuple.h b/dev/values_to_tuple.h index 1c0eeaf29..e6e4be907 100644 --- a/dev/values_to_tuple.h +++ b/dev/values_to_tuple.h @@ -1,7 +1,7 @@ #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::index_sequence, std::make_index_sequence #include // std::tuple, std::tuple_size, std::tuple_element #endif diff --git a/dev/xdestroy_handling.h b/dev/xdestroy_handling.h index 1d1650c85..c659d29c5 100644 --- a/dev/xdestroy_handling.h +++ b/dev/xdestroy_handling.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include @@ -9,7 +9,7 @@ #include "functional/cxx_type_traits_polyfill.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { using xdestroy_fn_t = void (*)(void*); using null_xdestroy_t = std::integral_constant; @@ -174,7 +174,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED /** diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 596390c3f..0cb210dba 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -23,7 +23,7 @@ __pragma(push_macro("max")) */ #include // alternative operator representations -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // sqlite_orm is using ::size_t, ::ptrdiff_t, std::nullptr_t everywhere, pull it in early // earlier libcxx versions didn't make std::nullptr_t available in the global namespace via stddef.h, // though it should have according to C++ documentation (see https://en.cppreference.com/w/cpp/types/std::nullptr_t#Notes). @@ -205,10 +205,10 @@ using std::nullptr_t; #define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS #endif -#ifdef _BUILD_SQLITE_ORM_MODULE -#define _EXPORT_SQLITE_ORM export +#ifdef BUILD_SQLITE_ORM_MODULE +#define SQLITE_ORM_EXPORT export #else -#define _EXPORT_SQLITE_ORM +#define SQLITE_ORM_EXPORT #endif #if SQLITE_ORM_HAS_INCLUDE() @@ -296,7 +296,7 @@ namespace sqlite_orm { #pragma once #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr/shared_ptr, std::make_unique #include // std::system_error #include // std::string @@ -314,7 +314,7 @@ namespace sqlite_orm { // #include "cxx_core_features.h" -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else #if SQLITE_ORM_HAS_INCLUDE() @@ -328,7 +328,7 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else #include @@ -519,7 +519,7 @@ namespace sqlite_orm { // #include "functional/cxx_functional_polyfill.h" -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else #include @@ -533,7 +533,7 @@ namespace sqlite_orm { // #include "../member_traits/member_traits.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_function, std::true_type, std::false_type #endif @@ -699,7 +699,7 @@ namespace sqlite_orm { // #include "functional/static_magic.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED #include // std::false_type, std::true_type, std::integral_constant #endif @@ -809,7 +809,7 @@ namespace sqlite_orm { * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). */ -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::is_same, std::negation, std::conjunction, std::disjunction #include #endif @@ -1364,7 +1364,7 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_filter.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant, std::index_sequence, std::conditional, std::declval #include // std::tuple, std::tuple_cat, std::tuple_element #endif @@ -1373,7 +1373,7 @@ namespace sqlite_orm { // #include "../functional/index_sequence_util.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence #endif @@ -1531,7 +1531,7 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_transformer.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval #include // std::tuple_size, std::get #endif @@ -1650,7 +1650,7 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_iteration.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::get, std::tuple_element, std::tuple_size #include // std::index_sequence, std::make_index_sequence #include // std::forward, std::move @@ -1740,7 +1740,7 @@ namespace sqlite_orm { // #include "type_traits.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::is_empty, std::is_aggregate #if __cpp_lib_unwrap_ref >= 201811L #include // std::reference_wrapper @@ -1881,7 +1881,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED template concept orm_names_type = requires { typename T::type; }; @@ -1890,7 +1890,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "alias.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same #include // std::make_index_sequence, std::move #include // std::string @@ -1906,7 +1906,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "functional/cstring_literal.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include // std::index_sequence #include // std::copy_n @@ -1946,7 +1946,7 @@ namespace sqlite_orm::internal { // #include "alias_traits.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::is_same #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include @@ -1959,7 +1959,7 @@ namespace sqlite_orm::internal { // #include "table_reference.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const, std::type_identity #endif @@ -1998,7 +1998,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. @@ -2014,7 +2014,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #endif } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Base class for a custom table alias, column alias or expression alias. */ @@ -2074,7 +2074,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template concept orm_alias = std::derived_from; @@ -2134,7 +2134,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "table_type_of.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_convertible #endif @@ -2232,7 +2232,7 @@ namespace sqlite_orm { // #include "column_pointer.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_convertible #include // std::move #endif @@ -2281,7 +2281,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Explicitly refer to a column, used in contexts * where the automatic object mapping deduction needs to be overridden. @@ -2572,7 +2572,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Using a column pointer, create a column reference to an aliased table column. * @@ -2881,7 +2881,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "error_code.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::error_code, std::system_error #include // std::string #include @@ -2889,7 +2889,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #include #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Enables classifying sqlite error codes. @@ -2932,7 +2932,7 @@ namespace std { struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { class orm_error_category : public std::error_category { public: @@ -3016,7 +3016,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - std::string get_error_message(sqlite3 * db, T && ... args) { + std::string get_error_message(sqlite3* db, T&&... args) { std::ostringstream stream; using unpack = int[]; (void)unpack{0, (stream << args, 0)...}; @@ -3025,7 +3025,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - [[noreturn]] void throw_error(sqlite3 * db, T && ... args) { + [[noreturn]] void throw_error(sqlite3* db, T&&... args) { throw std::system_error{sqlite_errc(sqlite3_errcode(db)), get_error_message(db, std::forward(args)...)}; } @@ -3033,7 +3033,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { return {sqlite_errc(ev)}; } - inline std::system_error sqlite_to_system_error(sqlite3 * db) { + inline std::system_error sqlite_to_system_error(sqlite3* db) { return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(db)}; } @@ -3041,18 +3041,18 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { throw sqlite_to_system_error(ev); } - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3 * db) { + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { throw sqlite_to_system_error(db); } - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt * stmt) { + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { throw sqlite_to_system_error(sqlite3_db_handle(stmt)); } } // #include "type_printer.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::shared_ptr, std::unique_ptr #include // std::vector @@ -3065,12 +3065,12 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "is_std_ptr.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Specialization for optional type (std::shared_ptr / std::unique_ptr). @@ -3097,7 +3097,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { }; } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) @@ -3171,7 +3171,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "constraints.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::false_type, std::true_type #include // std::system_error #include // std::ostream @@ -3185,7 +3185,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "tuple_helper/same_or_void.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::common_type #endif @@ -3750,7 +3750,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3031000 template internal::generated_always_t generated_always_as(T expression) { @@ -3888,7 +3888,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "field_printer.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::stringstream #include // std::vector @@ -3907,7 +3907,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "type_traits.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Is used to print members mapped to objects in storage_t::dump member function. @@ -4063,7 +4063,7 @@ namespace sqlite_orm { // #include "rowid.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -4106,7 +4106,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::rowid_t rowid() { return {}; } @@ -4137,7 +4137,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "operators.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move #endif @@ -4146,7 +4146,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "is_base_of_template.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::declval #endif @@ -4193,7 +4193,7 @@ namespace sqlite_orm { // #include "cxx_core_features.h" -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else #if SQLITE_ORM_HAS_INCLUDE() @@ -4205,7 +4205,7 @@ namespace sqlite_orm { #define SQLITE_ORM_STRING_VIEW_SUPPORTED #endif -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED #include // std::string #endif @@ -4426,7 +4426,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT * name || '@gmail.com' FROM users @@ -4516,7 +4516,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "select_constraints.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include #endif @@ -4573,7 +4573,7 @@ namespace sqlite_orm { // #include "ast/where.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move #endif @@ -4613,7 +4613,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * WHERE clause. Use it to add WHERE conditions wherever you like. * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc @@ -4631,7 +4631,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "ast/group_by.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple #include // std::true_type, std::false_type #include // std::forward, std::move @@ -4672,7 +4672,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * GROUP BY column. * Example: storage.get_all(group_by(&Employee::name)) @@ -4685,7 +4685,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "core_functions.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::make_tuple, std::tuple_size #include // std::forward, std::is_base_of, std::enable_if @@ -4703,7 +4703,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "conditions.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::enable_if, std::is_same, std::remove_const #include // std::vector @@ -4774,7 +4774,7 @@ namespace sqlite_orm { // #include "expression.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::enable_if #include // std::move, std::forward, std::declval @@ -4870,7 +4870,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or * `storage.update(set(c(&User::name) = "Dua Lipa")); @@ -5691,7 +5691,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Explicit FROM function. Usage: * `storage.select(&User::id, from());` @@ -6105,8 +6105,8 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * } */ template - internal::dynamic_order_by_t> dynamic_order_by( - const S& storage) { + internal::dynamic_order_by_t> + dynamic_order_by(const S& storage) { internal::serializer_context_builder builder(storage); return builder(); } @@ -6182,7 +6182,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::into_t into() { return {}; @@ -6800,7 +6800,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { using int64 = sqlite_int64; using uint64 = sqlite_uint64; @@ -8058,10 +8058,8 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * The return type is the type of the first argument. */ template - internal::built_in_function_t, internal::max_string, X, Y, Rest...> max( - X x, - Y y, - Rest... rest) { + internal::built_in_function_t, internal::max_string, X, Y, Rest...> + max(X x, Y y, Rest... rest) { return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; } @@ -8070,10 +8068,8 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * The return type is the type of the first argument. */ template - internal::built_in_function_t, internal::min_string, X, Y, Rest...> min( - X x, - Y y, - Rest... rest) { + internal::built_in_function_t, internal::min_string, X, Y, Rest...> + min(X x, Y y, Rest... rest) { return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; } @@ -8344,7 +8340,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "cte_moniker.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include @@ -8412,7 +8408,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { inline namespace literals { /** * cte_moniker<'n'> from a numeric literal. @@ -8439,7 +8435,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "schema/column.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple #include // std::string #include // std::unique_ptr @@ -8459,7 +8455,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "../type_is_nullable.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type, std::enable_if #include // std::shared_ptr, std::unique_ptr #endif @@ -8467,7 +8463,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * This is class that tells `sqlite_orm` that type is nullable. Nullable types @@ -8631,14 +8627,13 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Factory function for a column definition from a member object pointer of the object to be mapped. */ template = true> - internal::column_t make_column(std::string name, - M memberPointer, - Op... constraints) { + internal::column_t + make_column(std::string name, M memberPointer, Op... constraints) { static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, @@ -9073,7 +9068,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template internal::as_optional_t as_optional(T value) { @@ -9347,9 +9342,8 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * @note The use of RECURSIVE does not force common table expressions to be recursive. */ template = true> - internal::with_t, CTEs...> with_recursive( - internal::common_table_expressions ctes, - Compound sel) { + internal::with_t, CTEs...> + with_recursive(internal::common_table_expressions ctes, Compound sel) { return {true, std::move(ctes), sqlite_orm::select(std::move(sel))}; } @@ -9456,7 +9450,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "statement_binder.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence #include // std::default_delete #include // std::string, std::wstring @@ -9487,13 +9481,13 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "arithmetic_tag.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_integral #endif // #include "functional/mpl/conditional.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper classes used by statement_binder and row_extractor. @@ -9513,7 +9507,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "xdestroy_handling.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include @@ -9522,7 +9516,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { using xdestroy_fn_t = void (*)(void*); using null_xdestroy_t = std::integral_constant; @@ -9687,7 +9681,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED /** @@ -9765,7 +9759,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "pointer_value.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3020000 #include #include @@ -9793,7 +9787,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES inline namespace literals { template @@ -9956,7 +9950,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #endif } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Wrap a pointer, its type and its deleter function for binding it to a statement. * @@ -9965,7 +9959,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - auto bind_pointer(P * p, D d) noexcept -> pointer_binding { + auto bind_pointer(P* p, D d) noexcept -> pointer_binding { return {p, std::move(d)}; } @@ -9975,27 +9969,25 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto bind_pointer(typename B::qualified_type * p, typename B::deleter_type d = {}) noexcept -> B { + auto bind_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept -> B { return B{p, std::move(d)}; } template - [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding bindable_pointer( - P * p, - D d) noexcept { + [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding + bindable_pointer(P* p, D d) noexcept { return bind_pointer(p, std::move(d)); } template - [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding bindable_pointer( - std::unique_ptr p) noexcept { + [[deprecated("Use the better named function `bind_pointer(...)`")]] pointer_binding + bindable_pointer(std::unique_ptr p) noexcept { return bind_pointer(p.release(), p.get_deleter()); } template - [[deprecated("Use the better named function `bind_pointer(...)`")]] B bindable_pointer( - typename B::qualified_type * p, - typename B::deleter_type d = {}) noexcept { + [[deprecated("Use the better named function `bind_pointer(...)`")]] B + bindable_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept { return bind_pointer(p, std::move(d)); } @@ -10008,7 +10000,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - auto bind_pointer(P * p, D d) noexcept -> pointer_binding { + auto bind_pointer(P* p, D d) noexcept -> pointer_binding { return {p, std::move(d)}; } @@ -10025,26 +10017,26 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. */ template - auto bind_pointer_statically(P * p) noexcept -> static_pointer_binding { + auto bind_pointer_statically(P* p) noexcept -> static_pointer_binding { return bind_pointer(p, null_xdestroy_f); } template - B bind_pointer_statically(typename B::qualified_type * p, + B bind_pointer_statically(typename B::qualified_type* p, typename B::deleter_type* /*exposition*/ = nullptr) noexcept { return bind_pointer(p); } template [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] static_pointer_binding - statically_bindable_pointer(P * p) noexcept { + statically_bindable_pointer(P* p) noexcept { return bind_pointer(p, null_xdestroy_f); } template - [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] B statically_bindable_pointer( - typename B::qualified_type * p, - typename B::deleter_type* /*exposition*/ = nullptr) noexcept { + [[deprecated("Use the better named function `bind_pointer_statically(...)`")]] B + statically_bindable_pointer(typename B::qualified_type* p, + typename B::deleter_type* /*exposition*/ = nullptr) noexcept { return bind_pointer(p); } @@ -10056,7 +10048,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. */ template - auto bind_pointer_statically(P * p) noexcept -> static_pointer_binding { + auto bind_pointer_statically(P* p) noexcept -> static_pointer_binding { return bind_pointer(p, null_xdestroy_f); } #endif @@ -10071,7 +10063,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper class used for binding fields to sqlite3 statements. @@ -10418,7 +10410,7 @@ namespace sqlite_orm { // #include "column_result.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of #include // std::reference_wrapper #endif @@ -10431,7 +10423,7 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_fy.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif @@ -10465,7 +10457,7 @@ namespace sqlite_orm { // #include "mapped_type_proxy.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const #endif @@ -10554,7 +10546,7 @@ namespace sqlite_orm { // #include "cte_types.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include #include @@ -10620,7 +10612,7 @@ namespace sqlite_orm { // #include "storage_traits.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple #endif @@ -10634,7 +10626,7 @@ namespace sqlite_orm { // #include "storage_lookup.h" -#ifndef _IMPORT_STD_MODULE +#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::index_sequence, std::make_index_sequence @@ -10840,7 +10832,7 @@ namespace sqlite_orm { // #include "function.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_member_function_pointer, std::is_function, std::remove_const, std::decay, std::is_convertible, std::is_same, std::false_type, std::true_type #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #include // std::copy_constructible @@ -10992,7 +10984,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** @short Specifies that a type is a function signature (i.e. a function in the C++ type system). */ @@ -11411,7 +11403,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Call a user-defined function. * * Note: Currently the number of call arguments is checked and whether the types of pointer values match, @@ -11484,7 +11476,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::current_time_t current_time() { return {}; } @@ -11826,11 +11818,11 @@ namespace sqlite_orm { // #include "sync_schema_result.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { enum class sync_schema_result { @@ -11890,12 +11882,12 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "table_info.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { struct table_info { int cid = 0; @@ -11942,7 +11934,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "storage_impl.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -11966,7 +11958,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "schema/table.h" -#ifndef _IMPORT_STD_MODULE +#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::vector @@ -12021,7 +12013,7 @@ namespace sqlite_orm { // #include "index.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t #include // std::string #include // std::forward @@ -12031,7 +12023,7 @@ namespace sqlite_orm { // #include "../indexed_column.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move #endif @@ -12086,7 +12078,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Use this function to specify indexed column inside `make_index` function call. * Example: make_index("index_name", indexed_column(&User::id).asc()) @@ -12124,7 +12116,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::index_t()))...> make_index(std::string name, Cols... cols) { @@ -12553,7 +12545,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3009000 template>::object_type> internal::using_fts5_t using_fts5(Cs... columns) { @@ -12731,7 +12723,7 @@ namespace sqlite_orm { // #include "journal_mode.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string #include // std::pair @@ -12747,7 +12739,7 @@ namespace sqlite_orm { #undef DELETE #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Caps case because of: @@ -12823,7 +12815,7 @@ namespace sqlite_orm { // #include "mapped_view.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::forward, std::move #endif @@ -12831,7 +12823,7 @@ namespace sqlite_orm { // #include "row_extractor.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if #include // ::atof, ::atoi, ::atoll #include // ::strlen @@ -12866,7 +12858,7 @@ namespace sqlite_orm { // #include "locking_mode.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string #include // std::pair @@ -12876,7 +12868,7 @@ namespace sqlite_orm { // #include "serialize_result_type.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { enum class locking_mode : signed char { NORMAL = 0, EXCLUSIVE = 1, @@ -12931,7 +12923,7 @@ namespace sqlite_orm { // #include "type_traits.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Helper for casting values originating from SQL to C++ typed values, usually from rows of a result set. @@ -13021,7 +13013,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template int extract_single_value(void* data, int argc, char** argv, char**) { auto& res = *(R*)data; @@ -13489,7 +13481,7 @@ namespace sqlite_orm { // #include "mapped_iterator.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::shared_ptr, std::make_shared #include // std::move #include // std::input_iterator_tag @@ -13500,12 +13492,12 @@ namespace sqlite_orm { // #include "statement_finalizer.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr #include // std::integral_constant #endif -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Guard class which finalizes `sqlite3_stmt` in dtor @@ -13519,7 +13511,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "object_from_column_builder.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_member_object_pointer #include // std::move #endif @@ -13607,14 +13599,14 @@ namespace sqlite_orm { // #include "util.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move #endif // #include "error_code.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * Escape the provided character in the given string by doubling it. @@ -13845,7 +13837,7 @@ namespace sqlite_orm { // #include "ast_iterator.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::vector #include // std::reference_wrapper #endif @@ -13867,7 +13859,7 @@ namespace sqlite_orm { // #include "prepared_statement.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr #include // std::iterator_traits #include // std::string @@ -13885,7 +13877,7 @@ namespace sqlite_orm { // #include "connection_holder.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::string #endif @@ -13971,7 +13963,7 @@ namespace sqlite_orm { // #include "values.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::vector #include // std::tuple #include // std::forward, std::move @@ -14004,7 +13996,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::values_t values(Args... args) { return {{std::forward(args)...}}; @@ -14022,7 +14014,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "ast/upsert_clause.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3024000 #include // std::tuple #include // std::forward, std::move @@ -14077,7 +14069,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3024000 /** * ON CONFLICT upsert clause builder function. @@ -14100,7 +14092,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "ast/set.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::tuple_size #include // std::string #include // std::vector @@ -14112,7 +14104,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "../table_name_collector.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::set #include // std::string #include // std::pair, std::move @@ -14300,7 +14292,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * SET keyword used in UPDATE ... SET queries. * Args must have `assign_t` type. E.g. set(assign(&User::id, 5)) or set(c(&User::id) = 5) @@ -14621,7 +14613,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::insert_constraint or_rollback() { return {internal::conflict_action::rollback}; } @@ -15049,7 +15041,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { template>, class... Args> - auto get_all(Args && ... conditions) { + auto get_all(Args&&... conditions) { return get_all, R>(std::forward(conditions)...); } #endif @@ -15119,7 +15111,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { template>, class... Args> - auto get_all_optional(Args && ... conditions) { + auto get_all_optional(Args&&... conditions) { return get_all_optional, R>(std::forward(conditions)...); } #endif @@ -15131,7 +15123,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "ast/excluded.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #endif @@ -15147,7 +15139,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::excluded_t excluded(T expression) { return {std::move(expression)}; @@ -15164,7 +15156,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "ast/exists.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #endif @@ -15185,7 +15177,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * EXISTS(condition). * Example: storage.select(columns(&Agent::code, &Agent::name, &Agent::workingArea, &Agent::comission), @@ -15204,7 +15196,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "ast/match.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif @@ -15221,7 +15213,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::match_t match(X argument) { return {std::move(argument)}; @@ -15994,7 +15986,7 @@ inline constexpr bool std::ranges::enable_borrowed_range -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move, std::remove_cvref #include // std::reference_wrapper #if defined(SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED) && defined(SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED) && \ @@ -16010,7 +16002,7 @@ inline constexpr bool std::ranges::enable_borrowed_range -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #include // std::input_iterator_tag, std::default_sentinel_t #include // std::reference_wrapper @@ -16178,7 +16170,7 @@ inline constexpr bool std::ranges::enable_borrowed_range -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // atoi #include // std::allocator #include // std::function, std::bind, std::bind_front @@ -16197,7 +16189,7 @@ inline constexpr bool std::ranges::enable_borrowed_range #else #include // std::apply; std::tuple_size @@ -16239,7 +16231,7 @@ namespace sqlite_orm { // #include "pragma.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::function #include // std::shared_ptr @@ -16262,7 +16254,7 @@ namespace sqlite_orm { // #include "serializing_util.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence, std::remove_cvref #include #include @@ -16962,7 +16954,7 @@ namespace sqlite_orm { // #include "limit_accessor.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::map #include // std::function #include // std::shared_ptr @@ -17103,7 +17095,7 @@ namespace sqlite_orm { // #include "transaction_guard.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::function #include // std::move #endif @@ -17191,7 +17183,7 @@ namespace sqlite_orm { // #include "backup.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::system_error #include // std::string #include @@ -17270,7 +17262,7 @@ namespace sqlite_orm { // #include "values_to_tuple.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::index_sequence, std::make_index_sequence #include // std::tuple, std::tuple_size, std::tuple_element #endif @@ -17287,7 +17279,7 @@ namespace sqlite_orm { // #include "row_extractor.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Wrapper around a dynamically typed value object. */ @@ -17472,7 +17464,7 @@ namespace sqlite_orm { // #include "udf_proxy.h" #include -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // assert macro #include // std::true_type, std::false_type #include // std::bad_alloc @@ -18705,7 +18697,7 @@ namespace sqlite_orm { // #include "expression_object_type.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::decay, std::remove_reference #include // std::reference_wrapper #endif @@ -18824,7 +18816,7 @@ namespace sqlite_orm { // #include "statement_serializer.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::remove_pointer, std::remove_reference, std::remove_cvref, std::disjunction #include // std::stringstream #include // std::string @@ -18867,7 +18859,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { inline internal::rank_t rank() { return {}; } @@ -18903,7 +18895,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "column_names_getter.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of #include // std::string #include // std::vector @@ -19045,7 +19037,7 @@ namespace sqlite_orm { // #include "cte_column_names_collector.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include #include @@ -19272,7 +19264,7 @@ namespace sqlite_orm { // #include "order_by_serializer.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::string #include // std::stringstream @@ -19365,7 +19357,7 @@ namespace sqlite_orm { // #include "schema/triggers.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include #include @@ -19588,7 +19580,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * NEW.expression function used within TRIGGER expressions */ @@ -21965,7 +21957,7 @@ namespace sqlite_orm { // #include "cte_storage.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include #include @@ -21992,7 +21984,7 @@ namespace sqlite_orm { // #include "column_expression.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic #include // std::tuple #include // std::reference_wrapper @@ -24062,7 +24054,7 @@ namespace sqlite_orm { } } -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /* * Factory function for a storage, from a database file and a bunch of database object definitions. */ @@ -24090,7 +24082,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * this file is also used to provide definitions of interface methods 'hitting the database'. */ -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::make_unique #endif @@ -24100,7 +24092,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "../default_value_extractor.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -24159,7 +24151,7 @@ namespace sqlite_orm { * this file is also used to provide definitions of interface methods 'hitting the database'. */ -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference #include // std::move #include // std::find_if, std::ranges::find @@ -24234,7 +24226,7 @@ namespace sqlite_orm { * e.g. usage of the dbstat table. */ -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_same #include // std::stringstream #include // std::flush @@ -24246,7 +24238,7 @@ namespace sqlite_orm { // #include "../sqlite_schema_table.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -24258,7 +24250,7 @@ namespace sqlite_orm { // #include "alias.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { /** * SQLite's "schema table" that stores the schema for a database. * @@ -24297,7 +24289,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "../eponymous_vtabs/dbstat.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ENABLE_DBSTAT_VTAB #include // std::string #endif @@ -24309,7 +24301,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { // #include "../column_pointer.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB struct dbstat { std::string name; @@ -24486,7 +24478,7 @@ namespace sqlite_orm { #pragma once -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_same, std::remove_reference, std::remove_cvref #include // std::get #endif @@ -24503,7 +24495,7 @@ namespace sqlite_orm { // #include "node_tuple.h" -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if #include // std::tuple #include // std::pair @@ -24780,10 +24772,10 @@ namespace sqlite_orm { // #include "expression_object_type.h" -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return std::get(statement.expression.range); } @@ -24793,7 +24785,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return std::get(statement.expression.range); } @@ -24803,7 +24795,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -24813,7 +24805,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -24824,7 +24816,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -24835,7 +24827,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { return internal::get_ref(std::get(statement.expression.ids)); } @@ -24845,7 +24837,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); return internal::get_ref(statement.expression.object); } @@ -24857,7 +24849,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.expression.obj); } @@ -24869,7 +24861,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); return internal::get_ref(statement.expression.object); } @@ -24881,7 +24873,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t> & statement) { + auto& get(internal::prepared_statement_t>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); return internal::get_ref(statement.expression.object); } @@ -24918,7 +24910,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { } template - auto& get(internal::prepared_statement_t & statement) { + auto& get(internal::prepared_statement_t& statement) { using statement_type = std::remove_reference_t; using expression_type = internal::expression_type_t; using node_tuple = internal::node_tuple_t; @@ -24951,7 +24943,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * Hence we make it only available for compilers supporting inline variables. */ -#ifndef _IMPORT_STD_MODULE +#ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3020000 #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED #include // std::move @@ -24966,7 +24958,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3020000 #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED -_EXPORT_SQLITE_ORM namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES inline constexpr orm_pointer_type auto carray_pointer_tag = "carray"_pointer_type; @@ -24988,12 +24980,12 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - carray_pointer_binding bind_carray_pointer(P * p, D d) noexcept { + carray_pointer_binding bind_carray_pointer(P* p, D d) noexcept { return bind_pointer(p, std::move(d)); } template - static_carray_pointer_binding

bind_carray_pointer_statically(P * p) noexcept { + static_carray_pointer_binding

bind_carray_pointer_statically(P* p) noexcept { return bind_pointer_statically(p); } @@ -25005,14 +24997,14 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { */ template [[deprecated("Use the better named function `bind_carray_pointer(...)`")]] carray_pointer_binding - bindable_carray_pointer(P * p, D d) noexcept { + bindable_carray_pointer(P* p, D d) noexcept { return bind_pointer(p, std::move(d)); } template [[deprecated( "Use the better named function `bind_carray_pointer_statically(...)` ")]] static_carray_pointer_binding

- statically_bindable_carray_pointer(P * p) noexcept { + statically_bindable_carray_pointer(P* p) noexcept { return bind_pointer_statically(p); } #else @@ -25036,7 +25028,7 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * the deleter when the statement finishes. */ template - carray_pointer_binding bind_carray_pointer(P * p, D d) noexcept { + carray_pointer_binding bind_carray_pointer(P* p, D d) noexcept { return bind_pointer(p, std::move(d)); } @@ -25047,20 +25039,20 @@ _EXPORT_SQLITE_ORM namespace sqlite_orm { * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. */ template - static_carray_pointer_binding

bind_carray_pointer_statically(P * p) noexcept { + static_carray_pointer_binding

bind_carray_pointer_statically(P* p) noexcept { return bind_pointer_statically(p); } template [[deprecated("Use the better named function `bind_carray_pointer(...)`")]] carray_pointer_binding - bindable_carray_pointer(P * p, D d) noexcept { + bindable_carray_pointer(P* p, D d) noexcept { return bind_carray_pointer(p, std::move(d)); } template [[deprecated( "Use the better named function `bind_carray_pointer_statically(...)` ")]] static_carray_pointer_binding

- statically_bindable_carray_pointer(P * p) noexcept { + statically_bindable_carray_pointer(P* p) noexcept { return bind_carray_pointer_statically(p); } #endif diff --git a/modules/sqlite_orm.cppm b/modules/sqlite_orm.cppm index 386bac954..fb3929fae 100644 --- a/modules/sqlite_orm.cppm +++ b/modules/sqlite_orm.cppm @@ -1,17 +1,17 @@ // In a module-file, the optional `module;` must appear first; see [cpp.pre]. module; -#define _BUILD_SQLITE_ORM_MODULE -#define _IMPORT_STD_MODULE +#define BUILD_SQLITE_ORM_MODULE +#define SQLITE_ORM_IMPORT_STD_MODULE -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE // Including assert.h when using `import std;` doesn't work with msvc (as it leads to multiple defined symbols) (see sqlite_orm.ixx), hence we include it here #include // assert macro #endif export module sqlite_orm; -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE import std.compat; #endif diff --git a/modules/sqlite_orm.cxx b/modules/sqlite_orm.cxx index a52d5dd5e..7a116389b 100644 --- a/modules/sqlite_orm.cxx +++ b/modules/sqlite_orm.cxx @@ -1,17 +1,17 @@ // In a module-file, the optional `module;` must appear first; see [cpp.pre]. module; -#define _BUILD_SQLITE_ORM_MODULE -#define _IMPORT_STD_MODULE +#define BUILD_SQLITE_ORM_MODULE +#define SQLITE_ORM_IMPORT_STD_MODULE -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE // Including assert.h when using `import std;` doesn't work with msvc (as it leads to multiple defined symbols) (see sqlite_orm.ixx), hence we include it here #include // assert macro #endif export module sqlite_orm; -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE import std.compat; #endif diff --git a/modules/sqlite_orm.ixx b/modules/sqlite_orm.ixx index 5be51158b..3c830e5a7 100644 --- a/modules/sqlite_orm.ixx +++ b/modules/sqlite_orm.ixx @@ -1,17 +1,17 @@ // In a module-file, the optional `module;` must appear first; see [cpp.pre]. module; -#define _BUILD_SQLITE_ORM_MODULE -#define _IMPORT_STD_MODULE +#define BUILD_SQLITE_ORM_MODULE +#define SQLITE_ORM_IMPORT_STD_MODULE -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE // Including assert.h when using `import std;` below doesn't work (as it leads to multiple defined symbols), hence we include it here #include // assert macro #endif export module sqlite_orm; -#ifdef _IMPORT_STD_MODULE +#ifdef SQLITE_ORM_IMPORT_STD_MODULE import std.compat; #endif From fbf6e14a181422ca76b41eb5ee8cb93a1de0d13e Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 5 Feb 2025 15:08:31 +0200 Subject: [PATCH 063/170] Improved connection reference counting Accurate memory access order appropriate for reference counting, just like for shared pointers. Therefore, instead of establishing a global order among threads we can rely on the more light-weight memory access ordering. Also: * Reset the connection pointer after successfully closing the database. * Improved (internal) comments for better understanding. --- dev/connection_holder.h | 35 ++++++++++++++++++++++----------- include/sqlite_orm/sqlite_orm.h | 35 ++++++++++++++++++++++----------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 11e6dd0a7..0b67f5f82 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -7,43 +7,54 @@ #include "error_code.h" namespace sqlite_orm { - namespace internal { struct connection_holder { - - connection_holder(std::string filename_) : filename(std::move(filename_)) {} + connection_holder(std::string filename) : filename(std::move(filename)) {} void retain() { - if (1 == ++this->_retain_count) { - auto rc = sqlite3_open(this->filename.c_str(), &this->db); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + // first one opens the connection. + // we presume that the connection is opened once in a single-threaded context [also open forever]. + // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. + if (this->_retain_count.fetch_add(1, std::memory_order_relaxed) == 0) { + int rc = sqlite3_open(this->filename.c_str(), &this->db); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { + throw_translated_sqlite_error(this->db); } } } void release() { - if (0 == --this->_retain_count) { - auto rc = sqlite3_close(this->db); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + // last one closes the connection. + // we assume that this might happen by any thread, therefore the counter must serve as a synchronization point. + if (this->_retain_count.fetch_sub(1, std::memory_order_acq_rel) == 1) { + int rc = sqlite3_close(this->db); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY { + throw_translated_sqlite_error(this->db); + } else { + this->db = nullptr; } } } sqlite3* get() const { + // note: ensuring a valid DB handle was already memory ordered with `retain()` return this->db; } + /** + * @attention While retrieving the reference count value is atomic it makes only sense at single-threaded points in code. + */ int retain_count() const { - return this->_retain_count; + return this->_retain_count.load(std::memory_order_relaxed); } const std::string filename; protected: sqlite3* db = nullptr; + + private: std::atomic_int _retain_count{}; }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index cbd8695b6..26b33f731 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13649,43 +13649,54 @@ namespace sqlite_orm { // #include "error_code.h" namespace sqlite_orm { - namespace internal { struct connection_holder { - - connection_holder(std::string filename_) : filename(std::move(filename_)) {} + connection_holder(std::string filename) : filename(std::move(filename)) {} void retain() { - if (1 == ++this->_retain_count) { - auto rc = sqlite3_open(this->filename.c_str(), &this->db); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + // first one opens the connection. + // we presume that the connection is opened once in a single-threaded context [also open forever]. + // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. + if (this->_retain_count.fetch_add(1, std::memory_order_relaxed) == 0) { + int rc = sqlite3_open(this->filename.c_str(), &this->db); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { + throw_translated_sqlite_error(this->db); } } } void release() { - if (0 == --this->_retain_count) { - auto rc = sqlite3_close(this->db); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + // last one closes the connection. + // we assume that this might happen by any thread, therefore the counter must serve as a synchronization point. + if (this->_retain_count.fetch_sub(1, std::memory_order_acq_rel) == 1) { + int rc = sqlite3_close(this->db); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY { + throw_translated_sqlite_error(this->db); + } else { + this->db = nullptr; } } } sqlite3* get() const { + // note: ensuring a valid DB handle was already memory ordered with `retain()` return this->db; } + /** + * @attention While retrieving the reference count value is atomic it makes only sense at single-threaded points in code. + */ int retain_count() const { - return this->_retain_count; + return this->_retain_count.load(std::memory_order_relaxed); } const std::string filename; protected: sqlite3* db = nullptr; + + private: std::atomic_int _retain_count{}; }; From 96232895dadd2f108119a1ad74a93cdaf0e4ba5e Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 5 Feb 2025 18:00:46 +0200 Subject: [PATCH 064/170] Corrected newline endings of file cstring_literal.h --- dev/functional/cstring_literal.h | 6 +++--- include/sqlite_orm/sqlite_orm.h | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/dev/functional/cstring_literal.h b/dev/functional/cstring_literal.h index 9492bf662..b14904213 100644 --- a/dev/functional/cstring_literal.h +++ b/dev/functional/cstring_literal.h @@ -9,9 +9,9 @@ #ifdef SQLITE_ORM_WITH_CPP20_ALIASES namespace sqlite_orm::internal { - /* - * Wraps a C string of fixed size. - * Its main purpose is to enable the user-defined string literal operator template. + /* + * Wraps a C string of fixed size. + * Its main purpose is to enable the user-defined string literal operator template. */ template struct cstring_literal { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 0cb210dba..fcd358c15 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1916,11 +1916,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES namespace sqlite_orm::internal { /* - * Wraps a C string of fixed size. - * Its main purpose is to enable the user-defined string literal operator template. - */ template struct cstring_literal { From dedbf10701c59e9567d3719258d875381056c18b Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 16 Feb 2025 15:25:20 +0200 Subject: [PATCH 065/170] Database connection control options * Introduced a connection control struct as an aggregate of options. This opens the way for various options and modes to establish a database connection. * An 'on open' callback can now be specified in a declarative way to the `make_storage()` call. This finally solves the issue that in-memory databases didn't call the 'on open' callback. * Reordered members of the connection holder. This will be important for upcoming multi-threading improvements, avoiding false L1 cache sharing. --- dev/connection_holder.h | 36 ++- dev/functional/cxx_core_features.h | 5 +- dev/functional/mpl.h | 14 +- dev/pragma.h | 9 +- dev/storage.h | 67 ++++- dev/storage_base.h | 77 ++--- dev/storage_options.h | 50 ++++ dev/tuple_helper/tuple_transformer.h | 20 +- include/sqlite_orm/sqlite_orm.h | 276 ++++++++++++++---- tests/filename.cpp | 6 +- .../functional/tuple_transform.cpp | 2 + tests/storage_tests.cpp | 43 +++ 12 files changed, 483 insertions(+), 122 deletions(-) create mode 100644 dev/storage_options.h diff --git a/dev/connection_holder.h b/dev/connection_holder.h index fad35c96a..1544ffdcc 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -3,6 +3,7 @@ #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include +#include // std::function #include // std::string #endif @@ -12,25 +13,38 @@ namespace sqlite_orm { namespace internal { struct connection_holder { - connection_holder(std::string filename) : filename(std::move(filename)) {} + connection_holder(std::string filename, std::function onAfterOpen) : + _onAfterOpen{std::move(onAfterOpen)}, filename(std::move(filename)) {} + + connection_holder(const connection_holder&) = delete; + + connection_holder(const connection_holder& other, std::function onAfterOpen) : + _onAfterOpen{std::move(onAfterOpen)}, filename{other.filename} {} void retain() { // first one opens the connection. // we presume that the connection is opened once in a single-threaded context [also open forever]. // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. - if (this->_retain_count.fetch_add(1, std::memory_order_relaxed) == 0) { - int rc = sqlite3_open(this->filename.c_str(), &this->db); + if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { + int rc = sqlite3_open_v2(this->filename.c_str(), + &this->db, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + nullptr); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { throw_translated_sqlite_error(this->db); } } + + if (_onAfterOpen) { + _onAfterOpen(this->db); + } } void release() { // last one closes the connection. // we assume that this might happen by any thread, therefore the counter must serve as a synchronization point. - if (this->_retain_count.fetch_sub(1, std::memory_order_acq_rel) == 1) { - int rc = sqlite3_close(this->db); + if (_retainCount.fetch_sub(1, std::memory_order_acq_rel) == 1) { + int rc = sqlite3_close_v2(this->db); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY { throw_translated_sqlite_error(this->db); } else { @@ -48,16 +62,20 @@ namespace sqlite_orm { * @attention While retrieving the reference count value is atomic it makes only sense at single-threaded points in code. */ int retain_count() const { - return this->_retain_count.load(std::memory_order_relaxed); + return _retainCount.load(std::memory_order_relaxed); } - const std::string filename; - protected: sqlite3* db = nullptr; private: - std::atomic_int _retain_count{}; + std::atomic_int _retainCount{}; + + private: + const std::function _onAfterOpen; + + public: + const std::string filename; }; struct connection_ref { diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index 93977a4f2..7bb4d2a06 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -49,9 +49,12 @@ #define SQLITE_ORM_STRUCTURED_BINDINGS_SUPPORTED #endif +#if __cpp_deduction_guides >= 201703L +#define SQLITE_ORM_CTAD_SUPPORTED +#endif + #if __cpp_generic_lambdas >= 201707L #define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED -#else #endif #if __cpp_init_captures >= 201803L diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index f2d196dee..108f3ccd6 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -453,7 +453,7 @@ namespace sqlite_orm { * Commonly used named abbreviation for `check_if`. */ template - using check_if_is_type = mpl::bind_front_fn; + using check_if_is_type = check_if; /* * Quoted trait metafunction that checks if a type's template matches the specified template @@ -463,6 +463,18 @@ namespace sqlite_orm { using check_if_is_template = mpl::pass_extracted_fn_to>>; + /* + * Quoted trait metafunction that checks if a type names a nested type determined by `Op`. + */ + template class Op> + using check_if_names = mpl::bind_front_higherorder_fn; + + /* + * Quoted trait metafunction that checks if a type does not name a nested type determined by `Op`. + */ + template class Op> + using check_if_lacks = mpl::not_>; + /* * Quoted metafunction that finds the index of the given type in a tuple. */ diff --git a/dev/pragma.h b/dev/pragma.h index d1868fae2..aef4dc9e4 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -258,11 +258,12 @@ namespace sqlite_orm { } void set_pragma_impl(const std::string& query, sqlite3* db = nullptr) { - auto con = this->get_connection(); - if (db == nullptr) { - db = con.get(); + if (db) { + perform_void_exec(db, query); + } else { + auto con = this->get_connection(); + perform_void_exec(con.get(), query); } - perform_void_exec(db, query); } }; } diff --git a/dev/storage.h b/dev/storage.h index b9bedbf82..04011a9b8 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -81,21 +81,39 @@ namespace sqlite_orm { polyfill::void_t().prepare(std::declval()))>>> = true; + template + decltype(auto) storage_opt_or_default(OptionsTpl& options) { +#ifdef SQLITE_ORM_CTAD_SUPPORTED + if constexpr (tuple_has_type::value) { + return std::move(std::get(options)); + } else { + return Opt{}; + } +#else + return Opt{}; +#endif + } + /** * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` * function. */ template struct storage_t : storage_base { - using self = storage_t; + using self_type = storage_t; using db_objects_type = db_objects_tuple; /** * @param filename database filename. * @param dbObjects db_objects_tuple */ - storage_t(std::string filename, db_objects_type dbObjects) : - storage_base{std::move(filename), foreign_keys_count(dbObjects)}, db_objects{std::move(dbObjects)} {} + template + storage_t(std::string filename, db_objects_type dbObjects, OptionsTpl options) : + storage_base{std::move(filename), + storage_opt_or_default(options), + storage_opt_or_default(options), + foreign_keys_count(dbObjects)}, + db_objects{std::move(dbObjects)} {} storage_t(const storage_t&) = default; @@ -114,7 +132,7 @@ namespace sqlite_orm { * * Hence, friend was replaced by `obtain_db_objects()` and `pick_const_impl()`. */ - friend const db_objects_type& obtain_db_objects(const self& storage) noexcept { + friend const db_objects_type& obtain_db_objects(const self_type& storage) noexcept { return storage.db_objects; } @@ -246,7 +264,7 @@ namespace sqlite_orm { public: template, class... Args> - mapped_view iterate(Args&&... args) { + mapped_view iterate(Args&&... args) { this->assert_mapped_type(); auto con = this->get_connection(); @@ -781,7 +799,7 @@ namespace sqlite_orm { std::enable_if_t::value && !is_mapped::value, bool> = true> std::string dump(E&& expression, bool parametrized = false) const { - static_assert(is_preparable_v, "Expression must be a high-level statement"); + static_assert(is_preparable_v, "Expression must be a high-level statement"); decltype(auto) e2 = static_if::value>( [](auto expression) -> auto { @@ -1702,17 +1720,50 @@ namespace sqlite_orm { } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED }; // struct storage_t + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + template + using dbo_index_sequence = filter_tuple_sequence_t::template fn>; + + template + using opt_index_sequence = filter_tuple_sequence_t::template fn>; + + template + storage_t make_storage(std::string filename, std::tuple dbObjects, OptionsTpl options) { + return {std::move(filename), std::move(dbObjects), std::move(options)}; + } +#endif } } SQLITE_ORM_EXPORT namespace sqlite_orm { +#ifdef SQLITE_ORM_CTAD_SUPPORTED + /* + * Factory function for a storage instance, from a database file, a set of database object definitions + * and option storage options like connection control options and an 'on open' callback. + * + * E.g. + * auto storage = make_storage("", connection_control{.open_forever = true}, on_open([](sqlite3* db) {})); + */ + template + auto make_storage(std::string filename, Spec... specifications) { + using namespace ::sqlite_orm::internal; + + std::tuple specTuple{std::forward(specifications)...}; + return internal::make_storage( + std::move(filename), + create_from_tuple(std::move(specTuple), dbo_index_sequence{}), + create_from_tuple(std::move(specTuple), opt_index_sequence{})); + } +#else /* - * Factory function for a storage, from a database file and a bunch of database object definitions. + * Factory function for a storage instance, from a database file and a bunch of database object definitions. */ template internal::storage_t make_storage(std::string filename, DBO... dbObjects) { - return {std::move(filename), internal::db_objects_tuple{std::forward(dbObjects)...}}; + return {std::move(filename), {std::forward(dbObjects)...}, std::tuple<>{}}; } +#endif /** * sqlite3_threadsafe() interface. diff --git a/dev/storage_base.h b/dev/storage_base.h index cfc0638ad..9ee3c0a1a 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -14,7 +14,7 @@ #include // std::list #include // std::make_unique, std::unique_ptr #include // std::map -#include // std::is_same +#include // std::is_same, std::is_aggregate #include // std::find_if, std::ranges::find #endif @@ -34,9 +34,9 @@ #include "udf_proxy.h" #include "serializing_util.h" #include "table_info.h" +#include "storage_options.h" namespace sqlite_orm { - namespace internal { struct storage_base { @@ -289,20 +289,19 @@ namespace sqlite_orm { * needed and closes when it is not needed. This function breaks this rule. In memory storage always * keeps connection opened so calling this for in-memory storage changes nothing. * Note about multithreading: in multithreading context avoiding using this function for not in-memory - * storage may lead to data races. If you have data races in such a configuration try to call `open_forever` + * storage may lead to data races. If you have data races in such a configuration try to call `open_forever()` * before accessing your storage - it may fix data races. */ void open_forever() { - this->isOpenedForever = true; - this->connection->retain(); - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); + if (!this->isOpenedForever) { + this->isOpenedForever = true; + this->connection->retain(); } } /** * Create an application-defined scalar SQL function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_scalar_function()` merely creates a closure to generate an instance of the scalar function object, * together with a copy of the passed initialization arguments. @@ -345,7 +344,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Create an application-defined scalar function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_scalar_function()` merely creates a closure to generate an instance of the scalar function object, * together with a copy of the passed initialization arguments. @@ -360,7 +359,7 @@ namespace sqlite_orm { /** * Create an application-defined scalar function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * If `quotedF` contains a freestanding function, stateless lambda or stateless function object, * `quoted_scalar_function::callable()` uses the original function object, assuming it is free of side effects; @@ -401,7 +400,7 @@ namespace sqlite_orm { /** * Create an application-defined aggregate SQL function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_aggregate_function()` merely creates a closure to generate an instance of the aggregate function object, * together with a copy of the passed initialization arguments. @@ -450,7 +449,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Create an application-defined aggregate function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_aggregate_function()` merely creates a closure to generate an instance of the aggregate function object, * together with a copy of the passed initialization arguments. @@ -465,7 +464,7 @@ namespace sqlite_orm { /** * Delete a scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_scalar_function() { @@ -477,7 +476,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Delete a scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_scalar_function() { @@ -486,7 +485,7 @@ namespace sqlite_orm { /** * Delete a quoted scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_scalar_function() { @@ -496,7 +495,7 @@ namespace sqlite_orm { /** * Delete aggregate function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_aggregate_function() { @@ -610,7 +609,7 @@ namespace sqlite_orm { } backup_t make_backup_to(const std::string& filename) { - auto holder = std::make_unique(filename); + auto holder = std::make_unique(filename, nullptr); connection_ref conRef{*holder}; return {conRef, "main", this->get_connection(), "main", std::move(holder)}; } @@ -620,7 +619,7 @@ namespace sqlite_orm { } backup_t make_backup_from(const std::string& filename) { - auto holder = std::make_unique(filename); + auto holder = std::make_unique(filename, nullptr); connection_ref conRef{*holder}; return {this->get_connection(), "main", conRef, "main", std::move(holder)}; } @@ -664,26 +663,39 @@ namespace sqlite_orm { } protected: - storage_base(std::string filename, int foreignKeysCount) : + storage_base(std::string filename, + connection_control connectionCtrl, + on_open_spec onOpenSpec, + int foreignKeysCount) : + on_open{std::move(onOpenSpec.onOpen)}, isOpenedForever{connectionCtrl.open_forever}, pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(filename.empty() || filename == ":memory:"), - connection(std::make_unique(std::move(filename))), + connection(std::make_unique( + std::move(filename), + std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), cachedForeignKeysCount(foreignKeysCount) { if (this->inMemory) { this->connection->retain(); - this->on_open_internal(this->connection->get()); + } + if (this->isOpenedForever) { + this->connection->retain(); } } storage_base(const storage_base& other) : on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), - connection(std::make_unique(other.connection->filename)), + isOpenedForever{other.isOpenedForever}, + connection(std::make_unique( + *other.connection, + std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), cachedForeignKeysCount(other.cachedForeignKeysCount) { if (this->inMemory) { this->connection->retain(); - this->on_open_internal(this->connection->get()); + } + if (this->isOpenedForever) { + this->connection->retain(); } } @@ -698,18 +710,12 @@ namespace sqlite_orm { void begin_transaction_internal(const std::string& query) { this->connection->retain(); - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); - } sqlite3* db = this->connection->get(); perform_void_exec(db, query); } connection_ref get_connection() { connection_ref res{*this->connection}; - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); - } return res; } @@ -725,17 +731,17 @@ namespace sqlite_orm { perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); return result; } - #endif - void on_open_internal(sqlite3* db) { + void on_open_internal(sqlite3* db) { #if SQLITE_VERSION_NUMBER >= 3006019 if (this->cachedForeignKeysCount) { this->foreign_keys(db, true); } #endif + if (this->pragma.synchronous_ != -1) { - this->pragma.synchronous(this->pragma.synchronous_); + this->pragma.set_pragma("synchronous", this->pragma.synchronous_, db); } if (this->pragma.journal_mode_ != -1) { @@ -995,8 +1001,8 @@ namespace sqlite_orm { }); #endif if (dbColumnInfoIt != dbTableInfo.end()) { - auto& dbColumnInfo = *dbColumnInfoIt; - auto columnsAreEqual = + table_xinfo& dbColumnInfo = *dbColumnInfoIt; + bool columnsAreEqual = dbColumnInfo.name == storageColumnInfo.name && dbColumnInfo.notnull == storageColumnInfo.notnull && (!dbColumnInfo.dflt_value.empty()) == (!storageColumnInfo.dflt_value.empty()) && @@ -1007,8 +1013,7 @@ namespace sqlite_orm { break; } dbTableInfo.erase(dbColumnInfoIt); - storageTableInfo.erase(storageTableInfo.begin() + - static_cast(storageColumnInfoIndex)); + storageTableInfo.erase(storageTableInfo.begin() + storageColumnInfoIndex); --storageColumnInfoIndex; } else { columnsToAdd.push_back(&storageColumnInfo); diff --git a/dev/storage_options.h b/dev/storage_options.h new file mode 100644 index 000000000..54f310087 --- /dev/null +++ b/dev/storage_options.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else +#include // std::is_aggregate +#include // std::move +#include // std::function +#endif + +namespace sqlite_orm { + namespace internal { + template + using storage_opt_tag_t = typename T::storage_opt_tag; + + struct on_open_spec { + using storage_opt_tag = int; + + std::function onOpen; + }; + } +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Database connection control options to be passed to `make_storage()`. + */ + struct connection_control { + /// Whether to open the database once and for all. + /// Required if using a 'storage' instance from multiple threads. + bool open_forever = false; + + using storage_opt_tag = int; + }; +#if __cpp_lib_is_aggregate >= 201703L + // design choice: must be an aggregate that can be constructed using designated initializers + static_assert(std::is_aggregate_v); +#endif + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + /** + * Callback function to be passed to `make_storage()`. + * The provided function is called immdediately after the database connection has been established and set up. + */ + inline internal::on_open_spec on_open(std::function onOpen) { + return {std::move(onOpen)}; + } +#endif +} diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index 55d940d5a..57fef52d5 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -103,7 +103,7 @@ namespace sqlite_orm { } /* - * Like `std::make_from_tuple`, but using a projection on the tuple elements. + * Like `std::make_from_tuple()`, but using a projection on the tuple elements. */ template constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) { @@ -112,5 +112,23 @@ namespace sqlite_orm { std::make_index_sequence>::value>{}, std::forward(project)); } + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + template class R, class Tpl, size_t... Idx, class Projection = polyfill::identity> + constexpr auto create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } + + /* + * Similar to `create_from_tuple()`, but the result type is specified as a template class. + */ + template class R, class Tpl, class Projection = polyfill::identity> + constexpr auto create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } +#endif } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a9b89b850..fa8023f64 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -98,9 +98,12 @@ using std::nullptr_t; #define SQLITE_ORM_STRUCTURED_BINDINGS_SUPPORTED #endif +#if __cpp_deduction_guides >= 201703L +#define SQLITE_ORM_CTAD_SUPPORTED +#endif + #if __cpp_generic_lambdas >= 201707L #define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED -#else #endif #if __cpp_init_captures >= 201803L @@ -1235,7 +1238,7 @@ namespace sqlite_orm { * Commonly used named abbreviation for `check_if`. */ template - using check_if_is_type = mpl::bind_front_fn; + using check_if_is_type = check_if; /* * Quoted trait metafunction that checks if a type's template matches the specified template @@ -1245,6 +1248,18 @@ namespace sqlite_orm { using check_if_is_template = mpl::pass_extracted_fn_to>>; + /* + * Quoted trait metafunction that checks if a type names a nested type determined by `Op`. + */ + template class Op> + using check_if_names = mpl::bind_front_higherorder_fn; + + /* + * Quoted trait metafunction that checks if a type does not name a nested type determined by `Op`. + */ + template class Op> + using check_if_lacks = mpl::not_>; + /* * Quoted metafunction that finds the index of the given type in a tuple. */ @@ -1636,7 +1651,7 @@ namespace sqlite_orm { } /* - * Like `std::make_from_tuple`, but using a projection on the tuple elements. + * Like `std::make_from_tuple()`, but using a projection on the tuple elements. */ template constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) { @@ -1645,6 +1660,24 @@ namespace sqlite_orm { std::make_index_sequence>::value>{}, std::forward(project)); } + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + template class R, class Tpl, size_t... Idx, class Projection = polyfill::identity> + constexpr auto create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } + + /* + * Similar to `create_from_tuple()`, but the result type is specified as a template class. + */ + template class R, class Tpl, class Projection = polyfill::identity> + constexpr auto create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } +#endif } } @@ -13876,6 +13909,7 @@ namespace sqlite_orm { #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include +#include // std::function #include // std::string #endif @@ -13885,25 +13919,38 @@ namespace sqlite_orm { namespace internal { struct connection_holder { - connection_holder(std::string filename) : filename(std::move(filename)) {} + connection_holder(std::string fileName, std::function onAfterOpen) : + _onAfterOpen{std::move(onAfterOpen)}, fileName(std::move(fileName)) {} + + connection_holder(const connection_holder&) = delete; + + connection_holder(const connection_holder& other, std::function onAfterOpen) : + _onAfterOpen{std::move(onAfterOpen)}, fileName{other.fileName} {} void retain() { // first one opens the connection. // we presume that the connection is opened once in a single-threaded context [also open forever]. // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. - if (this->_retain_count.fetch_add(1, std::memory_order_relaxed) == 0) { - int rc = sqlite3_open(this->filename.c_str(), &this->db); + if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { + int rc = sqlite3_open_v2(this->fileName.c_str(), + &this->db, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + nullptr); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { throw_translated_sqlite_error(this->db); } } + + if (_onAfterOpen) { + _onAfterOpen(this->db); + } } void release() { // last one closes the connection. // we assume that this might happen by any thread, therefore the counter must serve as a synchronization point. - if (this->_retain_count.fetch_sub(1, std::memory_order_acq_rel) == 1) { - int rc = sqlite3_close(this->db); + if (_retainCount.fetch_sub(1, std::memory_order_acq_rel) == 1) { + int rc = sqlite3_close_v2(this->db); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY { throw_translated_sqlite_error(this->db); } else { @@ -13921,16 +13968,20 @@ namespace sqlite_orm { * @attention While retrieving the reference count value is atomic it makes only sense at single-threaded points in code. */ int retain_count() const { - return this->_retain_count.load(std::memory_order_relaxed); + return _retainCount.load(std::memory_order_relaxed); } - const std::string filename; - protected: sqlite3* db = nullptr; private: - std::atomic_int _retain_count{}; + std::atomic_int _retainCount{}; + + private: + const std::function _onAfterOpen; + + public: + const std::string fileName; }; struct connection_ref { @@ -16191,7 +16242,7 @@ inline constexpr bool std::ranges::enable_borrowed_range // std::list #include // std::make_unique, std::unique_ptr #include // std::map -#include // std::is_same +#include // std::is_same, std::is_aggregate #include // std::find_if, std::ranges::find #endif @@ -16949,11 +17000,12 @@ namespace sqlite_orm { } void set_pragma_impl(const std::string& query, sqlite3* db = nullptr) { - auto con = this->get_connection(); - if (db == nullptr) { - db = con.get(); + if (db) { + perform_void_exec(db, query); + } else { + auto con = this->get_connection(); + perform_void_exec(con.get(), query); } - perform_void_exec(db, query); } }; } @@ -17707,8 +17759,58 @@ namespace sqlite_orm { // #include "table_info.h" +// #include "storage_options.h" + +#include +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else +#include // std::is_aggregate +#include // std::move +#include // std::function +#endif + namespace sqlite_orm { + namespace internal { + template + using storage_opt_tag_t = typename T::storage_opt_tag; + + struct on_open_spec { + using storage_opt_tag = int; + std::function onOpen; + }; + } +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Database connection control options to be passed to `make_storage()`. + */ + struct connection_control { + /// Whether to open the database once and for all. + /// Required if using a 'storage' instance from multiple threads. + bool open_forever = false; + + using storage_opt_tag = int; + }; +#if __cpp_lib_is_aggregate >= 201703L + // design choice: must be an aggregate that can be constructed using designated initializers + static_assert(std::is_aggregate_v); +#endif + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + /** + * Callback function to be passed to `make_storage()`. + * The provided function is called immdediately after the database connection has been established and set up. + */ + inline internal::on_open_spec on_open(std::function onOpen) { + return {std::move(onOpen)}; + } +#endif +} + +namespace sqlite_orm { namespace internal { struct storage_base { @@ -17961,20 +18063,19 @@ namespace sqlite_orm { * needed and closes when it is not needed. This function breaks this rule. In memory storage always * keeps connection opened so calling this for in-memory storage changes nothing. * Note about multithreading: in multithreading context avoiding using this function for not in-memory - * storage may lead to data races. If you have data races in such a configuration try to call `open_forever` + * storage may lead to data races. If you have data races in such a configuration try to call `open_forever()` * before accessing your storage - it may fix data races. */ void open_forever() { - this->isOpenedForever = true; - this->connection->retain(); - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); + if (!this->isOpenedForever) { + this->isOpenedForever = true; + this->connection->retain(); } } /** * Create an application-defined scalar SQL function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_scalar_function()` merely creates a closure to generate an instance of the scalar function object, * together with a copy of the passed initialization arguments. @@ -18017,7 +18118,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Create an application-defined scalar function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_scalar_function()` merely creates a closure to generate an instance of the scalar function object, * together with a copy of the passed initialization arguments. @@ -18032,7 +18133,7 @@ namespace sqlite_orm { /** * Create an application-defined scalar function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * If `quotedF` contains a freestanding function, stateless lambda or stateless function object, * `quoted_scalar_function::callable()` uses the original function object, assuming it is free of side effects; @@ -18073,7 +18174,7 @@ namespace sqlite_orm { /** * Create an application-defined aggregate SQL function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_aggregate_function()` merely creates a closure to generate an instance of the aggregate function object, * together with a copy of the passed initialization arguments. @@ -18122,7 +18223,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Create an application-defined aggregate function. - * Can be called at any time no matter whether the database connection is opened or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is opened or not. * * Note: `create_aggregate_function()` merely creates a closure to generate an instance of the aggregate function object, * together with a copy of the passed initialization arguments. @@ -18137,7 +18238,7 @@ namespace sqlite_orm { /** * Delete a scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_scalar_function() { @@ -18149,7 +18250,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** * Delete a scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_scalar_function() { @@ -18158,7 +18259,7 @@ namespace sqlite_orm { /** * Delete a quoted scalar function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_scalar_function() { @@ -18168,7 +18269,7 @@ namespace sqlite_orm { /** * Delete aggregate function you created before. - * Can be called at any time no matter whether the database connection is open or not. + * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ template void delete_aggregate_function() { @@ -18282,7 +18383,7 @@ namespace sqlite_orm { } backup_t make_backup_to(const std::string& filename) { - auto holder = std::make_unique(filename); + auto holder = std::make_unique(filename, nullptr); connection_ref conRef{*holder}; return {conRef, "main", this->get_connection(), "main", std::move(holder)}; } @@ -18292,7 +18393,7 @@ namespace sqlite_orm { } backup_t make_backup_from(const std::string& filename) { - auto holder = std::make_unique(filename); + auto holder = std::make_unique(filename, nullptr); connection_ref conRef{*holder}; return {this->get_connection(), "main", conRef, "main", std::move(holder)}; } @@ -18336,26 +18437,39 @@ namespace sqlite_orm { } protected: - storage_base(std::string filename, int foreignKeysCount) : + storage_base(std::string filename, + connection_control connectionCtrl, + on_open_spec onOpenSpec, + int foreignKeysCount) : + on_open{std::move(onOpenSpec.onOpen)}, isOpenedForever{connectionCtrl.open_forever}, pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(filename.empty() || filename == ":memory:"), - connection(std::make_unique(std::move(filename))), + connection(std::make_unique( + std::move(filename), + std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), cachedForeignKeysCount(foreignKeysCount) { if (this->inMemory) { this->connection->retain(); - this->on_open_internal(this->connection->get()); + } + if (this->isOpenedForever) { + this->connection->retain(); } } storage_base(const storage_base& other) : on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), - connection(std::make_unique(other.connection->filename)), + isOpenedForever{other.isOpenedForever}, + connection(std::make_unique( + *other.connection, + std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), cachedForeignKeysCount(other.cachedForeignKeysCount) { if (this->inMemory) { this->connection->retain(); - this->on_open_internal(this->connection->get()); + } + if (this->isOpenedForever) { + this->connection->retain(); } } @@ -18370,18 +18484,12 @@ namespace sqlite_orm { void begin_transaction_internal(const std::string& query) { this->connection->retain(); - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); - } sqlite3* db = this->connection->get(); perform_void_exec(db, query); } connection_ref get_connection() { connection_ref res{*this->connection}; - if (1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); - } return res; } @@ -18397,17 +18505,17 @@ namespace sqlite_orm { perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); return result; } - #endif - void on_open_internal(sqlite3* db) { + void on_open_internal(sqlite3* db) { #if SQLITE_VERSION_NUMBER >= 3006019 if (this->cachedForeignKeysCount) { this->foreign_keys(db, true); } #endif + if (this->pragma.synchronous_ != -1) { - this->pragma.synchronous(this->pragma.synchronous_); + this->pragma.set_pragma("synchronous", this->pragma.synchronous_, db); } if (this->pragma.journal_mode_ != -1) { @@ -18667,8 +18775,8 @@ namespace sqlite_orm { }); #endif if (dbColumnInfoIt != dbTableInfo.end()) { - auto& dbColumnInfo = *dbColumnInfoIt; - auto columnsAreEqual = + table_xinfo& dbColumnInfo = *dbColumnInfoIt; + bool columnsAreEqual = dbColumnInfo.name == storageColumnInfo.name && dbColumnInfo.notnull == storageColumnInfo.notnull && (!dbColumnInfo.dflt_value.empty()) == (!storageColumnInfo.dflt_value.empty()) && @@ -18679,8 +18787,7 @@ namespace sqlite_orm { break; } dbTableInfo.erase(dbColumnInfoIt); - storageTableInfo.erase(storageTableInfo.begin() + - static_cast(storageColumnInfoIndex)); + storageTableInfo.erase(storageTableInfo.begin() + storageColumnInfoIndex); --storageColumnInfoIndex; } else { columnsToAdd.push_back(&storageColumnInfo); @@ -22438,21 +22545,39 @@ namespace sqlite_orm { polyfill::void_t().prepare(std::declval()))>>> = true; + template + decltype(auto) storage_opt_or_default(OptionsTpl& options) { +#ifdef SQLITE_ORM_CTAD_SUPPORTED + if constexpr (tuple_has_type::value) { + return std::move(std::get(options)); + } else { + return Opt{}; + } +#else + return Opt{}; +#endif + } + /** * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` * function. */ template struct storage_t : storage_base { - using self = storage_t; + using self_type = storage_t; using db_objects_type = db_objects_tuple; /** * @param filename database filename. * @param dbObjects db_objects_tuple */ - storage_t(std::string filename, db_objects_type dbObjects) : - storage_base{std::move(filename), foreign_keys_count(dbObjects)}, db_objects{std::move(dbObjects)} {} + template + storage_t(std::string filename, db_objects_type dbObjects, OptionsTpl options) : + storage_base{std::move(filename), + storage_opt_or_default(options), + storage_opt_or_default(options), + foreign_keys_count(dbObjects)}, + db_objects{std::move(dbObjects)} {} storage_t(const storage_t&) = default; @@ -22471,7 +22596,7 @@ namespace sqlite_orm { * * Hence, friend was replaced by `obtain_db_objects()` and `pick_const_impl()`. */ - friend const db_objects_type& obtain_db_objects(const self& storage) noexcept { + friend const db_objects_type& obtain_db_objects(const self_type& storage) noexcept { return storage.db_objects; } @@ -22603,7 +22728,7 @@ namespace sqlite_orm { public: template, class... Args> - mapped_view iterate(Args&&... args) { + mapped_view iterate(Args&&... args) { this->assert_mapped_type(); auto con = this->get_connection(); @@ -23138,7 +23263,7 @@ namespace sqlite_orm { std::enable_if_t::value && !is_mapped::value, bool> = true> std::string dump(E&& expression, bool parametrized = false) const { - static_assert(is_preparable_v, "Expression must be a high-level statement"); + static_assert(is_preparable_v, "Expression must be a high-level statement"); decltype(auto) e2 = static_if::value>( [](auto expression) -> auto { @@ -24059,17 +24184,50 @@ namespace sqlite_orm { } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED }; // struct storage_t + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + template + using dbo_index_sequence = filter_tuple_sequence_t::template fn>; + + template + using opt_index_sequence = filter_tuple_sequence_t::template fn>; + + template + storage_t make_storage(std::string filename, std::tuple dbObjects, OptionsTpl options) { + return {std::move(filename), std::move(dbObjects), std::move(options)}; + } +#endif } } SQLITE_ORM_EXPORT namespace sqlite_orm { +#ifdef SQLITE_ORM_CTAD_SUPPORTED + /* + * Factory function for a storage instance, from a database file, a set of database object definitions + * and option storage options like connection control options and an 'on open' callback. + * + * E.g. + * auto storage = make_storage("", connection_control{.open_forever = true}, on_open([](sqlite3* db) {})); + */ + template + auto make_storage(std::string filename, Spec... specifications) { + using namespace ::sqlite_orm::internal; + + std::tuple specTuple{std::forward(specifications)...}; + return internal::make_storage( + std::move(filename), + create_from_tuple(std::move(specTuple), dbo_index_sequence{}), + create_from_tuple(std::move(specTuple), opt_index_sequence{})); + } +#else /* - * Factory function for a storage, from a database file and a bunch of database object definitions. + * Factory function for a storage instance, from a database file and a bunch of database object definitions. */ template internal::storage_t make_storage(std::string filename, DBO... dbObjects) { - return {std::move(filename), internal::db_objects_tuple{std::forward(dbObjects)...}}; + return {std::move(filename), {std::forward(dbObjects)...}, std::tuple<>{}}; } +#endif /** * sqlite3_threadsafe() interface. diff --git a/tests/filename.cpp b/tests/filename.cpp index e779ab836..de4f5559f 100644 --- a/tests/filename.cpp +++ b/tests/filename.cpp @@ -4,15 +4,15 @@ using namespace sqlite_orm; TEST_CASE("filename") { - { + SECTION("empty") { auto storage = make_storage(""); REQUIRE(storage.filename() == ""); } - { + SECTION("memory") { auto storage = make_storage(":memory:"); REQUIRE(storage.filename() == ":memory:"); } - { + SECTION("file name") { auto storage = make_storage("myDatabase.sqlite"); REQUIRE(storage.filename() == "myDatabase.sqlite"); } diff --git a/tests/static_tests/functional/tuple_transform.cpp b/tests/static_tests/functional/tuple_transform.cpp index d67539098..26bcab109 100644 --- a/tests/static_tests/functional/tuple_transform.cpp +++ b/tests/static_tests/functional/tuple_transform.cpp @@ -49,6 +49,8 @@ TEST_CASE("tuple_helper static") { #if __cpp_lib_constexpr_algorithms >= 201806L STATIC_REQUIRE(create_from_tuple>(std::make_tuple(1, 2), polyfill::identity{}) == std::array{1, 2}); + STATIC_REQUIRE(create_from_tuple(std::make_tuple(1, 2), polyfill::identity{}) == + std::tuple{1, 2}); #endif #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && (__cpp_lib_constexpr_functional >= 201907L) STATIC_REQUIRE(recombine_tuple(tuple_maker{}, std::make_tuple(1, 2), polyfill::identity{}, 3) == diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index c46cd28ad..0a3598641 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -4,6 +4,49 @@ using namespace sqlite_orm; +#ifdef SQLITE_ORM_CTAD_SUPPORTED +TEST_CASE("connection control") { + const auto openForever = GENERATE(false, true); + SECTION("") { + bool onOpenCalled = false; + SECTION("empty") { + auto storage = make_storage("", connection_control{openForever}, on_open([&onOpenCalled](sqlite3* db) { + onOpenCalled = db || false; + })); + REQUIRE(storage.is_opened()); + REQUIRE(onOpenCalled); + } +#if __cpp_designated_initializers >= 201707L + SECTION("empty C++20") { + auto storage = + make_storage("", connection_control{.open_forever = openForever}, on_open([&onOpenCalled](sqlite3* db) { + onOpenCalled = db || false; + })); + REQUIRE(storage.is_opened()); + REQUIRE(onOpenCalled); + } +#endif + SECTION("memory") { + auto storage = + make_storage(":memory:", connection_control{openForever}, on_open([&onOpenCalled](sqlite3* db) { + onOpenCalled = db || false; + })); + REQUIRE(storage.is_opened()); + REQUIRE(onOpenCalled); + } + SECTION("file name") { + auto storage = make_storage("myDatabase.sqlite", + connection_control{openForever}, + on_open([&onOpenCalled](sqlite3* db) { + onOpenCalled = db || false; + })); + REQUIRE(storage.is_opened() == openForever); + REQUIRE(onOpenCalled == openForever); + } + } +} +#endif + TEST_CASE("Current time/date/timestamp") { auto storage = make_storage(""); SECTION("time") { From 117f2b1878c20e935472c9a178041228eeb1cfc3 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 16 Feb 2025 18:19:16 +0200 Subject: [PATCH 066/170] Updated amalgamated header --- include/sqlite_orm/sqlite_orm.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index fa8023f64..0fc45f641 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13919,20 +13919,20 @@ namespace sqlite_orm { namespace internal { struct connection_holder { - connection_holder(std::string fileName, std::function onAfterOpen) : - _onAfterOpen{std::move(onAfterOpen)}, fileName(std::move(fileName)) {} + connection_holder(std::string filename, std::function onAfterOpen) : + _onAfterOpen{std::move(onAfterOpen)}, filename(std::move(filename)) {} connection_holder(const connection_holder&) = delete; connection_holder(const connection_holder& other, std::function onAfterOpen) : - _onAfterOpen{std::move(onAfterOpen)}, fileName{other.fileName} {} + _onAfterOpen{std::move(onAfterOpen)}, filename{other.filename} {} void retain() { // first one opens the connection. // we presume that the connection is opened once in a single-threaded context [also open forever]. // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { - int rc = sqlite3_open_v2(this->fileName.c_str(), + int rc = sqlite3_open_v2(this->filename.c_str(), &this->db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); @@ -13981,7 +13981,7 @@ namespace sqlite_orm { const std::function _onAfterOpen; public: - const std::string fileName; + const std::string filename; }; struct connection_ref { From 9ab6712ece71caf2a39c87c7a1b802d0e72ef2a2 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 18 Feb 2025 15:31:45 +0200 Subject: [PATCH 067/170] Avoided a triple-layered alias template expressions Older compilers such as Visual C++ 16.11 [msvc 1929] choke on deeply nested alias template expressions and can be made happy by unraveling these expressions. --- dev/storage_impl.h | 19 ++++++++----------- include/sqlite_orm/sqlite_orm.h | 19 ++++++++----------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/dev/storage_impl.h b/dev/storage_impl.h index 81537fdcc..b90efd1e5 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -70,17 +70,15 @@ namespace sqlite_orm { constexpr decltype(auto) materialize_column_pointer(const DBOs&, const column_pointer>&) { using table_type = storage_pick_table_t; - using cte_mapper_type = cte_mapper_type_t; + using cte_colrefs_tuple = typename cte_mapper_type_t::final_colrefs_tuple; + using cte_fields_type = typename cte_mapper_type_t::fields_type; // lookup ColAlias in the final column references - using colalias_index = - find_tuple_type>; - static_assert(colalias_index::value < std::tuple_size_v, + using colalias_index = find_tuple_type>; + static_assert(colalias_index::value < std::tuple_size_v, "No such column mapped into the CTE."); - return &aliased_field< - ColAlias, - std::tuple_element_t>::field; + return &aliased_field>::field; } #endif @@ -104,14 +102,13 @@ namespace sqlite_orm { constexpr decltype(auto) find_column_name(const DBOs& dboObjects, const column_pointer>&) { using table_type = storage_pick_table_t; - using cte_mapper_type = cte_mapper_type_t; + using cte_colrefs_tuple = typename cte_mapper_type_t::final_colrefs_tuple; using column_index_sequence = filter_tuple_sequence_t, is_column>; // note: even though the columns contain the [`aliased_field<>::*`] we perform the lookup using the column references. // lookup ColAlias in the final column references - using colalias_index = - find_tuple_type>; - static_assert(colalias_index::value < std::tuple_size_v, + using colalias_index = find_tuple_type>; + 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; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a9b89b850..138745731 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -12663,17 +12663,15 @@ namespace sqlite_orm { constexpr decltype(auto) materialize_column_pointer(const DBOs&, const column_pointer>&) { using table_type = storage_pick_table_t; - using cte_mapper_type = cte_mapper_type_t; + using cte_colrefs_tuple = typename cte_mapper_type_t::final_colrefs_tuple; + using cte_fields_type = typename cte_mapper_type_t::fields_type; // lookup ColAlias in the final column references - using colalias_index = - find_tuple_type>; - static_assert(colalias_index::value < std::tuple_size_v, + using colalias_index = find_tuple_type>; + static_assert(colalias_index::value < std::tuple_size_v, "No such column mapped into the CTE."); - return &aliased_field< - ColAlias, - std::tuple_element_t>::field; + return &aliased_field>::field; } #endif @@ -12697,14 +12695,13 @@ namespace sqlite_orm { constexpr decltype(auto) find_column_name(const DBOs& dboObjects, const column_pointer>&) { using table_type = storage_pick_table_t; - using cte_mapper_type = cte_mapper_type_t; + using cte_colrefs_tuple = typename cte_mapper_type_t::final_colrefs_tuple; using column_index_sequence = filter_tuple_sequence_t, is_column>; // note: even though the columns contain the [`aliased_field<>::*`] we perform the lookup using the column references. // lookup ColAlias in the final column references - using colalias_index = - find_tuple_type>; - static_assert(colalias_index::value < std::tuple_size_v, + using colalias_index = find_tuple_type>; + 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; From f422a1b27e527b51b292d82132db4207f7bd2eb5 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 18 Feb 2025 17:56:08 +0200 Subject: [PATCH 068/170] Corrected the point at which the handler is called to set up the database --- dev/connection_holder.h | 6 +++--- include/sqlite_orm/sqlite_orm.h | 6 +++--- tests/storage_tests.cpp | 36 ++++++++++++++++++++++----------- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 1544ffdcc..bfe5dcbd8 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -33,10 +33,10 @@ namespace sqlite_orm { if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { throw_translated_sqlite_error(this->db); } - } - if (_onAfterOpen) { - _onAfterOpen(this->db); + if (_onAfterOpen) { + _onAfterOpen(this->db); + } } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 0fc45f641..0270c303c 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13939,10 +13939,10 @@ namespace sqlite_orm { if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { throw_translated_sqlite_error(this->db); } - } - if (_onAfterOpen) { - _onAfterOpen(this->db); + if (_onAfterOpen) { + _onAfterOpen(this->db); + } } } diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index 0a3598641..c3844ab53 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -9,39 +9,51 @@ TEST_CASE("connection control") { const auto openForever = GENERATE(false, true); SECTION("") { bool onOpenCalled = false; + int nOnOpenCalled = 0; SECTION("empty") { - auto storage = make_storage("", connection_control{openForever}, on_open([&onOpenCalled](sqlite3* db) { - onOpenCalled = db || false; - })); + auto storage = + make_storage("", connection_control{openForever}, on_open([&onOpenCalled, &nOnOpenCalled](sqlite3* db) { + onOpenCalled = db || false; + ++nOnOpenCalled; + })); REQUIRE(storage.is_opened()); REQUIRE(onOpenCalled); + REQUIRE(nOnOpenCalled == 1); } #if __cpp_designated_initializers >= 201707L SECTION("empty C++20") { - auto storage = - make_storage("", connection_control{.open_forever = openForever}, on_open([&onOpenCalled](sqlite3* db) { - onOpenCalled = db || false; - })); + auto storage = make_storage("", + connection_control{.open_forever = openForever}, + on_open([&onOpenCalled, &nOnOpenCalled](sqlite3* db) { + onOpenCalled = db || false; + ++nOnOpenCalled; + })); REQUIRE(storage.is_opened()); REQUIRE(onOpenCalled); + REQUIRE(nOnOpenCalled == 1); } #endif SECTION("memory") { - auto storage = - make_storage(":memory:", connection_control{openForever}, on_open([&onOpenCalled](sqlite3* db) { - onOpenCalled = db || false; - })); + auto storage = make_storage(":memory:", + connection_control{openForever}, + on_open([&onOpenCalled, &nOnOpenCalled](sqlite3* db) { + onOpenCalled = db || false; + ++nOnOpenCalled; + })); REQUIRE(storage.is_opened()); REQUIRE(onOpenCalled); + REQUIRE(nOnOpenCalled == 1); } SECTION("file name") { auto storage = make_storage("myDatabase.sqlite", connection_control{openForever}, - on_open([&onOpenCalled](sqlite3* db) { + on_open([&onOpenCalled, &nOnOpenCalled](sqlite3* db) { onOpenCalled = db || false; + ++nOnOpenCalled; })); REQUIRE(storage.is_opened() == openForever); REQUIRE(onOpenCalled == openForever); + REQUIRE(nOnOpenCalled == int(openForever)); } } } From 8147850519cd68a55bf60b16584d310d0e4becfd Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 18 Feb 2025 17:58:57 +0200 Subject: [PATCH 069/170] Renamed database callback function variable --- dev/connection_holder.h | 14 +++++++------- include/sqlite_orm/sqlite_orm.h | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index bfe5dcbd8..59d093858 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -13,13 +13,13 @@ namespace sqlite_orm { namespace internal { struct connection_holder { - connection_holder(std::string filename, std::function onAfterOpen) : - _onAfterOpen{std::move(onAfterOpen)}, filename(std::move(filename)) {} + connection_holder(std::string filename, std::function didOpenDb) : + _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)) {} connection_holder(const connection_holder&) = delete; - connection_holder(const connection_holder& other, std::function onAfterOpen) : - _onAfterOpen{std::move(onAfterOpen)}, filename{other.filename} {} + connection_holder(const connection_holder& other, std::function didOpenDb) : + _didOpenDb{std::move(didOpenDb)}, filename{other.filename} {} void retain() { // first one opens the connection. @@ -34,8 +34,8 @@ namespace sqlite_orm { throw_translated_sqlite_error(this->db); } - if (_onAfterOpen) { - _onAfterOpen(this->db); + if (_didOpenDb) { + _didOpenDb(this->db); } } } @@ -72,7 +72,7 @@ namespace sqlite_orm { std::atomic_int _retainCount{}; private: - const std::function _onAfterOpen; + const std::function _didOpenDb; public: const std::string filename; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 0270c303c..d39c28aad 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13919,13 +13919,13 @@ namespace sqlite_orm { namespace internal { struct connection_holder { - connection_holder(std::string filename, std::function onAfterOpen) : - _onAfterOpen{std::move(onAfterOpen)}, filename(std::move(filename)) {} + connection_holder(std::string filename, std::function didOpenDb) : + _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)) {} connection_holder(const connection_holder&) = delete; - connection_holder(const connection_holder& other, std::function onAfterOpen) : - _onAfterOpen{std::move(onAfterOpen)}, filename{other.filename} {} + connection_holder(const connection_holder& other, std::function didOpenDb) : + _didOpenDb{std::move(didOpenDb)}, filename{other.filename} {} void retain() { // first one opens the connection. @@ -13940,8 +13940,8 @@ namespace sqlite_orm { throw_translated_sqlite_error(this->db); } - if (_onAfterOpen) { - _onAfterOpen(this->db); + if (_didOpenDb) { + _didOpenDb(this->db); } } } @@ -13978,7 +13978,7 @@ namespace sqlite_orm { std::atomic_int _retainCount{}; private: - const std::function _onAfterOpen; + const std::function _didOpenDb; public: const std::string filename; From 3dcb110c33847eeab098bd63729e529e7c121f85 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 2 Mar 2025 19:15:00 +0530 Subject: [PATCH 070/170] Added column_pointer and table reference support to foreign key columns Additionally prefixed `foreign_key_intermediate_t::_columns` member with an underscore to make using it in an IDE more user-friendly. --- dev/constraints.h | 54 ++++++++++++++++++++++------- include/sqlite_orm/sqlite_orm.h | 55 +++++++++++++++++++++++------- tests/static_tests/foreign_key.cpp | 23 +++++++++++++ 3 files changed, 108 insertions(+), 24 deletions(-) diff --git a/dev/constraints.h b/dev/constraints.h index cbb476eb8..38dcbbd51 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -19,6 +19,7 @@ #include "table_type_of.h" #include "type_printer.h" #include "column_pointer.h" +#include "table_reference.h" namespace sqlite_orm { @@ -304,7 +305,7 @@ namespace sqlite_orm { return res; } - operator bool() const { + explicit operator bool() const { return this->_action != foreign_key_action::none; } }; @@ -318,7 +319,6 @@ namespace sqlite_orm { struct foreign_key_t, std::tuple> { using columns_type = std::tuple; using references_type = std::tuple; - using self = foreign_key_t; /** * Holds obect type of all referenced columns. @@ -333,8 +333,8 @@ namespace sqlite_orm { columns_type columns; references_type references; - on_update_delete_t on_update; - on_update_delete_t on_delete; + on_update_delete_t on_update; + on_update_delete_t on_delete; static_assert(std::tuple_size::value == std::tuple_size::value, "Columns size must be equal to references tuple"); @@ -344,11 +344,11 @@ namespace sqlite_orm { columns(std::move(columns_)), references(std::move(references_)), on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} - foreign_key_t(const self& other) : + foreign_key_t(const foreign_key_t& other) : columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), on_delete(*this, false, other.on_delete._action) {} - self& operator=(const self& other) { + foreign_key_t& operator=(const foreign_key_t& other) { this->columns = other.columns; this->references = other.references; this->on_update = {*this, true, other.on_update._action}; @@ -372,17 +372,25 @@ namespace sqlite_orm { struct foreign_key_intermediate_t { using tuple_type = std::tuple; - tuple_type columns; + tuple_type _columns; template - foreign_key_t, std::tuple> references(Rs... refs) { - return {std::move(this->columns), {std::forward(refs)...}}; + foreign_key_t> references(Rs... refs) { + return {std::move(_columns), {std::forward(refs)...}}; } template - foreign_key_t, std::tuple...>> references(Rs... refs) { - return {std::move(this->columns), {sqlite_orm::column(refs)...}}; + foreign_key_t...>> references(Rs... refs) { + return {std::move(_columns), {{refs}...}}; } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto references(Rs... refs) { + using fk_type = foreign_key_t, Rs>...>>; + return fk_type{std::move(_columns), {{refs}...}}; + } +#endif }; #endif @@ -525,13 +533,35 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 /** - * FOREIGN KEY constraint construction function that takes member pointer as argument + * FOREIGN KEY constraint builder function that takes a member pointer as argument * Available in SQLite 3.6.19 or higher */ template internal::foreign_key_intermediate_t foreign_key(Cs... columns) { return {{std::forward(columns)...}}; } + + /** + * FOREIGN KEY constraint builder function that takes a member pointer of a derived class as argument. + * Available in SQLite 3.6.19 or higher + */ + template + internal::foreign_key_intermediate_t...> foreign_key(Cs... columns) { + return {{{columns}...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * FOREIGN KEY constraint builder function that takes a member pointer of a derived class as argument. + * Available in SQLite 3.6.19 or higher + */ + template + auto foreign_key(Cs... columns) { + using namespace ::sqlite_orm::internal; + using builder_type = foreign_key_intermediate_t, Cs>...>; + return builder_type{{{columns}...}}; + } +#endif #endif /** diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index d965117b4..1a3551a88 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -3290,6 +3290,8 @@ namespace sqlite_orm { // #include "column_pointer.h" +// #include "table_reference.h" + namespace sqlite_orm { namespace internal { @@ -3574,7 +3576,7 @@ namespace sqlite_orm { return res; } - operator bool() const { + explicit operator bool() const { return this->_action != foreign_key_action::none; } }; @@ -3588,7 +3590,6 @@ namespace sqlite_orm { struct foreign_key_t, std::tuple> { using columns_type = std::tuple; using references_type = std::tuple; - using self = foreign_key_t; /** * Holds obect type of all referenced columns. @@ -3603,8 +3604,8 @@ namespace sqlite_orm { columns_type columns; references_type references; - on_update_delete_t on_update; - on_update_delete_t on_delete; + on_update_delete_t on_update; + on_update_delete_t on_delete; static_assert(std::tuple_size::value == std::tuple_size::value, "Columns size must be equal to references tuple"); @@ -3614,11 +3615,11 @@ namespace sqlite_orm { columns(std::move(columns_)), references(std::move(references_)), on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} - foreign_key_t(const self& other) : + foreign_key_t(const foreign_key_t& other) : columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), on_delete(*this, false, other.on_delete._action) {} - self& operator=(const self& other) { + foreign_key_t& operator=(const foreign_key_t& other) { this->columns = other.columns; this->references = other.references; this->on_update = {*this, true, other.on_update._action}; @@ -3642,17 +3643,25 @@ namespace sqlite_orm { struct foreign_key_intermediate_t { using tuple_type = std::tuple; - tuple_type columns; + tuple_type _columns; template - foreign_key_t, std::tuple> references(Rs... refs) { - return {std::move(this->columns), {std::forward(refs)...}}; + foreign_key_t> references(Rs... refs) { + return {std::move(_columns), {std::forward(refs)...}}; } template - foreign_key_t, std::tuple...>> references(Rs... refs) { - return {std::move(this->columns), {sqlite_orm::column(refs)...}}; + foreign_key_t...>> references(Rs... refs) { + return {std::move(_columns), {{refs}...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto references(Rs... refs) { + using fk_type = foreign_key_t, Rs>...>>; + return fk_type{std::move(_columns), {{refs}...}}; } +#endif }; #endif @@ -3795,13 +3804,35 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 /** - * FOREIGN KEY constraint construction function that takes member pointer as argument + * FOREIGN KEY constraint construction function that takes a member pointer as argument * Available in SQLite 3.6.19 or higher */ template internal::foreign_key_intermediate_t foreign_key(Cs... columns) { return {{std::forward(columns)...}}; } + + /** + * FOREIGN KEY constraint construction function that takes member pointer of a derived class as argument. + * Available in SQLite 3.6.19 or higher + */ + template + internal::foreign_key_intermediate_t...> foreign_key(Cs... columns) { + return {{{columns}...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * FOREIGN KEY constraint construction function that takes member pointer of a derived class as argument. + * Available in SQLite 3.6.19 or higher + */ + template + auto foreign_key(Cs... columns) { + using namespace ::sqlite_orm::internal; + using builder_type = foreign_key_intermediate_t, Cs>...>; + return builder_type{{{columns}...}}; + } +#endif #endif /** diff --git a/tests/static_tests/foreign_key.cpp b/tests/static_tests/foreign_key.cpp index c869e304e..4a3aa30eb 100644 --- a/tests/static_tests/foreign_key.cpp +++ b/tests/static_tests/foreign_key.cpp @@ -40,6 +40,15 @@ TEST_CASE("foreign key static") { bool is_global; std::string file_path; }; + struct Base { + int id; + int parentId; + }; + struct Derived : Base {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr orm_table_reference auto derived = c(); +#endif + auto cppInclusionIncluderPathFk = foreign_key(&CppInclusion::includer_path).references(&File::path); STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); @@ -65,6 +74,20 @@ TEST_CASE("foreign key static") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); + auto selfRefFk1 = foreign_key(column(&Derived::parentId)).references(column(&Derived::id)); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + auto selfRefFk2 = foreign_key(&Derived::parentId).references(&Derived::id); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + auto selfRefFk3 = foreign_key(&Derived::parentId).references(&Derived::id); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); +#endif + auto storage = make_storage({}, make_table("files", make_column("path", &File::path, primary_key())), make_table("c_inclusions", From b8eb4e11e363f893fac310e9bb732c33ff4d5d0f Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 5 Mar 2025 11:02:57 +0530 Subject: [PATCH 071/170] Simplified column pointer logic for foreign keys There is no need to use the `column_pointer` facility if we know the type of the derived class type of a pointer-to-member, which I think is the cleaner approach. Also introduced unit tests for: * comparing fields * foreign keys of derived classes. --- dev/alias.h | 1 + dev/constraints.h | 75 ++++---- dev/field_of.h | 55 ++++++ dev/member_traits/member_traits.h | 16 ++ dev/schema/table.h | 10 +- dev/table_type_of.h | 26 --- dev/typed_comparator.h | 16 -- include/sqlite_orm/sqlite_orm.h | 194 ++++++++++++--------- tests/static_tests/column.cpp | 50 ++++-- tests/static_tests/foreign_key.cpp | 78 +++++++-- tests/static_tests/member_traits_tests.cpp | 7 + 11 files changed, 336 insertions(+), 192 deletions(-) create mode 100644 dev/field_of.h delete mode 100644 dev/typed_comparator.h diff --git a/dev/alias.h b/dev/alias.h index 3719c2498..fbc174126 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -15,6 +15,7 @@ #include "functional/cstring_literal.h" #include "type_traits.h" #include "alias_traits.h" +#include "field_of.h" #include "table_type_of.h" #include "tags.h" #include "column_pointer.h" diff --git a/dev/constraints.h b/dev/constraints.h index 38dcbbd51..83fc3c2e9 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -16,9 +16,9 @@ #include "type_traits.h" #include "collate_argument.h" #include "error_code.h" +#include "field_of.h" #include "table_type_of.h" #include "type_printer.h" -#include "column_pointer.h" #include "table_reference.h" namespace sqlite_orm { @@ -191,9 +191,8 @@ namespace sqlite_orm { * FOREIGN KEY constraint class. * Cs are columns which has foreign key * Rs are column which C references to - * Available in SQLite 3.6.19 or higher + * Available since SQLite 3.6.19 */ - template struct foreign_key_t; @@ -306,7 +305,7 @@ namespace sqlite_orm { } explicit operator bool() const { - return this->_action != foreign_key_action::none; + return _action != foreign_key_action::none; } }; @@ -338,7 +337,8 @@ namespace sqlite_orm { static_assert(std::tuple_size::value == std::tuple_size::value, "Columns size must be equal to references tuple"); - static_assert(!std::is_same::value, "All references must have the same type"); + static_assert(!std::is_same::value, "All columns must have the same mapped type"); + static_assert(!std::is_same::value, "All references must have the same mapped type"); foreign_key_t(columns_type columns_, references_type references_) : columns(std::move(columns_)), references(std::move(references_)), @@ -348,13 +348,7 @@ namespace sqlite_orm { columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), on_delete(*this, false, other.on_delete._action) {} - foreign_key_t& operator=(const foreign_key_t& other) { - this->columns = other.columns; - this->references = other.references; - this->on_update = {*this, true, other.on_update._action}; - this->on_delete = {*this, false, other.on_delete._action}; - return *this; - } + foreign_key_t& operator=(const foreign_key_t&) = delete; }; template @@ -364,9 +358,8 @@ namespace sqlite_orm { } /** - * Cs can be a class member pointer, a getter function member pointer or setter - * func member pointer - * Available in SQLite 3.6.19 or higher + * Cs can be a class member pointer or column pointer + * Available since SQLite 3.6.19 */ template struct foreign_key_intermediate_t { @@ -374,21 +367,31 @@ namespace sqlite_orm { tuple_type _columns; + /** + * Specify one or more target fields, which can either be class member pointers or column pointers. + */ template foreign_key_t> references(Rs... refs) { return {std::move(_columns), {std::forward(refs)...}}; } - template - foreign_key_t...>> references(Rs... refs) { - return {std::move(_columns), {{refs}...}}; + /** + * Specify one or more target fields, which are class member pointers of base classes. + */ + template + foreign_key_t> references(F Base::*... refs) { + static_assert(std::conjunction...>::value, + "Referenced fields must be from explicitly specified derived class"); + return {std::move(_columns), {refs...}}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto references(Rs... refs) { - using fk_type = foreign_key_t, Rs>...>>; - return fk_type{std::move(_columns), {{refs}...}}; + /** + * Specify one or more target fields, which are class member pointers of base classes. + */ + template + auto references(F Base::*... refs) { + return this->references>(refs...); } #endif }; @@ -533,8 +536,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 /** - * FOREIGN KEY constraint builder function that takes a member pointer as argument - * Available in SQLite 3.6.19 or higher + * FOREIGN KEY constraint builder function taking one or more fields [member pointer or column pointer] as argument. + * Available since SQLite 3.6.19 */ template internal::foreign_key_intermediate_t foreign_key(Cs... columns) { @@ -542,24 +545,24 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } /** - * FOREIGN KEY constraint builder function that takes a member pointer of a derived class as argument. - * Available in SQLite 3.6.19 or higher + * FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * Available since SQLite 3.6.19 */ - template - internal::foreign_key_intermediate_t...> foreign_key(Cs... columns) { - return {{{columns}...}}; + template + internal::foreign_key_intermediate_t foreign_key(F Base::*... columns) { + static_assert(std::conjunction...>::value, + "Fields must be from explicitly specified derived class"); + return {{columns...}}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * FOREIGN KEY constraint builder function that takes a member pointer of a derived class as argument. - * Available in SQLite 3.6.19 or higher + * FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * Available since SQLite 3.6.19 */ - template - auto foreign_key(Cs... columns) { - using namespace ::sqlite_orm::internal; - using builder_type = foreign_key_intermediate_t, Cs>...>; - return builder_type{{{columns}...}}; + template + auto foreign_key(F Base::*... columns) { + return foreign_key>(columns...); } #endif #endif diff --git a/dev/field_of.h b/dev/field_of.h new file mode 100644 index 000000000..6036dba36 --- /dev/null +++ b/dev/field_of.h @@ -0,0 +1,55 @@ +#pragma once + +#ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // std::enable_if, std::is_convertible, std::bool_constant +#endif + +namespace sqlite_orm { + namespace internal { + template + struct column_pointer; + + /* + * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. + * + * One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer. + * E.g. + * regular: `alias_column>(column(&Base::field))` + * short: `alias_column>(&Base::field)` + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; + + /* + * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_field_of_v::value>> = true; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; + + template + struct is_field_of : polyfill::bool_constant> {}; + + /* + * Compare unrelated fields, like from completely different class types or an empty setter. + */ + template + constexpr bool compare_fields(const L&, const R&) { + return false; + } + + /* + * Compare related fields, either from the same class types or also if the pointer-to-member is of a base class. + */ + template::value || is_field_of::value, bool> = true> + constexpr bool compare_fields(F O1::* lhs, F O2::* rhs) { + return lhs == rhs; + } + } +} diff --git a/dev/member_traits/member_traits.h b/dev/member_traits/member_traits.h index 9265b4d21..e130c21ee 100644 --- a/dev/member_traits/member_traits.h +++ b/dev/member_traits/member_traits.h @@ -90,5 +90,21 @@ namespace sqlite_orm { template using member_object_type_t = typename member_object_type::type; + + /* + * Casts the class type of a pointer-to-member from a base class to the specified derived class. + */ + template + constexpr F O::* as_field_of(F Base::* f) { + return f; + } + + /* + * Metafunction that casts the class type of a pointer-to-member from a base class to the specified derived class. + * note (implementation): go through `member_field_type_t<>` instead of `decltype(as_field_of())` because of + * older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + */ + template + using as_field_of_t = member_field_type_t O::*; } } diff --git a/dev/schema/table.h b/dev/schema/table.h index 27826dc9c..ee958aef9 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -18,7 +18,7 @@ #include "../tuple_helper/tuple_iteration.h" #include "../tuple_helper/tuple_transformer.h" #include "../member_traits/member_traits.h" -#include "../typed_comparator.h" +#include "../field_of.h" #include "../type_traits.h" #include "../alias_traits.h" #include "../constraints.h" @@ -143,7 +143,7 @@ namespace sqlite_orm { iterate_tuple(this->elements, col_index_sequence_with_field_type{}, call_as_template_base([&res, &memberPointer, &object](const auto& column) { - if (compare_any(column.setter, memberPointer)) { + if (compare_fields(column.setter, memberPointer)) { res = &polyfill::invoke(column.member_pointer, object); } })); @@ -237,7 +237,7 @@ namespace sqlite_orm { iterate_tuple(this->elements, col_index_sequence_with_field_type{}, [&res, m](auto& c) { - if (compare_any(c.member_pointer, m) || compare_any(c.setter, m)) { + if (compare_fields(c.member_pointer, m) || compare_fields(c.setter, m)) { res = &c.name; } }); @@ -402,8 +402,8 @@ namespace sqlite_orm { check_if_is_type>::template fn, member_field_type_t>; iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { - if (compare_any(memberPointer, column.member_pointer) || - compare_any(memberPointer, column.setter)) { + if (compare_fields(memberPointer, column.member_pointer) || + compare_fields(memberPointer, column.setter)) { res = true; } }); diff --git a/dev/table_type_of.h b/dev/table_type_of.h index 050a23022..a3123dcf7 100644 --- a/dev/table_type_of.h +++ b/dev/table_type_of.h @@ -1,11 +1,6 @@ #pragma once -#ifndef SQLITE_ORM_IMPORT_STD_MODULE -#include // std::enable_if, std::is_convertible -#endif - namespace sqlite_orm { - namespace internal { template @@ -43,26 +38,5 @@ namespace sqlite_orm { template using table_type_of_t = typename table_type_of::type; - - /* - * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. - * - * One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer. - * E.g. - * regular: `alias_column>(column(&Base::field))` - * short: `alias_column>(&Base::field)` - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; - - /* - * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_field_of_v::value>> = true; - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; } } diff --git a/dev/typed_comparator.h b/dev/typed_comparator.h deleted file mode 100644 index d911c1b7e..000000000 --- a/dev/typed_comparator.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -namespace sqlite_orm { - - namespace internal { - - template - bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { - return false; - } - template - bool compare_any(const O& lhs, const O& rhs) { - return lhs == rhs; - } - } -} diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 1a3551a88..153711002 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -626,6 +626,22 @@ namespace sqlite_orm { template using member_object_type_t = typename member_object_type::type; + + /* + * Casts the class type of a pointer-to-member from a base class to the specified derived class. + */ + template + constexpr F O::* as_field_of(F Base::* f) { + return f; + } + + /* + * Metafunction that casts the class type of a pointer-to-member from a base class to the specified derived class. + * note (implementation): go through `member_field_type_t<>` instead of `decltype(as_field_of())` because of + * older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + */ + template + using as_field_of_t = member_field_type_t O::*; } } @@ -2162,14 +2178,65 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #endif } -// #include "table_type_of.h" +// #include "field_of.h" #ifndef SQLITE_ORM_IMPORT_STD_MODULE -#include // std::enable_if, std::is_convertible +#include // std::enable_if, std::is_convertible, std::bool_constant #endif namespace sqlite_orm { + namespace internal { + template + struct column_pointer; + + /* + * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. + * + * One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer. + * E.g. + * regular: `alias_column>(column(&Base::field))` + * short: `alias_column>(&Base::field)` + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; + + /* + * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_field_of_v::value>> = true; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; + + template + struct is_field_of : polyfill::bool_constant> {}; + + /* + * Compare unrelated fields, like from completely different class types or an empty setter. + */ + template + constexpr bool compare_fields(const L&, const R&) { + return false; + } + + /* + * Compare related fields, either from the same class types or also if the pointer-to-member is of a base class. + */ + template::value || is_field_of::value, bool> = true> + constexpr bool compare_fields(F O1::* lhs, F O2::* rhs) { + return lhs == rhs; + } + } +} + +// #include "table_type_of.h" +namespace sqlite_orm { namespace internal { template @@ -2207,27 +2274,6 @@ namespace sqlite_orm { template using table_type_of_t = typename table_type_of::type; - - /* - * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. - * - * One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer. - * E.g. - * regular: `alias_column>(column(&Base::field))` - * short: `alias_column>(&Base::field)` - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; - - /* - * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_field_of_v::value>> = true; - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; } } @@ -3284,12 +3330,12 @@ namespace sqlite_orm { // #include "error_code.h" +// #include "field_of.h" + // #include "table_type_of.h" // #include "type_printer.h" -// #include "column_pointer.h" - // #include "table_reference.h" namespace sqlite_orm { @@ -3462,9 +3508,8 @@ namespace sqlite_orm { * FOREIGN KEY constraint class. * Cs are columns which has foreign key * Rs are column which C references to - * Available in SQLite 3.6.19 or higher + * Available since SQLite 3.6.19 */ - template struct foreign_key_t; @@ -3577,7 +3622,7 @@ namespace sqlite_orm { } explicit operator bool() const { - return this->_action != foreign_key_action::none; + return _action != foreign_key_action::none; } }; @@ -3609,7 +3654,8 @@ namespace sqlite_orm { static_assert(std::tuple_size::value == std::tuple_size::value, "Columns size must be equal to references tuple"); - static_assert(!std::is_same::value, "All references must have the same type"); + static_assert(!std::is_same::value, "All columns must have the same mapped type"); + static_assert(!std::is_same::value, "All references must have the same mapped type"); foreign_key_t(columns_type columns_, references_type references_) : columns(std::move(columns_)), references(std::move(references_)), @@ -3619,13 +3665,7 @@ namespace sqlite_orm { columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), on_delete(*this, false, other.on_delete._action) {} - foreign_key_t& operator=(const foreign_key_t& other) { - this->columns = other.columns; - this->references = other.references; - this->on_update = {*this, true, other.on_update._action}; - this->on_delete = {*this, false, other.on_delete._action}; - return *this; - } + foreign_key_t& operator=(const foreign_key_t&) = delete; }; template @@ -3635,9 +3675,8 @@ namespace sqlite_orm { } /** - * Cs can be a class member pointer, a getter function member pointer or setter - * func member pointer - * Available in SQLite 3.6.19 or higher + * Cs can be a class member pointer or column pointer + * Available since SQLite 3.6.19 */ template struct foreign_key_intermediate_t { @@ -3645,21 +3684,31 @@ namespace sqlite_orm { tuple_type _columns; + /** + * Specify one or more target fields, which can either be class member pointers or column pointers. + */ template foreign_key_t> references(Rs... refs) { return {std::move(_columns), {std::forward(refs)...}}; } - template - foreign_key_t...>> references(Rs... refs) { - return {std::move(_columns), {{refs}...}}; + /** + * Specify one or more target fields, which are class member pointers of base classes. + */ + template + foreign_key_t> references(F Base::*... refs) { + static_assert(std::conjunction...>::value, + "Referenced fields must be from explicitly specified derived class"); + return {std::move(_columns), {refs...}}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto references(Rs... refs) { - using fk_type = foreign_key_t, Rs>...>>; - return fk_type{std::move(_columns), {{refs}...}}; + /** + * Specify one or more target fields, which are class member pointers of base classes. + */ + template + auto references(F Base::*... refs) { + return this->references>(refs...); } #endif }; @@ -3804,8 +3853,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 /** - * FOREIGN KEY constraint construction function that takes a member pointer as argument - * Available in SQLite 3.6.19 or higher + * FOREIGN KEY constraint builder function taking one or more fields [member pointer or column pointer] as argument. + * Available since SQLite 3.6.19 */ template internal::foreign_key_intermediate_t foreign_key(Cs... columns) { @@ -3813,24 +3862,24 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } /** - * FOREIGN KEY constraint construction function that takes member pointer of a derived class as argument. - * Available in SQLite 3.6.19 or higher + * FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * Available since SQLite 3.6.19 */ - template - internal::foreign_key_intermediate_t...> foreign_key(Cs... columns) { - return {{{columns}...}}; + template + internal::foreign_key_intermediate_t foreign_key(F Base::*... columns) { + static_assert(std::conjunction...>::value, + "Fields must be from explicitly specified derived class"); + return {{columns...}}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * FOREIGN KEY constraint construction function that takes member pointer of a derived class as argument. - * Available in SQLite 3.6.19 or higher + * FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * Available since SQLite 3.6.19 */ - template - auto foreign_key(Cs... columns) { - using namespace ::sqlite_orm::internal; - using builder_type = foreign_key_intermediate_t, Cs>...>; - return builder_type{{{columns}...}}; + template + auto foreign_key(F Base::*... columns) { + return foreign_key>(columns...); } #endif #endif @@ -12047,22 +12096,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../member_traits/member_traits.h" -// #include "../typed_comparator.h" - -namespace sqlite_orm { - - namespace internal { - - template - bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { - return false; - } - template - bool compare_any(const O& lhs, const O& rhs) { - return lhs == rhs; - } - } -} +// #include "../field_of.h" // #include "../type_traits.h" @@ -12330,7 +12364,7 @@ namespace sqlite_orm { iterate_tuple(this->elements, col_index_sequence_with_field_type{}, call_as_template_base([&res, &memberPointer, &object](const auto& column) { - if (compare_any(column.setter, memberPointer)) { + if (compare_fields(column.setter, memberPointer)) { res = &polyfill::invoke(column.member_pointer, object); } })); @@ -12424,7 +12458,7 @@ namespace sqlite_orm { iterate_tuple(this->elements, col_index_sequence_with_field_type{}, [&res, m](auto& c) { - if (compare_any(c.member_pointer, m) || compare_any(c.setter, m)) { + if (compare_fields(c.member_pointer, m) || compare_fields(c.setter, m)) { res = &c.name; } }); @@ -12589,8 +12623,8 @@ namespace sqlite_orm { check_if_is_type>::template fn, member_field_type_t>; iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { - if (compare_any(memberPointer, column.member_pointer) || - compare_any(memberPointer, column.setter)) { + if (compare_fields(memberPointer, column.member_pointer) || + compare_fields(memberPointer, column.setter)) { res = true; } }); diff --git a/tests/static_tests/column.cpp b/tests/static_tests/column.cpp index 2c794a069..afb43778b 100644 --- a/tests/static_tests/column.cpp +++ b/tests/static_tests/column.cpp @@ -4,9 +4,12 @@ #include "static_tests_common.h" using namespace sqlite_orm; +using internal::as_field_of; +using internal::compare_fields; +using internal::is_field_of_v; TEST_CASE("Column") { - { + SECTION("string type") { using column_type = decltype(make_column("name", &User::name)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -17,7 +20,7 @@ TEST_CASE("Column") { using field_type = column_type::field_type; STATIC_REQUIRE(type_is_nullable::value); } - { + SECTION("int type, object field") { using column_type = decltype(make_column("id", &User::id)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -25,17 +28,17 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("getter by const ref") { using column_type = decltype(make_column("id", &User::getIdByRefConst, &User::setIdByVal)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); - STATIC_REQUIRE(internal::is_field_of_v); - STATIC_REQUIRE(internal::is_field_of_v); + STATIC_REQUIRE(is_field_of_v); + STATIC_REQUIRE(is_field_of_v); } - { + SECTION("setter by val") { using column_type = decltype(make_column("id", &User::setIdByVal, &User::getIdByRefConst)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -43,7 +46,7 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("getter by ref") { using column_type = decltype(make_column("id", &User::getIdByRef, &User::setIdByConstRef)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -51,7 +54,7 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("setter by const ref") { using column_type = decltype(make_column("id", &User::setIdByConstRef, &User::getIdByRef)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -59,7 +62,7 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("getter by const val") { using column_type = decltype(make_column("id", &User::getIdByValConst, &User::setIdByRef)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -67,7 +70,7 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("setter by ref") { using column_type = decltype(make_column("id", &User::setIdByRef, &User::getIdByValConst)); STATIC_REQUIRE(std::tuple_size::value == 0); STATIC_REQUIRE(std::is_same::value); @@ -75,22 +78,31 @@ TEST_CASE("Column") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); } - { + SECTION("inheritance") { using column_type = decltype(column(&Token::id)); STATIC_REQUIRE(std::is_same::value); using field_type = column_type::field_type; + STATIC_REQUIRE(std::is_member_pointer::value); + STATIC_REQUIRE_FALSE(std::is_member_function_pointer::value); STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::type, Token>::value); STATIC_REQUIRE(std::is_same::type, Object>::value); - STATIC_REQUIRE(internal::is_field_of_v); - STATIC_REQUIRE(internal::is_field_of_v); - STATIC_REQUIRE(internal::is_field_of_v); - STATIC_REQUIRE_FALSE(internal::is_field_of_v); - STATIC_REQUIRE_FALSE(internal::is_field_of_v); - STATIC_REQUIRE_FALSE(internal::is_field_of_v); + STATIC_REQUIRE(is_field_of_v); + STATIC_REQUIRE(is_field_of_v); + STATIC_REQUIRE(is_field_of_v); + STATIC_REQUIRE_FALSE(is_field_of_v); + STATIC_REQUIRE_FALSE(is_field_of_v); + STATIC_REQUIRE_FALSE(is_field_of_v); + STATIC_REQUIRE(compare_fields(&Object::id, as_field_of(&Object::id))); + STATIC_REQUIRE(compare_fields(as_field_of(&Object::id), &Object::id)); + STATIC_REQUIRE(compare_fields(&User::id, &User::id)); + STATIC_REQUIRE(compare_fields(&User::setIdByVal, &User::setIdByVal)); + STATIC_REQUIRE_FALSE(compare_fields(&User::id, &Object::id)); + STATIC_REQUIRE_FALSE(compare_fields(&Object::id, &User::id)); + STATIC_REQUIRE_FALSE(compare_fields(&User::id, &User::name)); + STATIC_REQUIRE_FALSE(compare_fields(internal::empty_setter{}, &User::setIdByVal)); + STATIC_REQUIRE_FALSE(compare_fields(&User::setIdByVal, internal::empty_setter{})); STATIC_REQUIRE(std::is_same::type, Object>::value); STATIC_REQUIRE(std::is_same, field_type>::type, int>::value); - STATIC_REQUIRE(std::is_member_pointer::value); - STATIC_REQUIRE_FALSE(std::is_member_function_pointer::value); } } diff --git a/tests/static_tests/foreign_key.cpp b/tests/static_tests/foreign_key.cpp index 4a3aa30eb..50ca5f8fa 100644 --- a/tests/static_tests/foreign_key.cpp +++ b/tests/static_tests/foreign_key.cpp @@ -9,6 +9,8 @@ #if SQLITE_VERSION_NUMBER >= 3006019 using namespace sqlite_orm; +using internal::as_field_of_t; +using internal::column_pointer; TEST_CASE("foreign key static") { struct FunctionDecl { @@ -41,8 +43,12 @@ TEST_CASE("foreign key static") { std::string file_path; }; struct Base { - int id; + // Note: `long` was chosen as a different type than `int` for the primary key columns to ensure that the following static asserts work as expected. + long id; + long id2; + int parentId; + int parentId2; }; struct Derived : Base {}; #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -74,19 +80,71 @@ TEST_CASE("foreign key static") { STATIC_REQUIRE(std::is_same::value); STATIC_REQUIRE(std::is_same::value); - auto selfRefFk1 = foreign_key(column(&Derived::parentId)).references(column(&Derived::id)); - STATIC_REQUIRE(std::is_same::value); - STATIC_REQUIRE(std::is_same::value); + SECTION("inheritance, single fk") { + using selfRefFk1 = + decltype(foreign_key(column(&Derived::parentId)).references(column(&Derived::id))); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + using selfRefFk2 = decltype(foreign_key(&Derived::parentId).references(&Derived::id)); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); - auto selfRefFk2 = foreign_key(&Derived::parentId).references(&Derived::id); - STATIC_REQUIRE(std::is_same::value); - STATIC_REQUIRE(std::is_same::value); +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + using selfRefFk3 = decltype(foreign_key(&Derived::parentId).references(&Derived::id)); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same>>::value); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); +#endif + } + SECTION("inheritance, composite fk") { + using selfRefMultiFk1 = + decltype(foreign_key(column(&Derived::parentId), column(&Derived::parentId2)) + .references(column(&Derived::id), column(&Derived::id2))); + STATIC_REQUIRE(std::is_same, + column_pointer>>::value); + STATIC_REQUIRE(std::is_same, + column_pointer>>::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); + + using selfRefMultiFk2 = decltype(foreign_key(&Derived::parentId, &Derived::parentId2) + .references(&Derived::id, &Derived::id2)); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); + STATIC_REQUIRE(std::is_same::value); + STATIC_REQUIRE(std::is_same::value); #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - auto selfRefFk3 = foreign_key(&Derived::parentId).references(&Derived::id); - STATIC_REQUIRE(std::is_same::value); - STATIC_REQUIRE(std::is_same::value); + using selfRefMultiFk3 = decltype(foreign_key(&Derived::parentId, &Derived::parentId2) + .references(&Derived::id, &Derived::id2)); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); #endif + } auto storage = make_storage({}, make_table("files", make_column("path", &File::path, primary_key())), diff --git a/tests/static_tests/member_traits_tests.cpp b/tests/static_tests/member_traits_tests.cpp index 597fa9fa2..bbcf9dc2f 100644 --- a/tests/static_tests/member_traits_tests.cpp +++ b/tests/static_tests/member_traits_tests.cpp @@ -4,6 +4,8 @@ using namespace sqlite_orm; TEST_CASE("member_traits_tests") { + using internal::as_field_of; + using internal::as_field_of_t; using internal::getter_field_type_t; using internal::is_getter; using internal::is_setter; @@ -105,6 +107,8 @@ TEST_CASE("member_traits_tests") { } }; + struct DerivedUser : User {}; + STATIC_REQUIRE_FALSE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); @@ -291,4 +295,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); #endif + + STATIC_REQUIRE(is_same(&User::id))>, DerivedUser>::value); + STATIC_REQUIRE(is_same>, DerivedUser>::value); } From 77daa3b58c58e8e65f425626334a549be651ef04 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 5 Mar 2025 22:54:17 +0530 Subject: [PATCH 072/170] Added support for primary key table constraint with inheritance Like for foreign keys, primary keys now accept an explicit template parameter for a derived class. Additionally: * Unit tests for primary key table constraints with inheritance * Split primary key table and column constraint tests * Creating primary keys can be done at compile-time * Foreign keys can be counted at compile-time --- dev/constraints.h | 53 ++- dev/field_of.h | 2 + dev/statement_serializer.h | 2 +- dev/storage.h | 2 +- dev/storage_impl.h | 7 +- include/sqlite_orm/sqlite_orm.h | 66 ++- .../column_constraints/primary_key.cpp | 376 +++++++++--------- .../table_constraints/primary_key.cpp | 64 +++ tests/static_tests/primary_key.cpp | 36 ++ 9 files changed, 367 insertions(+), 241 deletions(-) create mode 100644 tests/statement_serializer_tests/table_constraints/primary_key.cpp create mode 100644 tests/static_tests/primary_key.cpp diff --git a/dev/constraints.h b/dev/constraints.h index 83fc3c2e9..5eb0fc033 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -54,7 +54,7 @@ namespace sqlite_orm { return *this; } #ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} + constexpr primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} #endif }; @@ -65,59 +65,58 @@ namespace sqlite_orm { */ template struct primary_key_t : primary_key_base { - using self = primary_key_t; using order_by = primary_key_base::order_by; using columns_tuple = std::tuple; columns_tuple columns; - primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} + constexpr primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} - self asc() const { + constexpr primary_key_t asc() const { auto res = *this; res.options.asc_option = order_by::ascending; return res; } - self desc() const { + constexpr primary_key_t desc() const { auto res = *this; res.options.asc_option = order_by::descending; return res; } - primary_key_with_autoincrement autoincrement() const { + constexpr primary_key_with_autoincrement autoincrement() const { return {*this}; } - self on_conflict_rollback() const { + constexpr primary_key_t on_conflict_rollback() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::rollback; return res; } - self on_conflict_abort() const { + constexpr primary_key_t on_conflict_abort() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::abort; return res; } - self on_conflict_fail() const { + constexpr primary_key_t on_conflict_fail() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::fail; return res; } - self on_conflict_ignore() const { + constexpr primary_key_t on_conflict_ignore() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::ignore; return res; } - self on_conflict_replace() const { + constexpr primary_key_t on_conflict_replace() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::replace; @@ -536,7 +535,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 /** - * FOREIGN KEY constraint builder function taking one or more fields [member pointer or column pointer] as argument. + * Composite FOREIGN KEY constraint builder function taking one or more fields [member pointer or column pointer] as argument. * Available since SQLite 3.6.19 */ template @@ -545,7 +544,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } /** - * FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * Composite FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. * Available since SQLite 3.6.19 */ template @@ -557,7 +556,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * Composite FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. * Available since SQLite 3.6.19 */ template @@ -637,14 +636,34 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { * PRIMARY KEY table constraint builder function. */ template - internal::primary_key_t primary_key(Cs... cs) { + constexpr internal::primary_key_t primary_key(Cs... cs) { return {{std::forward(cs)...}}; } /** - * PRIMARY KEY column constraint builder function. + * Composite PRIMARY KEY table constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. */ - inline internal::primary_key_t<> primary_key() { + template + constexpr internal::primary_key_t primary_key(F Base::*... columns) { + static_assert(std::conjunction...>::value, + "Fields must be from explicitly specified derived class"); + return {{columns...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Composite PRIMARY KEY table constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + */ + template + constexpr auto primary_key(F Base::*... columns) { + return primary_key>(columns...); + } +#endif + + /** + * PRIMARY KEY column constraint builder function (used at a single column). + */ + inline constexpr internal::primary_key_t<> primary_key() { return {{}}; } diff --git a/dev/field_of.h b/dev/field_of.h index 6036dba36..c00869819 100644 --- a/dev/field_of.h +++ b/dev/field_of.h @@ -4,6 +4,8 @@ #include // std::enable_if, std::is_convertible, std::bool_constant #endif +#include "member_traits/member_traits.h" + namespace sqlite_orm { namespace internal { template diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 0d9f48bcb..ec95e9dc5 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1048,7 +1048,7 @@ namespace sqlite_orm { ss << " ON CONFLICT " << serialize(statement.options.conflict_clause, context); } using columns_tuple = typename statement_type::columns_tuple; - const size_t columnsCount = std::tuple_size::value; + constexpr size_t columnsCount = std::tuple_size::value; if (columnsCount) { ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; } diff --git a/dev/storage.h b/dev/storage.h index 04011a9b8..2598e8d48 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -112,7 +112,7 @@ namespace sqlite_orm { storage_base{std::move(filename), storage_opt_or_default(options), storage_opt_or_default(options), - foreign_keys_count(dbObjects)}, + foreign_keys_count()}, db_objects{std::move(dbObjects)} {} storage_t(const storage_t&) = default; diff --git a/dev/storage_impl.h b/dev/storage_impl.h index b90efd1e5..e6536ecbc 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -24,10 +24,11 @@ namespace sqlite_orm { using tables_index_sequence = filter_tuple_sequence_t; template = true> - int foreign_keys_count(const DBOs& dbObjects) { + constexpr int foreign_keys_count() { int res = 0; - iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { - res += table.template count_of(); + iterate_tuple(tables_index_sequence{}, [&res](const auto* dummy) { + using table_type = std::remove_pointer_t; + res += table_type::template count_of(); }); return res; } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 153711002..20564ed33 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -2184,6 +2184,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #include // std::enable_if, std::is_convertible, std::bool_constant #endif +// #include "member_traits/member_traits.h" + namespace sqlite_orm { namespace internal { template @@ -3371,7 +3373,7 @@ namespace sqlite_orm { return *this; } #ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} + constexpr primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} #endif }; @@ -3382,59 +3384,58 @@ namespace sqlite_orm { */ template struct primary_key_t : primary_key_base { - using self = primary_key_t; using order_by = primary_key_base::order_by; using columns_tuple = std::tuple; columns_tuple columns; - primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} + constexpr primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} - self asc() const { + constexpr primary_key_t asc() const { auto res = *this; res.options.asc_option = order_by::ascending; return res; } - self desc() const { + constexpr primary_key_t desc() const { auto res = *this; res.options.asc_option = order_by::descending; return res; } - primary_key_with_autoincrement autoincrement() const { + constexpr primary_key_with_autoincrement autoincrement() const { return {*this}; } - self on_conflict_rollback() const { + constexpr primary_key_t on_conflict_rollback() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::rollback; return res; } - self on_conflict_abort() const { + constexpr primary_key_t on_conflict_abort() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::abort; return res; } - self on_conflict_fail() const { + constexpr primary_key_t on_conflict_fail() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::fail; return res; } - self on_conflict_ignore() const { + constexpr primary_key_t on_conflict_ignore() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::ignore; return res; } - self on_conflict_replace() const { + constexpr primary_key_t on_conflict_replace() const { auto res = *this; res.options.conflict_clause_is_on = true; res.options.conflict_clause = conflict_clause_t::replace; @@ -3853,7 +3854,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 /** - * FOREIGN KEY constraint builder function taking one or more fields [member pointer or column pointer] as argument. + * Composite FOREIGN KEY constraint builder function taking one or more fields [member pointer or column pointer] as argument. * Available since SQLite 3.6.19 */ template @@ -3862,7 +3863,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } /** - * FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * Composite FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. * Available since SQLite 3.6.19 */ template @@ -3874,7 +3875,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * Composite FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. * Available since SQLite 3.6.19 */ template @@ -3954,14 +3955,34 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { * PRIMARY KEY table constraint builder function. */ template - internal::primary_key_t primary_key(Cs... cs) { + constexpr internal::primary_key_t primary_key(Cs... cs) { return {{std::forward(cs)...}}; } /** - * PRIMARY KEY column constraint builder function. + * Composite PRIMARY KEY table constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + */ + template + constexpr internal::primary_key_t primary_key(F Base::*... columns) { + static_assert(std::conjunction...>::value, + "Fields must be from explicitly specified derived class"); + return {{columns...}}; + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Composite PRIMARY KEY table constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + */ + template + constexpr auto primary_key(F Base::*... columns) { + return primary_key>(columns...); + } +#endif + + /** + * PRIMARY KEY column constraint builder function (used at a single column). */ - inline internal::primary_key_t<> primary_key() { + inline constexpr internal::primary_key_t<> primary_key() { return {{}}; } @@ -12715,10 +12736,11 @@ namespace sqlite_orm { using tables_index_sequence = filter_tuple_sequence_t; template = true> - int foreign_keys_count(const DBOs& dbObjects) { + constexpr int foreign_keys_count() { int res = 0; - iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { - res += table.template count_of(); + iterate_tuple(tables_index_sequence{}, [&res](const auto* dummy) { + using table_type = std::remove_pointer_t; + res += table_type::template count_of(); }); return res; } @@ -20820,7 +20842,7 @@ namespace sqlite_orm { ss << " ON CONFLICT " << serialize(statement.options.conflict_clause, context); } using columns_tuple = typename statement_type::columns_tuple; - const size_t columnsCount = std::tuple_size::value; + constexpr size_t columnsCount = std::tuple_size::value; if (columnsCount) { ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; } @@ -22638,7 +22660,7 @@ namespace sqlite_orm { storage_base{std::move(filename), storage_opt_or_default(options), storage_opt_or_default(options), - foreign_keys_count(dbObjects)}, + foreign_keys_count()}, db_objects{std::move(dbObjects)} {} storage_t(const storage_t&) = default; diff --git a/tests/statement_serializer_tests/column_constraints/primary_key.cpp b/tests/statement_serializer_tests/column_constraints/primary_key.cpp index 53e5fadf5..0348456a7 100644 --- a/tests/statement_serializer_tests/column_constraints/primary_key.cpp +++ b/tests/statement_serializer_tests/column_constraints/primary_key.cpp @@ -3,207 +3,189 @@ using namespace sqlite_orm; -TEST_CASE("statement_serializer primary key") { +TEST_CASE("statement_serializer primary key column constraint") { std::string value; decltype(value) expected; - SECTION("empty") { - internal::db_objects_tuple<> storage; - internal::serializer_context> context{storage}; - auto pk = primary_key(); + + using db_objects_t = internal::db_objects_tuple<>; + auto dbObjects = db_objects_t{}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + + SECTION("default pk") { + constexpr auto pk = primary_key(); value = serialize(pk, context); expected = "PRIMARY KEY"; } - SECTION("not empty") { - struct User { - int id = 0; - std::string name; - }; - auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); - using db_objects_t = internal::db_objects_tuple; - auto dbObjects = db_objects_t{table}; - using context_t = internal::serializer_context; - context_t context{dbObjects}; - SECTION("single column pk") { - auto pk = primary_key(&User::id); - value = serialize(pk, context); - expected = R"(PRIMARY KEY("id"))"; - } - SECTION("double column pk") { - auto pk = primary_key(&User::id, &User::name); - value = serialize(pk, context); - expected = R"(PRIMARY KEY("id", "name"))"; - } - SECTION("empty pk asc") { - auto pk = primary_key().asc(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC"; - } - SECTION("empty pk asc autoincrement") { - auto pk = primary_key().asc().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC AUTOINCREMENT"; - } - SECTION("empty pk desc") { - auto pk = primary_key().desc(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC"; - } - SECTION("empty pk desc autoincrement") { - auto pk = primary_key().desc().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC AUTOINCREMENT"; - } - SECTION("empty pk on conflict rollback") { - auto pk = primary_key().on_conflict_rollback(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT ROLLBACK"; - } - SECTION("empty pk on conflict abort") { - auto pk = primary_key().on_conflict_abort(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT ABORT"; - } - SECTION("empty pk on conflict fail") { - auto pk = primary_key().on_conflict_fail(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT FAIL"; - } - SECTION("empty pk on conflict ignore") { - auto pk = primary_key().on_conflict_ignore(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT IGNORE"; - } - SECTION("empty pk on conflict replace") { - auto pk = primary_key().on_conflict_replace(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT REPLACE"; - } - SECTION("empty pk asc on conflict rollback") { - auto pk = primary_key().asc().on_conflict_rollback(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT ROLLBACK"; - } - SECTION("empty pk asc on conflict abort") { - auto pk = primary_key().asc().on_conflict_abort(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT ABORT"; - } - SECTION("empty pk asc on conflict fail") { - auto pk = primary_key().asc().on_conflict_fail(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT FAIL"; - } - SECTION("empty pk asc on conflict ignore") { - auto pk = primary_key().asc().on_conflict_ignore(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT IGNORE"; - } - SECTION("empty pk asc on conflict replace") { - auto pk = primary_key().asc().on_conflict_replace(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT REPLACE"; - } - SECTION("empty pk desc on conflict rollback") { - auto pk = primary_key().desc().on_conflict_rollback(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT ROLLBACK"; - } - SECTION("empty pk desc on conflict abort") { - auto pk = primary_key().desc().on_conflict_abort(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT ABORT"; - } - SECTION("empty pk desc on conflict fail") { - auto pk = primary_key().desc().on_conflict_fail(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT FAIL"; - } - SECTION("empty pk desc on conflict ignore") { - auto pk = primary_key().desc().on_conflict_ignore(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT IGNORE"; - } - SECTION("empty pk desc on conflict replace") { - auto pk = primary_key().desc().on_conflict_replace(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT REPLACE"; - } + SECTION("pk asc") { + constexpr auto pk = primary_key().asc(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC"; + } + SECTION("pk asc autoincrement") { + constexpr auto pk = primary_key().asc().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC AUTOINCREMENT"; + } + SECTION("pk desc") { + constexpr auto pk = primary_key().desc(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC"; + } + SECTION("pk desc autoincrement") { + constexpr auto pk = primary_key().desc().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC AUTOINCREMENT"; + } + SECTION("pk on conflict rollback") { + constexpr auto pk = primary_key().on_conflict_rollback(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ROLLBACK"; + } + SECTION("pk on conflict abort") { + constexpr auto pk = primary_key().on_conflict_abort(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ABORT"; + } + SECTION("pk on conflict fail") { + constexpr auto pk = primary_key().on_conflict_fail(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT FAIL"; + } + SECTION("pk on conflict ignore") { + constexpr auto pk = primary_key().on_conflict_ignore(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT IGNORE"; + } + SECTION("pk on conflict replace") { + constexpr auto pk = primary_key().on_conflict_replace(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT REPLACE"; + } + SECTION("pk asc on conflict rollback") { + constexpr auto pk = primary_key().asc().on_conflict_rollback(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ROLLBACK"; + } + SECTION("pk asc on conflict abort") { + constexpr auto pk = primary_key().asc().on_conflict_abort(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ABORT"; + } + SECTION("pk asc on conflict fail") { + constexpr auto pk = primary_key().asc().on_conflict_fail(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT FAIL"; + } + SECTION("pk asc on conflict ignore") { + constexpr auto pk = primary_key().asc().on_conflict_ignore(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT IGNORE"; + } + SECTION("pk asc on conflict replace") { + constexpr auto pk = primary_key().asc().on_conflict_replace(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT REPLACE"; + } + SECTION("pk desc on conflict rollback") { + constexpr auto pk = primary_key().desc().on_conflict_rollback(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ROLLBACK"; + } + SECTION("pk desc on conflict abort") { + constexpr auto pk = primary_key().desc().on_conflict_abort(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ABORT"; + } + SECTION("pk desc on conflict fail") { + constexpr auto pk = primary_key().desc().on_conflict_fail(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT FAIL"; + } + SECTION("pk desc on conflict ignore") { + constexpr auto pk = primary_key().desc().on_conflict_ignore(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT IGNORE"; + } + SECTION("pk desc on conflict replace") { + constexpr auto pk = primary_key().desc().on_conflict_replace(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT REPLACE"; + } - SECTION("empty pk on conflict rollback autoincrement") { - auto pk = primary_key().on_conflict_rollback().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT"; - } - SECTION("empty pk on conflict abort autoincrement") { - auto pk = primary_key().on_conflict_abort().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT"; - } - SECTION("empty pk on conflict fail autoincrement") { - auto pk = primary_key().on_conflict_fail().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT FAIL AUTOINCREMENT"; - } - SECTION("empty pk on conflict ignore autoincrement") { - auto pk = primary_key().on_conflict_ignore().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT IGNORE AUTOINCREMENT"; - } - SECTION("empty pk on conflict replace autoincrement") { - auto pk = primary_key().on_conflict_replace().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ON CONFLICT REPLACE AUTOINCREMENT"; - } - SECTION("empty pk asc on conflict rollback autoincrement") { - auto pk = primary_key().asc().on_conflict_rollback().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT ROLLBACK AUTOINCREMENT"; - } - SECTION("empty pk asc on conflict abort autoincrement") { - auto pk = primary_key().asc().on_conflict_abort().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT ABORT AUTOINCREMENT"; - } - SECTION("empty pk asc on conflict fail autoincrement") { - auto pk = primary_key().asc().on_conflict_fail().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT FAIL AUTOINCREMENT"; - } - SECTION("empty pk asc on conflict ignore autoincrement") { - auto pk = primary_key().asc().on_conflict_ignore().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT IGNORE AUTOINCREMENT"; - } - SECTION("empty pk asc on conflict replace autoincrement") { - auto pk = primary_key().asc().on_conflict_replace().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY ASC ON CONFLICT REPLACE AUTOINCREMENT"; - } - SECTION("empty pk desc on conflict rollback autoincrement") { - auto pk = primary_key().desc().on_conflict_rollback().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT ROLLBACK AUTOINCREMENT"; - } - SECTION("empty pk desc on conflict abort autoincrement") { - auto pk = primary_key().desc().on_conflict_abort().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT ABORT AUTOINCREMENT"; - } - SECTION("empty pk desc on conflict fail autoincrement") { - auto pk = primary_key().desc().on_conflict_fail().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT FAIL AUTOINCREMENT"; - } - SECTION("empty pk desc on conflict ignore autoincrement") { - auto pk = primary_key().desc().on_conflict_ignore().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT IGNORE AUTOINCREMENT"; - } - SECTION("empty pk desc on conflict replace autoincrement") { - auto pk = primary_key().desc().on_conflict_replace().autoincrement(); - value = serialize(pk, context); - expected = "PRIMARY KEY DESC ON CONFLICT REPLACE AUTOINCREMENT"; - } - } - REQUIRE(value == expected); + SECTION("pk on conflict rollback autoincrement") { + constexpr auto pk = primary_key().on_conflict_rollback().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT"; + } + SECTION("pk on conflict abort autoincrement") { + constexpr auto pk = primary_key().on_conflict_abort().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT"; + } + SECTION("pk on conflict fail autoincrement") { + constexpr auto pk = primary_key().on_conflict_fail().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT FAIL AUTOINCREMENT"; + } + SECTION("pk on conflict ignore autoincrement") { + constexpr auto pk = primary_key().on_conflict_ignore().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT IGNORE AUTOINCREMENT"; + } + SECTION("pk on conflict replace autoincrement") { + constexpr auto pk = primary_key().on_conflict_replace().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ON CONFLICT REPLACE AUTOINCREMENT"; + } + SECTION("pk asc on conflict rollback autoincrement") { + constexpr auto pk = primary_key().asc().on_conflict_rollback().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ROLLBACK AUTOINCREMENT"; + } + SECTION("pk asc on conflict abort autoincrement") { + constexpr auto pk = primary_key().asc().on_conflict_abort().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT ABORT AUTOINCREMENT"; + } + SECTION("pk asc on conflict fail autoincrement") { + constexpr auto pk = primary_key().asc().on_conflict_fail().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT FAIL AUTOINCREMENT"; + } + SECTION("pk asc on conflict ignore autoincrement") { + constexpr auto pk = primary_key().asc().on_conflict_ignore().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT IGNORE AUTOINCREMENT"; + } + SECTION("pk asc on conflict replace autoincrement") { + constexpr auto pk = primary_key().asc().on_conflict_replace().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY ASC ON CONFLICT REPLACE AUTOINCREMENT"; + } + SECTION("pk desc on conflict rollback autoincrement") { + constexpr auto pk = primary_key().desc().on_conflict_rollback().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ROLLBACK AUTOINCREMENT"; + } + SECTION("pk desc on conflict abort autoincrement") { + constexpr auto pk = primary_key().desc().on_conflict_abort().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT ABORT AUTOINCREMENT"; + } + SECTION("pk desc on conflict fail autoincrement") { + constexpr auto pk = primary_key().desc().on_conflict_fail().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT FAIL AUTOINCREMENT"; + } + SECTION("pk desc on conflict ignore autoincrement") { + constexpr auto pk = primary_key().desc().on_conflict_ignore().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT IGNORE AUTOINCREMENT"; + } + SECTION("pk desc on conflict replace autoincrement") { + constexpr auto pk = primary_key().desc().on_conflict_replace().autoincrement(); + value = serialize(pk, context); + expected = "PRIMARY KEY DESC ON CONFLICT REPLACE AUTOINCREMENT"; + } } diff --git a/tests/statement_serializer_tests/table_constraints/primary_key.cpp b/tests/statement_serializer_tests/table_constraints/primary_key.cpp new file mode 100644 index 000000000..d843e3320 --- /dev/null +++ b/tests/statement_serializer_tests/table_constraints/primary_key.cpp @@ -0,0 +1,64 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer primary key table constraint") { + struct User { + int id = 0; + std::string name; + }; + struct DerivedUser : User {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr orm_table_reference auto derived_user = c(); +#endif + + std::string value; + decltype(value) expected; + SECTION("with inheritance") { + auto table1 = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); + auto table2 = make_table("derived_users", + make_column("derived_id", &DerivedUser::id), + make_column("derived_name", &DerivedUser::name)); + using db_objects_t = internal::db_objects_tuple; + auto dbObjects = db_objects_t{table1, table2}; + using context_t = internal::serializer_context; + context_t context{dbObjects}; + context.skip_table_name = false; + + SECTION("single column pk") { + constexpr auto pk = primary_key(&User::id); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("id"))"; + } + SECTION("composite pk") { + constexpr auto pk = primary_key(&User::id, &User::name); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("id", "name"))"; + } + SECTION("base, composite pk") { + constexpr auto pk = primary_key(&User::id, &User::name); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("id", "name"))"; + } + SECTION("inheritance, composite fk, column pointer") { + constexpr auto pk = + primary_key(column(&DerivedUser::id), column(&DerivedUser::name)); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("derived_id", "derived_name"))"; + } + SECTION("inheritance, composite fk, as field") { + constexpr auto pk = primary_key(&DerivedUser::id, &DerivedUser::name); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("derived_id", "derived_name"))"; + } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("inheritance, composite fk, as field") { + constexpr auto pk = primary_key(&DerivedUser::id, &DerivedUser::name); + value = serialize(pk, context); + expected = R"(PRIMARY KEY("derived_id", "derived_name"))"; + } +#endif + REQUIRE(value == expected); + } +} diff --git a/tests/static_tests/primary_key.cpp b/tests/static_tests/primary_key.cpp new file mode 100644 index 000000000..1c2c9429a --- /dev/null +++ b/tests/static_tests/primary_key.cpp @@ -0,0 +1,36 @@ +#include +#include + +using namespace sqlite_orm; +using internal::as_field_of_t; +using internal::column_pointer; + +TEST_CASE("primary key static") { + struct Base { + int id; + int id2; + }; + struct Derived : Base {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr orm_table_reference auto derived = c(); +#endif + + SECTION("inheritance, composite pk") { + using compositePk1 = decltype(primary_key(column(&Derived::id), column(&Derived::id2))); + STATIC_REQUIRE(std::is_same, + column_pointer>>::value); + + using compositePk2 = decltype(primary_key(&Derived::id, &Derived::id2)); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + using compositePk3 = decltype(primary_key(&Derived::id, &Derived::id2)); + STATIC_REQUIRE(std::is_same, + as_field_of_t>>::value); +#endif + } +} From b7104cdbc12475d93719b7efd28f8a82729e9f11 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 6 Mar 2025 11:18:20 +0530 Subject: [PATCH 073/170] Corrected namespace of metafunction `conjunction` --- dev/constraints.h | 6 +++--- include/sqlite_orm/sqlite_orm.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/constraints.h b/dev/constraints.h index 5eb0fc033..1bbe20c1a 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -379,7 +379,7 @@ namespace sqlite_orm { */ template foreign_key_t> references(F Base::*... refs) { - static_assert(std::conjunction...>::value, + static_assert(polyfill::conjunction...>::value, "Referenced fields must be from explicitly specified derived class"); return {std::move(_columns), {refs...}}; } @@ -549,7 +549,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { */ template internal::foreign_key_intermediate_t foreign_key(F Base::*... columns) { - static_assert(std::conjunction...>::value, + static_assert(polyfill::conjunction...>::value, "Fields must be from explicitly specified derived class"); return {{columns...}}; } @@ -645,7 +645,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { */ template constexpr internal::primary_key_t primary_key(F Base::*... columns) { - static_assert(std::conjunction...>::value, + static_assert(polyfill::conjunction...>::value, "Fields must be from explicitly specified derived class"); return {{columns...}}; } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 20564ed33..71a216d6b 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -3698,7 +3698,7 @@ namespace sqlite_orm { */ template foreign_key_t> references(F Base::*... refs) { - static_assert(std::conjunction...>::value, + static_assert(polyfill::conjunction...>::value, "Referenced fields must be from explicitly specified derived class"); return {std::move(_columns), {refs...}}; } @@ -3868,7 +3868,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { */ template internal::foreign_key_intermediate_t foreign_key(F Base::*... columns) { - static_assert(std::conjunction...>::value, + static_assert(polyfill::conjunction...>::value, "Fields must be from explicitly specified derived class"); return {{columns...}}; } @@ -3964,7 +3964,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { */ template constexpr internal::primary_key_t primary_key(F Base::*... columns) { - static_assert(std::conjunction...>::value, + static_assert(polyfill::conjunction...>::value, "Fields must be from explicitly specified derived class"); return {{columns...}}; } From 99d12ba8a30306fb1faea20329ac860bb9fe921c Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 6 Mar 2025 21:23:28 +0530 Subject: [PATCH 074/170] Updated comments on primary and foreign key builder functions --- dev/constraints.h | 24 +++++++++++++++--------- include/sqlite_orm/sqlite_orm.h | 24 +++++++++++++++--------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/dev/constraints.h b/dev/constraints.h index 1bbe20c1a..acfe247b2 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -367,7 +367,7 @@ namespace sqlite_orm { tuple_type _columns; /** - * Specify one or more target fields, which can either be class member pointers or column pointers. + * Specify one or more target fields, which can either be pointers to class members or column pointers. */ template foreign_key_t> references(Rs... refs) { @@ -375,7 +375,8 @@ namespace sqlite_orm { } /** - * Specify one or more target fields, which are class member pointers of base classes. + * Specify one or more target fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. */ template foreign_key_t> references(F Base::*... refs) { @@ -386,7 +387,8 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Specify one or more target fields, which are class member pointers of base classes. + * Specify one or more target fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. */ template auto references(F Base::*... refs) { @@ -535,7 +537,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 /** - * Composite FOREIGN KEY constraint builder function taking one or more fields [member pointer or column pointer] as argument. + * FOREIGN KEY constraint builder function taking one or more fields, which can either be pointers to class members or column pointers. * Available since SQLite 3.6.19 */ template @@ -544,7 +546,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } /** - * Composite FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * FOREIGN KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. * Available since SQLite 3.6.19 */ template @@ -556,7 +559,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Composite FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * FOREIGN KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. * Available since SQLite 3.6.19 */ template @@ -633,7 +637,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #endif /** - * PRIMARY KEY table constraint builder function. + * PRIMARY KEY constraint builder function taking one or more fields, which can either be pointers to class members or column pointers. */ template constexpr internal::primary_key_t primary_key(Cs... cs) { @@ -641,7 +645,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } /** - * Composite PRIMARY KEY table constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * PRIMARY KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. */ template constexpr internal::primary_key_t primary_key(F Base::*... columns) { @@ -652,7 +657,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Composite PRIMARY KEY table constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * PRIMARY KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. */ template constexpr auto primary_key(F Base::*... columns) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 71a216d6b..edb19b8d1 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -3686,7 +3686,7 @@ namespace sqlite_orm { tuple_type _columns; /** - * Specify one or more target fields, which can either be class member pointers or column pointers. + * Specify one or more target fields, which can either be pointers to class members or column pointers. */ template foreign_key_t> references(Rs... refs) { @@ -3694,7 +3694,8 @@ namespace sqlite_orm { } /** - * Specify one or more target fields, which are class member pointers of base classes. + * Specify one or more target fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. */ template foreign_key_t> references(F Base::*... refs) { @@ -3705,7 +3706,8 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Specify one or more target fields, which are class member pointers of base classes. + * Specify one or more target fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. */ template auto references(F Base::*... refs) { @@ -3854,7 +3856,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 /** - * Composite FOREIGN KEY constraint builder function taking one or more fields [member pointer or column pointer] as argument. + * FOREIGN KEY constraint builder function taking one or more fields, which can either be pointers to class members or column pointers. * Available since SQLite 3.6.19 */ template @@ -3863,7 +3865,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } /** - * Composite FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * FOREIGN KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. * Available since SQLite 3.6.19 */ template @@ -3875,7 +3878,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Composite FOREIGN KEY constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * FOREIGN KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. * Available since SQLite 3.6.19 */ template @@ -3952,7 +3956,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #endif /** - * PRIMARY KEY table constraint builder function. + * PRIMARY KEY constraint builder function taking one or more fields, which can either be pointers to class members or column pointers. */ template constexpr internal::primary_key_t primary_key(Cs... cs) { @@ -3960,7 +3964,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } /** - * Composite PRIMARY KEY table constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * PRIMARY KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. */ template constexpr internal::primary_key_t primary_key(F Base::*... columns) { @@ -3971,7 +3976,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Composite PRIMARY KEY table constraint builder function taking one or more fields from a derived class as a member pointer of a base class as argument. + * PRIMARY KEY constraint builder function taking one or more fields that are member pointers of base classes, + * specifying the derived class as an explicit template argument. */ template constexpr auto primary_key(F Base::*... columns) { From c44a36e9c377ddd21897d79c8c22c70e1c0348a1 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 20 Mar 2025 11:30:15 +0200 Subject: [PATCH 075/170] Fixed binding values in several 'order by' clauses --- dev/ast_iterator.h | 10 +++ include/sqlite_orm/sqlite_orm.h | 10 +++ tests/ast_iterator_tests.cpp | 16 +++++ .../statement_serializer_tests/conditions.cpp | 67 +++++++++++++++---- 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index 74bb5af6e..d5a5ceba0 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -707,6 +707,16 @@ namespace sqlite_orm { } }; + template + struct ast_iterator, void> { + using node_type = multi_order_by_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + template struct ast_iterator, void> { using node_type = collate_t; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index edb19b8d1..a97a62719 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -16052,6 +16052,16 @@ namespace sqlite_orm { } }; + template + struct ast_iterator, void> { + using node_type = multi_order_by_t; + + template + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + template struct ast_iterator, void> { using node_type = collate_t; diff --git a/tests/ast_iterator_tests.cpp b/tests/ast_iterator_tests.cpp index c56580bfd..1b31a29f5 100644 --- a/tests/ast_iterator_tests.cpp +++ b/tests/ast_iterator_tests.cpp @@ -183,6 +183,22 @@ TEST_CASE("ast_iterator") { auto node = order_by(1); iterate_ast(node, lambda); } + SECTION("normal") { + auto node = order_by(&User::id); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("multi, single") { + auto node = multi_order_by(order_by(&User::id)); + expected.push_back(typeid(&User::id)); + iterate_ast(node, lambda); + } + SECTION("multi, several") { + auto node = multi_order_by(order_by(&User::id), order_by(&User::name)); + expected.push_back(typeid(&User::id)); + expected.push_back(typeid(&User::name)); + iterate_ast(node, lambda); + } SECTION("sole column alias") { auto node = order_by(get()); iterate_ast(node, lambda); diff --git a/tests/statement_serializer_tests/conditions.cpp b/tests/statement_serializer_tests/conditions.cpp index 31a6db640..48d0b1b46 100644 --- a/tests/statement_serializer_tests/conditions.cpp +++ b/tests/statement_serializer_tests/conditions.cpp @@ -6,17 +6,18 @@ using namespace sqlite_orm; TEST_CASE("statement_serializer conditions") { std::string value, expected; - SECTION("using") { - struct User { - int64 id; - }; + struct User { + int64 id; + std::string name; + }; - auto t1 = make_table("user", make_column("id", &User::id)); - auto storage = internal::db_objects_tuple{t1}; - using db_objects_tuple = decltype(storage); + auto t1 = make_table("user", make_column("id", &User::id), make_column("name", &User::name)); + auto storage = internal::db_objects_tuple{t1}; + using db_objects_tuple = decltype(storage); - internal::serializer_context ctx{storage}; + internal::serializer_context ctx{storage}; + SECTION("using") { SECTION("using column") { auto expression = using_(&User::id); value = serialize(expression, ctx); @@ -29,16 +30,56 @@ TEST_CASE("statement_serializer conditions") { } } SECTION("order by") { - auto storage = internal::db_objects_tuple<>{}; - using db_objects_tuple = decltype(storage); - - internal::serializer_context ctx{storage}; - SECTION("positional ordinal") { auto expression = order_by(1); value = serialize(expression, ctx); expected = "ORDER BY 1"; } + SECTION("normal") { + auto expression = order_by(&User::id); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id")"; + } + SECTION("asc") { + auto expression = order_by(&User::id).asc(); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" ASC)"; + } + SECTION("desc") { + auto expression = order_by(&User::id).desc(); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" DESC)"; + } + SECTION("multi, single") { + auto expression = multi_order_by(order_by(&User::id)); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id")"; + } + SECTION("multi, single, asc") { + auto expression = multi_order_by(order_by(&User::id).asc()); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" ASC)"; + } + SECTION("multi, single, desc") { + auto expression = multi_order_by(order_by(&User::id).desc()); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" DESC)"; + } + SECTION("multi, several") { + auto expression = multi_order_by(order_by(&User::id), order_by(&User::name)); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id", "user"."name")"; + } + SECTION("multi, several, asc") { + auto expression = multi_order_by(order_by(&User::id).asc(), order_by(&User::name).asc()); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" ASC, "user"."name" ASC)"; + } + SECTION("multi, several, desc") { + auto expression = multi_order_by(order_by(&User::id).desc(), order_by(&User::name).desc()); + value = serialize(expression, ctx); + expected = R"(ORDER BY "user"."id" DESC, "user"."name" DESC)"; + } } REQUIRE(value == expected); From 3a8716ea53ab9d981feb083d9b6c68cbb05ac744 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 20 Mar 2025 11:31:58 +0200 Subject: [PATCH 076/170] appveyor: Updated vcpkg environment to 2025.02.14 --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 0fb3f7dc3..685b2c87e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -113,7 +113,7 @@ for: install: - |- cd C:\Tools\vcpkg - git fetch --tags && git checkout 2025.01.13 + git fetch --tags && git checkout 2025.02.14 cd %APPVEYOR_BUILD_FOLDER% C:\Tools\vcpkg\bootstrap-vcpkg.bat -disableMetrics C:\Tools\vcpkg\vcpkg integrate install @@ -146,7 +146,7 @@ for: install: - |- pushd $HOME/vcpkg - git fetch --tags && git checkout 2025.01.13 + git fetch --tags && git checkout 2025.02.14 popd $HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics $HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets @@ -174,7 +174,7 @@ for: # using custom vcpkg triplets for building and linking dynamic dependent libraries install: - |- - git clone --depth 1 --branch 2025.01.13 https://github.com/microsoft/vcpkg.git $HOME/vcpkg + git clone --depth 1 --branch 2025.02.14 https://github.com/microsoft/vcpkg.git $HOME/vcpkg $HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics $HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets vcpkg install sqlite3[core,dbstat,math,json1,fts5,soundex] catch2 --overlay-triplets=vcpkg/triplets From 88a15d9632b5eb79cb67ba6353b1616f764e8ee7 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 30 Jan 2025 13:45:39 -0800 Subject: [PATCH 077/170] open_mode and vfs_object implementation squash add test skeleton add open flags rename file move test file, finish test code, add to cmake fixup clang error add another make_storage overload remove overload fixup rename to v2, fixup tests fixup copy ctor remove int flags option add platform definitions add vfs file rename tests update tests and gen code rename to vfs_t implement vfs enum remove default arg for connection holder clarify test log add vfs getter and default vfs def rename vfs_default, add public getter swap from different method name to overload cleanup macros and fix linux macros more vfs cleanup and test fixup re-run gen remove unnecessary pack indexing check add open_mode.h add to header fixup win macro error use enum value instead of constexpr regenerate to include vfs move test file convert to constexpr, set up open_mode tests fully set up open mode in ctors rename vfs_t to vfs_mode begin renaming open_mode rename open_mode enum scoping fixups add readonly; need to attempt fix fix readonly call remove usage of tmpnam rename mac macro to apple rename file remove pointless line update gen run tests for mem as well reword test variable fixup test on Windows fix gcc err run gen fixup add return path to silence MSVC warn fixup pragma once placements remove inline no nested namespaces cleanup serializers add include change to serialize type remove unneeded namespacing just call it all open_mode implement storage_options remove auto inject options into connection add _t suffix large batch of name fixes separate static tests remove unused comments remove unused include revert _t suffix rename member vars rename to vfs_object fix weird typo more naming fixups fix namespacing attempt to fix MSVC crash real string fix --- dev/connection_holder.h | 1 + dev/functional/config.h | 9 ++ dev/open_mode.h | 28 ++++ dev/storage.h | 5 + dev/storage_base.h | 23 +++ dev/vfs_object.h | 59 ++++++++ include/sqlite_orm/sqlite_orm.h | 137 +++++++++++------- .../vfs_open_mode_static_tests.cpp | 29 ++++ tests/vfs_open_mode_tests.cpp | 78 ++++++++++ 9 files changed, 320 insertions(+), 49 deletions(-) create mode 100644 dev/open_mode.h create mode 100644 dev/vfs_object.h create mode 100644 tests/static_tests/vfs_open_mode_static_tests.cpp create mode 100644 tests/vfs_open_mode_tests.cpp diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 59d093858..718a91bf9 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -7,6 +7,7 @@ #include // std::string #endif +#include "storage_options.h" #include "error_code.h" namespace sqlite_orm { diff --git a/dev/functional/config.h b/dev/functional/config.h index 2478ee0bc..f19f4deff 100644 --- a/dev/functional/config.h +++ b/dev/functional/config.h @@ -86,6 +86,15 @@ #define SQLITE_ORM_WITH_CTE #endif +#if defined(_WIN32) +#define SQLITE_ORM_WIN +#elif defined(__APPLE__) +#define SQLITE_ORM_APPLE +#define SQLITE_ORM_UNIX +#elif defined(__unix__) || defined(__unix) || defined(__linux__) || defined(__FreeBSD__) +#define SQLITE_ORM_UNIX +#endif + // define the inline namespace "literals" so that it is available even if it was not introduced by a feature namespace sqlite_orm { inline namespace literals {} diff --git a/dev/open_mode.h b/dev/open_mode.h new file mode 100644 index 000000000..6018d430f --- /dev/null +++ b/dev/open_mode.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace sqlite_orm { + + enum class open_mode { + default_mode = 0, + create_readwrite = 0, + readonly = 1, + }; +} + +namespace sqlite_orm { + namespace internal { + constexpr int open_mode_to_int_flags(open_mode open) { + + switch (open) { + case open_mode::readonly: + return SQLITE_OPEN_READONLY; + case open_mode::create_readwrite: + return SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE; + }; + + return -1; + } + } +} diff --git a/dev/storage.h b/dev/storage.h index 2598e8d48..0f17fc698 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1759,6 +1759,11 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { /* * Factory function for a storage instance, from a database file and a bunch of database object definitions. */ + template + internal::storage_t make_storage(std::string filename, storage_options options, DBO... dbObjects) { + return {std::move(filename), options, internal::db_objects_tuple{std::forward(dbObjects)...}}; + } + template internal::storage_t make_storage(std::string filename, DBO... dbObjects) { return {std::move(filename), {std::forward(dbObjects)...}, std::tuple<>{}}; diff --git a/dev/storage_base.h b/dev/storage_base.h index 9ee3c0a1a..05119f40d 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -640,6 +640,29 @@ namespace sqlite_orm { return this->connection->retain_count() > 0; } + /** + * Public method for checking the VFS implementation being used by + * this storage object. Mostly useful for debug. + */ + sqlite_orm::vfs_object vfs_object() const { + return this->connection->options.vfs_option; + } + + /** + * Return the current open_mode for this storage object. + */ + sqlite_orm::open_mode open_mode() const { + return this->connection->options.open_option; + } + + /** + * Return true if this database object is opened in a readonly state. + */ + bool readonly() const { + sqlite3* db = this->connection->get(); + return sqlite3_db_readonly(db, "main"); + } + /* * returning false when there is a transaction in place * otherwise true; function is not const because it has to call get_connection() diff --git a/dev/vfs_object.h b/dev/vfs_object.h new file mode 100644 index 000000000..104558dd8 --- /dev/null +++ b/dev/vfs_object.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include "functional/config.h" +#include "serialize_result_type.h" + +namespace sqlite_orm { + + enum class vfs_object { + + default_vfs = 0, +#ifdef SQLITE_ORM_UNIX + unix = 0, + unix_posix = 0, + unix_dotfile = 1, +#ifdef SQLITE_ORM_APPLE + unix_afp = 2, +#endif +#endif + +#ifdef SQLITE_ORM_WIN + win32 = 0, + win32_longpath = 1, +#endif + num_vfs_objects + + }; + +} + +namespace sqlite_orm { + namespace internal { + + inline const serialize_result_type& vfs_object_to_string(vfs_object v) { + static constexpr size_t num_vfs_objects = static_cast(vfs_object::num_vfs_objects); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + static const std::array idx2str = { +#else + static const std::array idx2str = { +#endif + +#ifdef SQLITE_ORM_UNIX + "unix", + "unix-dotfile", +#ifdef SQLITE_ORM_APPLE + "unix-afp", +#endif +#endif + +#ifdef SQLITE_ORM_WIN + "win32", + "win32-longpath", +#endif + }; + + return idx2str.at(static_cast(v)); + } + } +} diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a97a62719..a308ee6ce 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -292,6 +292,15 @@ using std::nullptr_t; #define SQLITE_ORM_WITH_CTE #endif +#if defined(_WIN32) +#define SQLITE_ORM_WIN +#elif defined(__APPLE__) +#define SQLITE_ORM_APPLE +#define SQLITE_ORM_UNIX +#elif defined(__unix__) || defined(__unix) || defined(__linux__) || defined(__FreeBSD__) +#define SQLITE_ORM_UNIX +#endif + // define the inline namespace "literals" so that it is available even if it was not introduced by a feature namespace sqlite_orm { inline namespace literals {} @@ -14003,6 +14012,57 @@ namespace sqlite_orm { #include // std::string #endif +// #include "storage_options.h" + +#include +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else +#include // std::is_aggregate +#include // std::move +#include // std::function +#endif + +namespace sqlite_orm { + namespace internal { + template + using storage_opt_tag_t = typename T::storage_opt_tag; + + struct on_open_spec { + using storage_opt_tag = int; + + std::function onOpen; + }; + } +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Database connection control options to be passed to `make_storage()`. + */ + struct connection_control { + /// Whether to open the database once and for all. + /// Required if using a 'storage' instance from multiple threads. + bool open_forever = false; + + using storage_opt_tag = int; + }; +#if __cpp_lib_is_aggregate >= 201703L + // design choice: must be an aggregate that can be constructed using designated initializers + static_assert(std::is_aggregate_v); +#endif + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + /** + * Callback function to be passed to `make_storage()`. + * The provided function is called immdediately after the database connection has been established and set up. + */ + inline internal::on_open_spec on_open(std::function onOpen) { + return {std::move(onOpen)}; + } +#endif +} + // #include "error_code.h" namespace sqlite_orm { @@ -17861,55 +17921,6 @@ namespace sqlite_orm { // #include "storage_options.h" -#include -#ifdef SQLITE_ORM_IMPORT_STD_MODULE -#include -#else -#include // std::is_aggregate -#include // std::move -#include // std::function -#endif - -namespace sqlite_orm { - namespace internal { - template - using storage_opt_tag_t = typename T::storage_opt_tag; - - struct on_open_spec { - using storage_opt_tag = int; - - std::function onOpen; - }; - } -} - -SQLITE_ORM_EXPORT namespace sqlite_orm { - /** - * Database connection control options to be passed to `make_storage()`. - */ - struct connection_control { - /// Whether to open the database once and for all. - /// Required if using a 'storage' instance from multiple threads. - bool open_forever = false; - - using storage_opt_tag = int; - }; -#if __cpp_lib_is_aggregate >= 201703L - // design choice: must be an aggregate that can be constructed using designated initializers - static_assert(std::is_aggregate_v); -#endif - -#ifdef SQLITE_ORM_CTAD_SUPPORTED - /** - * Callback function to be passed to `make_storage()`. - * The provided function is called immdediately after the database connection has been established and set up. - */ - inline internal::on_open_spec on_open(std::function onOpen) { - return {std::move(onOpen)}; - } -#endif -} - namespace sqlite_orm { namespace internal { @@ -18514,6 +18525,29 @@ namespace sqlite_orm { return this->connection->retain_count() > 0; } + /** + * Public method for checking the VFS implementation being used by + * this storage object. Mostly useful for debug. + */ + sqlite_orm::vfs_object vfs_object() const { + return this->connection->options.vfs_option; + } + + /** + * Return the current open_mode for this storage object. + */ + sqlite_orm::open_mode open_mode() const { + return this->connection->options.open_option; + } + + /** + * Return true if this database object is opened in a readonly state. + */ + bool readonly() const { + sqlite3* db = this->connection->get(); + return sqlite3_db_readonly(db, "main"); + } + /* * returning false when there is a transaction in place * otherwise true; function is not const because it has to call get_connection() @@ -24323,6 +24357,11 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { /* * Factory function for a storage instance, from a database file and a bunch of database object definitions. */ + template + internal::storage_t make_storage(std::string filename, storage_options options, DBO... dbObjects) { + return {std::move(filename), options, internal::db_objects_tuple{std::forward(dbObjects)...}}; + } + template internal::storage_t make_storage(std::string filename, DBO... dbObjects) { return {std::move(filename), {std::forward(dbObjects)...}, std::tuple<>{}}; diff --git a/tests/static_tests/vfs_open_mode_static_tests.cpp b/tests/static_tests/vfs_open_mode_static_tests.cpp new file mode 100644 index 000000000..fcc7ad55d --- /dev/null +++ b/tests/static_tests/vfs_open_mode_static_tests.cpp @@ -0,0 +1,29 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("vfs string conversion returns expected strings") { + +#ifdef SQLITE_ORM_UNIX + REQUIRE(internal::vfs_object_to_string(vfs_object::unix) == "unix"); + REQUIRE(internal::vfs_object_to_string(vfs_object::unix_posix) == "unix"); + REQUIRE(internal::vfs_object_to_string(vfs_object::unix_dotfile) == "unix-dotfile"); +#ifdef SQLITE_ORM_APPLE + REQUIRE(internal::vfs_object_to_string(vfs_object::unix_afp) == "unix-afp"); +#endif +#endif + +#ifdef SQLITE_ORM_WIN + REQUIRE(internal::vfs_object_to_string(vfs_object::win32) == "win32"); + REQUIRE(internal::vfs_object_to_string(vfs_object::win32_longpath) == "win32-longpath"); +#endif +} + +TEST_CASE("open_mode flag conversion returns expected flags") { + STATIC_REQUIRE(internal::open_mode_to_int_flags(open_mode::default_mode) == + (SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE)); + STATIC_REQUIRE(internal::open_mode_to_int_flags(open_mode::create_readwrite) == + (SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE)); + STATIC_REQUIRE(internal::open_mode_to_int_flags(open_mode::readonly) == (SQLITE_OPEN_READONLY)); +} diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp new file mode 100644 index 000000000..8d507114b --- /dev/null +++ b/tests/vfs_open_mode_tests.cpp @@ -0,0 +1,78 @@ +#include +#include + +struct User { + std::string id; +}; + +using namespace sqlite_orm; + +static const auto default_table = make_table("users", make_column("id", &User::id, primary_key())); + +TEST_CASE("vfs modes open successfully") { + +#if defined(SQLITE_ORM_APPLE) + vfs_object vfs = GENERATE(vfs_object::unix, vfs_object::unix_posix, vfs_object::unix_dotfile, vfs_object::unix_afp); +#elif defined(SQLITE_ORM_UNIX) + vfs_object vfs = GENERATE(vfs_object::unix, vfs_object::unix_posix, vfs_object::unix_dotfile); +#elif defined(SQLITE_ORM_WIN) + vfs_object vfs = GENERATE(vfs_object::win32, vfs_object::win32_longpath); +#endif + + storage_options options; + options.vfs_option = vfs; + + auto storage = make_storage(":memory:", options, default_table); + REQUIRE_NOTHROW(storage.open_forever()); + + internal::serialize_result_type vfs_string = internal::vfs_object_to_string(options.vfs_option); + UNSCOPED_INFO("FAILED VFS: " << vfs_string); + REQUIRE(storage.is_opened()); + REQUIRE(storage.vfs_object() == options.vfs_option); + REQUIRE(storage.open_mode() == options.open_option); +} + +TEST_CASE("create/readwrite open mode behaves as expected") { + + const bool in_memory = GENERATE(true, false); + const char* tmp_filename = in_memory ? ":memory:" : "open_mode.sqlite"; + + storage_options options, readonly_options; + options.open_option = open_mode::create_readwrite; + readonly_options.open_option = open_mode::readonly; + + if (!in_memory) { + std::remove(tmp_filename); + } + + { + auto storage = make_storage(tmp_filename, options, default_table); + + CHECK_NOTHROW(storage.open_forever()); + + CHECK(storage.is_opened()); + CHECK(storage.open_mode() == open_mode::create_readwrite); + CHECK(!storage.readonly()); + + SECTION("readonly open mode behaves as expected") { + auto readonly_storage = make_storage(tmp_filename, readonly_options, default_table); + CHECK_NOTHROW(readonly_storage.open_forever()); + + CHECK(readonly_storage.is_opened()); + CHECK(readonly_storage.open_mode() == open_mode::readonly); + CHECK(readonly_storage.readonly()); + } + } + + if (!in_memory) { + INFO(tmp_filename); + REQUIRE(std::remove(tmp_filename) == 0); + } + + if (!in_memory) { + SECTION("readonly fails with deleted files") { + auto readonly_storage = make_storage(tmp_filename, readonly_options, default_table); + REQUIRE_THROWS_AS(readonly_storage.open_forever(), std::system_error); + } + } +} From c8dd742657048a4c08014ef8938247a1367180f9 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Feb 2025 16:19:36 -0800 Subject: [PATCH 078/170] test green tests green again --- dev/connection_holder.h | 34 ++++-- dev/storage.h | 5 - dev/storage_base.h | 12 +- dev/storage_options.h | 2 + include/sqlite_orm/sqlite_orm.h | 210 ++++++++++++++++++++++++-------- tests/vfs_open_mode_tests.cpp | 36 +++--- 6 files changed, 214 insertions(+), 85 deletions(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 718a91bf9..5be4c1d73 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -7,30 +7,38 @@ #include // std::string #endif -#include "storage_options.h" #include "error_code.h" +#include "vfs_object.h" +#include "open_mode.h" namespace sqlite_orm { namespace internal { struct connection_holder { - connection_holder(std::string filename, std::function didOpenDb) : - _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)) {} + connection_holder(std::string filename, + std::function didOpenDb, + vfs_object vfsObject = vfs_object::default_vfs, + open_mode openMode = open_mode::default_mode) : + _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)), _vfsObject(vfsObject), + _openMode(openMode) {} connection_holder(const connection_holder&) = delete; connection_holder(const connection_holder& other, std::function didOpenDb) : - _didOpenDb{std::move(didOpenDb)}, filename{other.filename} {} + _didOpenDb{std::move(didOpenDb)}, filename{other.filename}, _vfsObject{other._vfsObject}, + _openMode{other._openMode} {} void retain() { // first one opens the connection. // we presume that the connection is opened once in a single-threaded context [also open forever]. // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { - int rc = sqlite3_open_v2(this->filename.c_str(), - &this->db, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, - nullptr); + + const std::string vfs_name(internal::vfs_object_to_string(_vfsObject)); + const int open_flags = internal::open_mode_to_int_flags(_openMode); + + int rc = sqlite3_open_v2(this->filename.c_str(), &this->db, open_flags, vfs_name.c_str()); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { throw_translated_sqlite_error(this->db); } @@ -66,6 +74,14 @@ namespace sqlite_orm { return _retainCount.load(std::memory_order_relaxed); } + vfs_object vfs() { + return _vfsObject; + } + + sqlite_orm::open_mode open_flags() { + return _openMode; + } + protected: sqlite3* db = nullptr; @@ -74,6 +90,8 @@ namespace sqlite_orm { private: const std::function _didOpenDb; + const vfs_object _vfsObject; + const open_mode _openMode; public: const std::string filename; diff --git a/dev/storage.h b/dev/storage.h index 0f17fc698..2598e8d48 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1759,11 +1759,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { /* * Factory function for a storage instance, from a database file and a bunch of database object definitions. */ - template - internal::storage_t make_storage(std::string filename, storage_options options, DBO... dbObjects) { - return {std::move(filename), options, internal::db_objects_tuple{std::forward(dbObjects)...}}; - } - template internal::storage_t make_storage(std::string filename, DBO... dbObjects) { return {std::move(filename), {std::forward(dbObjects)...}, std::tuple<>{}}; diff --git a/dev/storage_base.h b/dev/storage_base.h index 05119f40d..ccda5b2a3 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -644,15 +644,15 @@ namespace sqlite_orm { * Public method for checking the VFS implementation being used by * this storage object. Mostly useful for debug. */ - sqlite_orm::vfs_object vfs_object() const { - return this->connection->options.vfs_option; + vfs_object vfs() const { + return this->connection->vfs(); } /** * Return the current open_mode for this storage object. */ - sqlite_orm::open_mode open_mode() const { - return this->connection->options.open_option; + open_mode open_flags() const { + return this->connection->open_flags(); } /** @@ -696,7 +696,9 @@ namespace sqlite_orm { inMemory(filename.empty() || filename == ":memory:"), connection(std::make_unique( std::move(filename), - std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), + std::bind(&storage_base::on_open_internal, this, std::placeholders::_1), + connectionCtrl.vfs_mode, + connectionCtrl.open_flags)), cachedForeignKeysCount(foreignKeysCount) { if (this->inMemory) { this->connection->retain(); diff --git a/dev/storage_options.h b/dev/storage_options.h index 54f310087..73b271b5a 100644 --- a/dev/storage_options.h +++ b/dev/storage_options.h @@ -30,6 +30,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { /// Whether to open the database once and for all. /// Required if using a 'storage' instance from multiple threads. bool open_forever = false; + vfs_object vfs_mode = vfs_object::default_vfs; + open_mode open_flags = open_mode::default_mode; using storage_opt_tag = int; }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a308ee6ce..09e423915 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -14012,80 +14012,126 @@ namespace sqlite_orm { #include // std::string #endif -// #include "storage_options.h" +// #include "error_code.h" + +// #include "vfs_object.h" #include -#ifdef SQLITE_ORM_IMPORT_STD_MODULE -#include -#else -#include // std::is_aggregate -#include // std::move -#include // std::function +// #include "functional/config.h" + +// #include "serialize_result_type.h" + +namespace sqlite_orm { + + enum class vfs_object { + + default_vfs = 0, +#ifdef SQLITE_ORM_UNIX + unix = 0, + unix_posix = 0, + unix_dotfile = 1, +#ifdef SQLITE_ORM_APPLE + unix_afp = 2, +#endif #endif +#ifdef SQLITE_ORM_WIN + win32 = 0, + win32_longpath = 1, +#endif + num_vfs_objects + + }; + +} + namespace sqlite_orm { namespace internal { - template - using storage_opt_tag_t = typename T::storage_opt_tag; - struct on_open_spec { - using storage_opt_tag = int; + inline const serialize_result_type& vfs_object_to_string(vfs_object v) { + static constexpr size_t num_vfs_objects = static_cast(vfs_object::num_vfs_objects); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + static const std::array idx2str = { +#else + static const std::array idx2str = { +#endif - std::function onOpen; - }; +#ifdef SQLITE_ORM_UNIX + "unix", + "unix-dotfile", +#ifdef SQLITE_ORM_APPLE + "unix-afp", +#endif +#endif + +#ifdef SQLITE_ORM_WIN + "win32", + "win32-longpath", +#endif + }; + + return idx2str.at(static_cast(v)); + } } } -SQLITE_ORM_EXPORT namespace sqlite_orm { - /** - * Database connection control options to be passed to `make_storage()`. - */ - struct connection_control { - /// Whether to open the database once and for all. - /// Required if using a 'storage' instance from multiple threads. - bool open_forever = false; +// #include "open_mode.h" - using storage_opt_tag = int; +#include + +namespace sqlite_orm { + + enum class open_mode { + default_mode = 0, + create_readwrite = 0, + readonly = 1, }; -#if __cpp_lib_is_aggregate >= 201703L - // design choice: must be an aggregate that can be constructed using designated initializers - static_assert(std::is_aggregate_v); -#endif +} -#ifdef SQLITE_ORM_CTAD_SUPPORTED - /** - * Callback function to be passed to `make_storage()`. - * The provided function is called immdediately after the database connection has been established and set up. - */ - inline internal::on_open_spec on_open(std::function onOpen) { - return {std::move(onOpen)}; +namespace sqlite_orm { + namespace internal { + constexpr int open_mode_to_int_flags(open_mode open) { + + switch (open) { + case open_mode::readonly: + return SQLITE_OPEN_READONLY; + case open_mode::create_readwrite: + return SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE; + }; + + return -1; + } } -#endif } -// #include "error_code.h" - namespace sqlite_orm { namespace internal { struct connection_holder { - connection_holder(std::string filename, std::function didOpenDb) : - _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)) {} + connection_holder(std::string filename, + std::function didOpenDb, + vfs_object vfsObject = vfs_object::default_vfs, + open_mode openMode = open_mode::default_mode) : + _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)), _vfsObject(vfsObject), + _openMode(openMode) {} connection_holder(const connection_holder&) = delete; connection_holder(const connection_holder& other, std::function didOpenDb) : - _didOpenDb{std::move(didOpenDb)}, filename{other.filename} {} + _didOpenDb{std::move(didOpenDb)}, filename{other.filename}, _vfsObject{other._vfsObject}, + _openMode{other._openMode} {} void retain() { // first one opens the connection. // we presume that the connection is opened once in a single-threaded context [also open forever]. // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { - int rc = sqlite3_open_v2(this->filename.c_str(), - &this->db, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, - nullptr); + + const std::string vfs_name(internal::vfs_object_to_string(_vfsObject)); + const int open_flags = internal::open_mode_to_int_flags(_openMode); + + int rc = sqlite3_open_v2(this->filename.c_str(), &this->db, open_flags, vfs_name.c_str()); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { throw_translated_sqlite_error(this->db); } @@ -14121,6 +14167,14 @@ namespace sqlite_orm { return _retainCount.load(std::memory_order_relaxed); } + vfs_object vfs() { + return _vfsObject; + } + + sqlite_orm::open_mode open_flags() { + return _openMode; + } + protected: sqlite3* db = nullptr; @@ -14129,6 +14183,8 @@ namespace sqlite_orm { private: const std::function _didOpenDb; + const vfs_object _vfsObject; + const open_mode _openMode; public: const std::string filename; @@ -17921,6 +17977,57 @@ namespace sqlite_orm { // #include "storage_options.h" +#include +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else +#include // std::is_aggregate +#include // std::move +#include // std::function +#endif + +namespace sqlite_orm { + namespace internal { + template + using storage_opt_tag_t = typename T::storage_opt_tag; + + struct on_open_spec { + using storage_opt_tag = int; + + std::function onOpen; + }; + } +} + +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Database connection control options to be passed to `make_storage()`. + */ + struct connection_control { + /// Whether to open the database once and for all. + /// Required if using a 'storage' instance from multiple threads. + bool open_forever = false; + vfs_object vfs_mode = vfs_object::default_vfs; + open_mode open_flags = open_mode::default_mode; + + using storage_opt_tag = int; + }; +#if __cpp_lib_is_aggregate >= 201703L + // design choice: must be an aggregate that can be constructed using designated initializers + static_assert(std::is_aggregate_v); +#endif + +#ifdef SQLITE_ORM_CTAD_SUPPORTED + /** + * Callback function to be passed to `make_storage()`. + * The provided function is called immdediately after the database connection has been established and set up. + */ + inline internal::on_open_spec on_open(std::function onOpen) { + return {std::move(onOpen)}; + } +#endif +} + namespace sqlite_orm { namespace internal { @@ -18529,15 +18636,15 @@ namespace sqlite_orm { * Public method for checking the VFS implementation being used by * this storage object. Mostly useful for debug. */ - sqlite_orm::vfs_object vfs_object() const { - return this->connection->options.vfs_option; + vfs_object vfs() const { + return this->connection->vfs(); } /** * Return the current open_mode for this storage object. */ - sqlite_orm::open_mode open_mode() const { - return this->connection->options.open_option; + open_mode open_flags() const { + return this->connection->open_flags(); } /** @@ -18581,7 +18688,9 @@ namespace sqlite_orm { inMemory(filename.empty() || filename == ":memory:"), connection(std::make_unique( std::move(filename), - std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), + std::bind(&storage_base::on_open_internal, this, std::placeholders::_1), + connectionCtrl.vfs_mode, + connectionCtrl.open_flags)), cachedForeignKeysCount(foreignKeysCount) { if (this->inMemory) { this->connection->retain(); @@ -24357,11 +24466,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { /* * Factory function for a storage instance, from a database file and a bunch of database object definitions. */ - template - internal::storage_t make_storage(std::string filename, storage_options options, DBO... dbObjects) { - return {std::move(filename), options, internal::db_objects_tuple{std::forward(dbObjects)...}}; - } - template internal::storage_t make_storage(std::string filename, DBO... dbObjects) { return {std::move(filename), {std::forward(dbObjects)...}, std::tuple<>{}}; diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp index 8d507114b..2e1b9d48f 100644 --- a/tests/vfs_open_mode_tests.cpp +++ b/tests/vfs_open_mode_tests.cpp @@ -1,6 +1,8 @@ #include #include +#ifdef SQLITE_ORM_CTAD_SUPPORTED + struct User { std::string id; }; @@ -19,17 +21,15 @@ TEST_CASE("vfs modes open successfully") { vfs_object vfs = GENERATE(vfs_object::win32, vfs_object::win32_longpath); #endif - storage_options options; - options.vfs_option = vfs; - - auto storage = make_storage(":memory:", options, default_table); + auto storage = make_storage(":memory:", connection_control{.vfs_mode = vfs}, default_table); + storage.sync_schema(); REQUIRE_NOTHROW(storage.open_forever()); - internal::serialize_result_type vfs_string = internal::vfs_object_to_string(options.vfs_option); + internal::serialize_result_type vfs_string = internal::vfs_object_to_string(vfs); UNSCOPED_INFO("FAILED VFS: " << vfs_string); REQUIRE(storage.is_opened()); - REQUIRE(storage.vfs_object() == options.vfs_option); - REQUIRE(storage.open_mode() == options.open_option); + REQUIRE(storage.vfs() == vfs); + REQUIRE(storage.open_flags() == open_mode::default_mode); } TEST_CASE("create/readwrite open mode behaves as expected") { @@ -37,30 +37,36 @@ TEST_CASE("create/readwrite open mode behaves as expected") { const bool in_memory = GENERATE(true, false); const char* tmp_filename = in_memory ? ":memory:" : "open_mode.sqlite"; - storage_options options, readonly_options; - options.open_option = open_mode::create_readwrite; - readonly_options.open_option = open_mode::readonly; + connection_control options = {.open_flags = open_mode::create_readwrite}; + connection_control readonly_options = {.open_flags = open_mode::readonly}; if (!in_memory) { std::remove(tmp_filename); } { - auto storage = make_storage(tmp_filename, options, default_table); + auto storage = make_storage(tmp_filename, default_table, options); + REQUIRE_NOTHROW(storage.sync_schema()); CHECK_NOTHROW(storage.open_forever()); CHECK(storage.is_opened()); - CHECK(storage.open_mode() == open_mode::create_readwrite); + CHECK(storage.open_flags() == open_mode::create_readwrite); CHECK(!storage.readonly()); + User dummy{"dummy"}; + CHECK_NOTHROW(storage.replace(dummy)); + CHECK(storage.get_pointer(dummy.id) != nullptr); + SECTION("readonly open mode behaves as expected") { auto readonly_storage = make_storage(tmp_filename, readonly_options, default_table); - CHECK_NOTHROW(readonly_storage.open_forever()); + REQUIRE_NOTHROW(readonly_storage.open_forever()); CHECK(readonly_storage.is_opened()); - CHECK(readonly_storage.open_mode() == open_mode::readonly); + CHECK(readonly_storage.open_flags() == open_mode::readonly); CHECK(readonly_storage.readonly()); + CHECK_THROWS_AS(readonly_storage.replace(dummy), std::system_error); + CHECK(storage.get_pointer(dummy.id) != nullptr); } } @@ -76,3 +82,5 @@ TEST_CASE("create/readwrite open mode behaves as expected") { } } } + +#endif From 6393c23573c90a48556c7a15cb315ff3a1363bdc Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Fri, 21 Feb 2025 15:26:11 -0800 Subject: [PATCH 079/170] address MSVC lack of designated initializer support --- tests/vfs_open_mode_tests.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp index 2e1b9d48f..e066b57ae 100644 --- a/tests/vfs_open_mode_tests.cpp +++ b/tests/vfs_open_mode_tests.cpp @@ -21,15 +21,24 @@ TEST_CASE("vfs modes open successfully") { vfs_object vfs = GENERATE(vfs_object::win32, vfs_object::win32_longpath); #endif - auto storage = make_storage(":memory:", connection_control{.vfs_mode = vfs}, default_table); + connection_control options; + options.vfs_mode = vfs; + auto storage = make_storage(":memory:", options, default_table); storage.sync_schema(); REQUIRE_NOTHROW(storage.open_forever()); internal::serialize_result_type vfs_string = internal::vfs_object_to_string(vfs); UNSCOPED_INFO("FAILED VFS: " << vfs_string); - REQUIRE(storage.is_opened()); - REQUIRE(storage.vfs() == vfs); - REQUIRE(storage.open_flags() == open_mode::default_mode); + CHECK(storage.is_opened()); + CHECK(storage.vfs() == vfs); + CHECK(storage.open_flags() == open_mode::default_mode); + + SECTION("Storage copy operator carries over vfs option") { + auto storage_copy = storage; + CHECK(storage_copy.is_opened()); + CHECK(storage_copy.vfs() == vfs); + CHECK(storage_copy.open_flags() == open_mode::default_mode); + } } TEST_CASE("create/readwrite open mode behaves as expected") { From d1d98acbeca26eb8740a46f893876da77859ed1d Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Wed, 26 Feb 2025 10:44:43 -0800 Subject: [PATCH 080/170] fixup --- tests/vfs_open_mode_tests.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp index e066b57ae..49eab9d1e 100644 --- a/tests/vfs_open_mode_tests.cpp +++ b/tests/vfs_open_mode_tests.cpp @@ -46,8 +46,9 @@ TEST_CASE("create/readwrite open mode behaves as expected") { const bool in_memory = GENERATE(true, false); const char* tmp_filename = in_memory ? ":memory:" : "open_mode.sqlite"; - connection_control options = {.open_flags = open_mode::create_readwrite}; - connection_control readonly_options = {.open_flags = open_mode::readonly}; + connection_control options, readonly_options; + options.open_flags = open_mode::create_readwrite; + readonly_options.open_flags = open_mode::readonly; if (!in_memory) { std::remove(tmp_filename); From 34d38a572ff39e033147b0afd4ab6adc5496f7c6 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Mon, 3 Mar 2025 09:55:07 -0800 Subject: [PATCH 081/170] improve tests slightly --- tests/vfs_open_mode_tests.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp index 49eab9d1e..c8deb406e 100644 --- a/tests/vfs_open_mode_tests.cpp +++ b/tests/vfs_open_mode_tests.cpp @@ -59,6 +59,7 @@ TEST_CASE("create/readwrite open mode behaves as expected") { REQUIRE_NOTHROW(storage.sync_schema()); CHECK_NOTHROW(storage.open_forever()); + REQUIRE(!storage.table_names().empty()); CHECK(storage.is_opened()); CHECK(storage.open_flags() == open_mode::create_readwrite); @@ -75,9 +76,16 @@ TEST_CASE("create/readwrite open mode behaves as expected") { CHECK(readonly_storage.is_opened()); CHECK(readonly_storage.open_flags() == open_mode::readonly); CHECK(readonly_storage.readonly()); - CHECK_THROWS_AS(readonly_storage.replace(dummy), std::system_error); - CHECK(storage.get_pointer(dummy.id) != nullptr); + + if (!in_memory) { + CHECK(!readonly_storage.table_names().empty()); + CHECK_THROWS_AS(readonly_storage.remove(dummy.id), std::system_error); + CHECK(readonly_storage.get_pointer(dummy.id) != nullptr); + } } + + CHECK_NOTHROW(storage.remove(dummy.id)); + CHECK(storage.get_pointer(dummy.id) == nullptr); } if (!in_memory) { From 99f02e97fee402d12ba4f76e1a97197de39e835a Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Mar 2025 16:16:24 -0700 Subject: [PATCH 082/170] update test --- tests/vfs_open_mode_tests.cpp | 83 ++++++++++++++++------------------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp index c8deb406e..50a083861 100644 --- a/tests/vfs_open_mode_tests.cpp +++ b/tests/vfs_open_mode_tests.cpp @@ -1,5 +1,10 @@ #include #include +#include // std::remove +#if SQLITE_ORM_HAS_INCLUDE() +#include +#endif +#include "catch_matchers.h" #ifdef SQLITE_ORM_CTAD_SUPPORTED @@ -20,18 +25,15 @@ TEST_CASE("vfs modes open successfully") { #elif defined(SQLITE_ORM_WIN) vfs_object vfs = GENERATE(vfs_object::win32, vfs_object::win32_longpath); #endif + internal::serialize_result_type vfs_string = internal::vfs_object_to_string(vfs); - connection_control options; - options.vfs_mode = vfs; + connection_control options{true, vfs}; auto storage = make_storage(":memory:", options, default_table); - storage.sync_schema(); + UNSCOPED_INFO("FAILED VFS: " << vfs_string); REQUIRE_NOTHROW(storage.open_forever()); - internal::serialize_result_type vfs_string = internal::vfs_object_to_string(vfs); - UNSCOPED_INFO("FAILED VFS: " << vfs_string); - CHECK(storage.is_opened()); - CHECK(storage.vfs() == vfs); - CHECK(storage.open_flags() == open_mode::default_mode); + REQUIRE(storage.vfs() == vfs); + REQUIRE(storage.open_flags() == open_mode::default_mode); SECTION("Storage copy operator carries over vfs option") { auto storage_copy = storage; @@ -41,12 +43,11 @@ TEST_CASE("vfs modes open successfully") { } } -TEST_CASE("create/readwrite open mode behaves as expected") { - +TEST_CASE("readwrite/readonly open modes behaves as expected") { const bool in_memory = GENERATE(true, false); const char* tmp_filename = in_memory ? ":memory:" : "open_mode.sqlite"; - connection_control options, readonly_options; + connection_control options{true}, readonly_options{true}; options.open_flags = open_mode::create_readwrite; readonly_options.open_flags = open_mode::readonly; @@ -54,51 +55,45 @@ TEST_CASE("create/readwrite open mode behaves as expected") { std::remove(tmp_filename); } - { + SECTION("rw+ro") { auto storage = make_storage(tmp_filename, default_table, options); - REQUIRE_NOTHROW(storage.sync_schema()); - - CHECK_NOTHROW(storage.open_forever()); - REQUIRE(!storage.table_names().empty()); - CHECK(storage.is_opened()); - CHECK(storage.open_flags() == open_mode::create_readwrite); - CHECK(!storage.readonly()); + REQUIRE(storage.open_flags() == open_mode::create_readwrite); + REQUIRE_FALSE(storage.readonly()); - User dummy{"dummy"}; - CHECK_NOTHROW(storage.replace(dummy)); - CHECK(storage.get_pointer(dummy.id) != nullptr); + storage.sync_schema(); + const User dummy{"dummy"}; + storage.replace(dummy); SECTION("readonly open mode behaves as expected") { auto readonly_storage = make_storage(tmp_filename, readonly_options, default_table); - REQUIRE_NOTHROW(readonly_storage.open_forever()); - CHECK(readonly_storage.is_opened()); - CHECK(readonly_storage.open_flags() == open_mode::readonly); - CHECK(readonly_storage.readonly()); + REQUIRE(readonly_storage.open_flags() == open_mode::readonly); + REQUIRE(readonly_storage.readonly()); - if (!in_memory) { - CHECK(!readonly_storage.table_names().empty()); - CHECK_THROWS_AS(readonly_storage.remove(dummy.id), std::system_error); - CHECK(readonly_storage.get_pointer(dummy.id) != nullptr); + if (in_memory) { + SKIP("skipped for in-memory"); } + REQUIRE_NOTHROW(readonly_storage.sync_schema()); + CHECK_NOTHROW(readonly_storage.get(dummy.id)); + const ErrorCodeExceptionMatcher readOnlyExceptionMatcher(sqlite_errc{SQLITE_READONLY}); + REQUIRE_THROWS_MATCHES(readonly_storage.remove(dummy.id), + std::system_error, + readOnlyExceptionMatcher); } - - CHECK_NOTHROW(storage.remove(dummy.id)); - CHECK(storage.get_pointer(dummy.id) == nullptr); } - - if (!in_memory) { - INFO(tmp_filename); - REQUIRE(std::remove(tmp_filename) == 0); - } - - if (!in_memory) { - SECTION("readonly fails with deleted files") { - auto readonly_storage = make_storage(tmp_filename, readonly_options, default_table); - REQUIRE_THROWS_AS(readonly_storage.open_forever(), std::system_error); + SECTION("readonly fails with non-existing files") { + if (in_memory) { + SKIP("skipped for in-memory"); } + +#if __cpp_lib_filesystem >= 201703L + CHECK_FALSE(std::filesystem::exists(tmp_filename)); +#endif + const ErrorCodeExceptionMatcher cantOpenExceptionMatcher(sqlite_errc{SQLITE_CANTOPEN}); + REQUIRE_THROWS_MATCHES(make_storage(tmp_filename, readonly_options, default_table), + std::system_error, + cantOpenExceptionMatcher); } } - #endif From c0ef19fb6d9015a3ce20c49ecc219c6e6024b0e3 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Mar 2025 16:27:04 -0700 Subject: [PATCH 083/170] add string_constant_type define --- dev/serialize_result_type.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/serialize_result_type.h b/dev/serialize_result_type.h index 41a361c15..937992cb3 100644 --- a/dev/serialize_result_type.h +++ b/dev/serialize_result_type.h @@ -12,9 +12,11 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED using serialize_result_type = std::string_view; using serialize_arg_type = std::string_view; + using string_constant_type = std::string_view; #else using serialize_result_type = std::string; using serialize_arg_type = const std::string&; + using string_constant_type = const char*; #endif } } From c9be6fa58ae44164af03578963b844f9dc714986 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Mar 2025 16:33:47 -0700 Subject: [PATCH 084/170] convert vfs modes to inline constexpr strings --- dev/vfs_object.h | 52 ++++++++---------------------------------------- 1 file changed, 8 insertions(+), 44 deletions(-) diff --git a/dev/vfs_object.h b/dev/vfs_object.h index 104558dd8..f8aa4300f 100644 --- a/dev/vfs_object.h +++ b/dev/vfs_object.h @@ -1,59 +1,23 @@ #pragma once -#include #include "functional/config.h" #include "serialize_result_type.h" -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { - enum class vfs_object { - - default_vfs = 0, #ifdef SQLITE_ORM_UNIX - unix = 0, - unix_posix = 0, - unix_dotfile = 1, + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_vfs_name = "unix"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_dotfile_vfs_name = "unix-dotfile"; #ifdef SQLITE_ORM_APPLE - unix_afp = 2, + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_afp_vfs_name = "unix-afp"; #endif + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = "unix"; #endif #ifdef SQLITE_ORM_WIN - win32 = 0, - win32_longpath = 1, -#endif - num_vfs_objects - - }; - -} - -namespace sqlite_orm { - namespace internal { + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type win32_vfs_name = "win32"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type win32_longpath_vfs_name = "win32-longpath"; - inline const serialize_result_type& vfs_object_to_string(vfs_object v) { - static constexpr size_t num_vfs_objects = static_cast(vfs_object::num_vfs_objects); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - static const std::array idx2str = { -#else - static const std::array idx2str = { + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = win32_vfs_name; #endif - -#ifdef SQLITE_ORM_UNIX - "unix", - "unix-dotfile", -#ifdef SQLITE_ORM_APPLE - "unix-afp", -#endif -#endif - -#ifdef SQLITE_ORM_WIN - "win32", - "win32-longpath", -#endif - }; - - return idx2str.at(static_cast(v)); - } - } } From be575353f592e4f4d8e8d7fae5fb082241d93eb1 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Mar 2025 16:41:18 -0700 Subject: [PATCH 085/170] rename to db_open_mode --- dev/connection_holder.h | 2 +- dev/{open_mode.h => db_open_mode.h} | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) rename dev/{open_mode.h => db_open_mode.h} (60%) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 5be4c1d73..33531ee62 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -9,7 +9,7 @@ #include "error_code.h" #include "vfs_object.h" -#include "open_mode.h" +#include "db_open_mode.h" namespace sqlite_orm { namespace internal { diff --git a/dev/open_mode.h b/dev/db_open_mode.h similarity index 60% rename from dev/open_mode.h rename to dev/db_open_mode.h index 6018d430f..e3b70df57 100644 --- a/dev/open_mode.h +++ b/dev/db_open_mode.h @@ -1,11 +1,9 @@ #pragma once -#include - namespace sqlite_orm { - enum class open_mode { - default_mode = 0, + enum class db_open_mode { + default_ = 0, create_readwrite = 0, readonly = 1, }; @@ -13,12 +11,12 @@ namespace sqlite_orm { namespace sqlite_orm { namespace internal { - constexpr int open_mode_to_int_flags(open_mode open) { + constexpr int db_open_mode_to_int_flags(db_open_mode open) { switch (open) { - case open_mode::readonly: + case db_open_mode::readonly: return SQLITE_OPEN_READONLY; - case open_mode::create_readwrite: + case db_open_mode::create_readwrite: return SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE; }; From f586a4f1a6302856465ec97a535f321269071795 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Mar 2025 16:47:57 -0700 Subject: [PATCH 086/170] rename vfs name file --- dev/connection_holder.h | 2 +- dev/{vfs_object.h => vfs_name.h} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename dev/{vfs_object.h => vfs_name.h} (100%) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 33531ee62..c8aa411f7 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -8,7 +8,7 @@ #endif #include "error_code.h" -#include "vfs_object.h" +#include "vfs_name.h" #include "db_open_mode.h" namespace sqlite_orm { diff --git a/dev/vfs_object.h b/dev/vfs_name.h similarity index 100% rename from dev/vfs_object.h rename to dev/vfs_name.h From d3dfbe126904b925ccc1e60044ff1dabf3f347d3 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Mar 2025 16:54:58 -0700 Subject: [PATCH 087/170] add module exports --- dev/db_open_mode.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dev/db_open_mode.h b/dev/db_open_mode.h index e3b70df57..bc85e4255 100644 --- a/dev/db_open_mode.h +++ b/dev/db_open_mode.h @@ -1,6 +1,8 @@ #pragma once -namespace sqlite_orm { +#include "functional/config.h" + +SQLITE_ORM_EXPORT namespace sqlite_orm { enum class db_open_mode { default_ = 0, @@ -9,7 +11,7 @@ namespace sqlite_orm { }; } -namespace sqlite_orm { +SQLITE_ORM_EXPORT namespace sqlite_orm { namespace internal { constexpr int db_open_mode_to_int_flags(db_open_mode open) { From 0744c40116d852d01d69b27b774f0d9484431f16 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Mar 2025 16:55:53 -0700 Subject: [PATCH 088/170] remove label in skip clause --- tests/vfs_open_mode_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp index 50a083861..dbfdf4ef4 100644 --- a/tests/vfs_open_mode_tests.cpp +++ b/tests/vfs_open_mode_tests.cpp @@ -84,7 +84,7 @@ TEST_CASE("readwrite/readonly open modes behaves as expected") { } SECTION("readonly fails with non-existing files") { if (in_memory) { - SKIP("skipped for in-memory"); + SKIP(); } #if __cpp_lib_filesystem >= 201703L From 0b3970adb2d868585053547b0b40932b1f32ffcc Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Mar 2025 17:13:43 -0700 Subject: [PATCH 089/170] all variable renames --- dev/connection_holder.h | 28 ++-- dev/storage_base.h | 20 ++- dev/storage_options.h | 4 +- include/sqlite_orm/sqlite_orm.h | 223 +++++++++++++------------------- 4 files changed, 109 insertions(+), 166 deletions(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index c8aa411f7..e5d5f1eee 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -1,6 +1,5 @@ #pragma once -#include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::function @@ -10,6 +9,7 @@ #include "error_code.h" #include "vfs_name.h" #include "db_open_mode.h" +#include "storage_options.h" namespace sqlite_orm { namespace internal { @@ -17,16 +17,15 @@ namespace sqlite_orm { struct connection_holder { connection_holder(std::string filename, std::function didOpenDb, - vfs_object vfsObject = vfs_object::default_vfs, - open_mode openMode = open_mode::default_mode) : - _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)), _vfsObject(vfsObject), - _openMode(openMode) {} + connection_control options = {}) : + _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)), vfs_name(options.vfs_name), + open_mode(options.open_mode) {} connection_holder(const connection_holder&) = delete; connection_holder(const connection_holder& other, std::function didOpenDb) : - _didOpenDb{std::move(didOpenDb)}, filename{other.filename}, _vfsObject{other._vfsObject}, - _openMode{other._openMode} {} + _didOpenDb{std::move(didOpenDb)}, filename{other.filename}, vfs_name(other.vfs_name), + open_mode{other.open_mode} {} void retain() { // first one opens the connection. @@ -34,8 +33,7 @@ namespace sqlite_orm { // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { - const std::string vfs_name(internal::vfs_object_to_string(_vfsObject)); - const int open_flags = internal::open_mode_to_int_flags(_openMode); + const int open_flags = internal::db_open_mode_to_int_flags(open_mode); int rc = sqlite3_open_v2(this->filename.c_str(), &this->db, open_flags, vfs_name.c_str()); @@ -74,14 +72,6 @@ namespace sqlite_orm { return _retainCount.load(std::memory_order_relaxed); } - vfs_object vfs() { - return _vfsObject; - } - - sqlite_orm::open_mode open_flags() { - return _openMode; - } - protected: sqlite3* db = nullptr; @@ -90,10 +80,10 @@ namespace sqlite_orm { private: const std::function _didOpenDb; - const vfs_object _vfsObject; - const open_mode _openMode; public: + const std::string vfs_name; + const db_open_mode open_mode; const std::string filename; }; diff --git a/dev/storage_base.h b/dev/storage_base.h index ccda5b2a3..142d1e56d 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -641,26 +641,25 @@ namespace sqlite_orm { } /** - * Public method for checking the VFS implementation being used by - * this storage object. Mostly useful for debug. + * Return the name of the VFS object used by the database connection. */ - vfs_object vfs() const { - return this->connection->vfs(); + const std::string& vfs_name() const { + return this->connection->vfs_name; } /** * Return the current open_mode for this storage object. */ - open_mode open_flags() const { - return this->connection->open_flags(); + db_open_mode open_mode() const { + return this->connection->open_mode; } /** * Return true if this database object is opened in a readonly state. */ - bool readonly() const { - sqlite3* db = this->connection->get(); - return sqlite3_db_readonly(db, "main"); + bool readonly() { + sqlite3* db = this->get_connection().get(); + return static_cast(sqlite3_db_readonly(db, "main")); } /* @@ -697,8 +696,7 @@ namespace sqlite_orm { connection(std::make_unique( std::move(filename), std::bind(&storage_base::on_open_internal, this, std::placeholders::_1), - connectionCtrl.vfs_mode, - connectionCtrl.open_flags)), + connectionCtrl)), cachedForeignKeysCount(foreignKeysCount) { if (this->inMemory) { this->connection->retain(); diff --git a/dev/storage_options.h b/dev/storage_options.h index 73b271b5a..d9b258bfb 100644 --- a/dev/storage_options.h +++ b/dev/storage_options.h @@ -30,8 +30,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { /// Whether to open the database once and for all. /// Required if using a 'storage' instance from multiple threads. bool open_forever = false; - vfs_object vfs_mode = vfs_object::default_vfs; - open_mode open_flags = open_mode::default_mode; + std::string vfs_name{default_vfs_name}; + db_open_mode open_mode = db_open_mode::default_; using storage_opt_tag = int; }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 09e423915..1d29ee200 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -4362,9 +4362,11 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED using serialize_result_type = std::string_view; using serialize_arg_type = std::string_view; + using string_constant_type = std::string_view; #else using serialize_result_type = std::string; using serialize_arg_type = const std::string&; + using string_constant_type = const char*; #endif } } @@ -14005,7 +14007,6 @@ namespace sqlite_orm { // #include "connection_holder.h" -#include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::function @@ -14014,94 +14015,111 @@ namespace sqlite_orm { // #include "error_code.h" -// #include "vfs_object.h" +// #include "vfs_name.h" -#include // #include "functional/config.h" // #include "serialize_result_type.h" -namespace sqlite_orm { - - enum class vfs_object { +SQLITE_ORM_EXPORT namespace sqlite_orm { - default_vfs = 0, #ifdef SQLITE_ORM_UNIX - unix = 0, - unix_posix = 0, - unix_dotfile = 1, + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_vfs_name = "unix"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_dotfile_vfs_name = "unix-dotfile"; #ifdef SQLITE_ORM_APPLE - unix_afp = 2, + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_afp_vfs_name = "unix-afp"; #endif + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = "unix"; #endif #ifdef SQLITE_ORM_WIN - win32 = 0, - win32_longpath = 1, + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type win32_vfs_name = "win32"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type win32_longpath_vfs_name = "win32-longpath"; + + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = win32_vfs_name; #endif - num_vfs_objects +} - }; +// #include "db_open_mode.h" -} +// #include "functional/config.h" -namespace sqlite_orm { - namespace internal { +SQLITE_ORM_EXPORT namespace sqlite_orm { - inline const serialize_result_type& vfs_object_to_string(vfs_object v) { - static constexpr size_t num_vfs_objects = static_cast(vfs_object::num_vfs_objects); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - static const std::array idx2str = { -#else - static const std::array idx2str = { -#endif + enum class db_open_mode { + default_ = 0, + create_readwrite = 0, + readonly = 1, + }; +} -#ifdef SQLITE_ORM_UNIX - "unix", - "unix-dotfile", -#ifdef SQLITE_ORM_APPLE - "unix-afp", -#endif -#endif +SQLITE_ORM_EXPORT namespace sqlite_orm { + namespace internal { + constexpr int db_open_mode_to_int_flags(db_open_mode open) { -#ifdef SQLITE_ORM_WIN - "win32", - "win32-longpath", -#endif + switch (open) { + case db_open_mode::readonly: + return SQLITE_OPEN_READONLY; + case db_open_mode::create_readwrite: + return SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE; }; - return idx2str.at(static_cast(v)); + return -1; } } } -// #include "open_mode.h" +// #include "storage_options.h" #include +#ifdef SQLITE_ORM_IMPORT_STD_MODULE +#include +#else +#include // std::is_aggregate +#include // std::move +#include // std::function +#endif namespace sqlite_orm { + namespace internal { + template + using storage_opt_tag_t = typename T::storage_opt_tag; - enum class open_mode { - default_mode = 0, - create_readwrite = 0, - readonly = 1, - }; + struct on_open_spec { + using storage_opt_tag = int; + + std::function onOpen; + }; + } } -namespace sqlite_orm { - namespace internal { - constexpr int open_mode_to_int_flags(open_mode open) { +SQLITE_ORM_EXPORT namespace sqlite_orm { + /** + * Database connection control options to be passed to `make_storage()`. + */ + struct connection_control { + /// Whether to open the database once and for all. + /// Required if using a 'storage' instance from multiple threads. + bool open_forever = false; + std::string vfs_name{default_vfs_name}; + db_open_mode open_mode = db_open_mode::default_; - switch (open) { - case open_mode::readonly: - return SQLITE_OPEN_READONLY; - case open_mode::create_readwrite: - return SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE; - }; + using storage_opt_tag = int; + }; +#if __cpp_lib_is_aggregate >= 201703L + // design choice: must be an aggregate that can be constructed using designated initializers + static_assert(std::is_aggregate_v); +#endif - return -1; - } +#ifdef SQLITE_ORM_CTAD_SUPPORTED + /** + * Callback function to be passed to `make_storage()`. + * The provided function is called immdediately after the database connection has been established and set up. + */ + inline internal::on_open_spec on_open(std::function onOpen) { + return {std::move(onOpen)}; } +#endif } namespace sqlite_orm { @@ -14110,16 +14128,15 @@ namespace sqlite_orm { struct connection_holder { connection_holder(std::string filename, std::function didOpenDb, - vfs_object vfsObject = vfs_object::default_vfs, - open_mode openMode = open_mode::default_mode) : - _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)), _vfsObject(vfsObject), - _openMode(openMode) {} + connection_control options = {}) : + _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)), vfs_name(options.vfs_name), + open_mode(options.open_mode) {} connection_holder(const connection_holder&) = delete; connection_holder(const connection_holder& other, std::function didOpenDb) : - _didOpenDb{std::move(didOpenDb)}, filename{other.filename}, _vfsObject{other._vfsObject}, - _openMode{other._openMode} {} + _didOpenDb{std::move(didOpenDb)}, filename{other.filename}, vfs_name(other.vfs_name), + open_mode{other.open_mode} {} void retain() { // first one opens the connection. @@ -14127,8 +14144,7 @@ namespace sqlite_orm { // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { - const std::string vfs_name(internal::vfs_object_to_string(_vfsObject)); - const int open_flags = internal::open_mode_to_int_flags(_openMode); + const int open_flags = internal::db_open_mode_to_int_flags(open_mode); int rc = sqlite3_open_v2(this->filename.c_str(), &this->db, open_flags, vfs_name.c_str()); @@ -14167,14 +14183,6 @@ namespace sqlite_orm { return _retainCount.load(std::memory_order_relaxed); } - vfs_object vfs() { - return _vfsObject; - } - - sqlite_orm::open_mode open_flags() { - return _openMode; - } - protected: sqlite3* db = nullptr; @@ -14183,10 +14191,10 @@ namespace sqlite_orm { private: const std::function _didOpenDb; - const vfs_object _vfsObject; - const open_mode _openMode; public: + const std::string vfs_name; + const db_open_mode open_mode; const std::string filename; }; @@ -17977,57 +17985,6 @@ namespace sqlite_orm { // #include "storage_options.h" -#include -#ifdef SQLITE_ORM_IMPORT_STD_MODULE -#include -#else -#include // std::is_aggregate -#include // std::move -#include // std::function -#endif - -namespace sqlite_orm { - namespace internal { - template - using storage_opt_tag_t = typename T::storage_opt_tag; - - struct on_open_spec { - using storage_opt_tag = int; - - std::function onOpen; - }; - } -} - -SQLITE_ORM_EXPORT namespace sqlite_orm { - /** - * Database connection control options to be passed to `make_storage()`. - */ - struct connection_control { - /// Whether to open the database once and for all. - /// Required if using a 'storage' instance from multiple threads. - bool open_forever = false; - vfs_object vfs_mode = vfs_object::default_vfs; - open_mode open_flags = open_mode::default_mode; - - using storage_opt_tag = int; - }; -#if __cpp_lib_is_aggregate >= 201703L - // design choice: must be an aggregate that can be constructed using designated initializers - static_assert(std::is_aggregate_v); -#endif - -#ifdef SQLITE_ORM_CTAD_SUPPORTED - /** - * Callback function to be passed to `make_storage()`. - * The provided function is called immdediately after the database connection has been established and set up. - */ - inline internal::on_open_spec on_open(std::function onOpen) { - return {std::move(onOpen)}; - } -#endif -} - namespace sqlite_orm { namespace internal { @@ -18633,26 +18590,25 @@ namespace sqlite_orm { } /** - * Public method for checking the VFS implementation being used by - * this storage object. Mostly useful for debug. + * Return the name of the VFS object used by the database connection. */ - vfs_object vfs() const { - return this->connection->vfs(); + const std::string& vfs_name() const { + return this->connection->vfs_name; } /** * Return the current open_mode for this storage object. */ - open_mode open_flags() const { - return this->connection->open_flags(); + db_open_mode open_mode() const { + return this->connection->open_mode; } /** * Return true if this database object is opened in a readonly state. */ - bool readonly() const { - sqlite3* db = this->connection->get(); - return sqlite3_db_readonly(db, "main"); + bool readonly() { + sqlite3* db = this->get_connection().get(); + return static_cast(sqlite3_db_readonly(db, "main")); } /* @@ -18689,8 +18645,7 @@ namespace sqlite_orm { connection(std::make_unique( std::move(filename), std::bind(&storage_base::on_open_internal, this, std::placeholders::_1), - connectionCtrl.vfs_mode, - connectionCtrl.open_flags)), + connectionCtrl)), cachedForeignKeysCount(foreignKeysCount) { if (this->inMemory) { this->connection->retain(); From 573158d0d18c806496ddc62997a43ce7b56d17d3 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Mar 2025 17:21:39 -0700 Subject: [PATCH 090/170] name fixups; tests green --- dev/vfs_name.h | 1 + include/sqlite_orm/sqlite_orm.h | 1 + .../vfs_open_mode_static_tests.cpp | 25 +++-------------- tests/vfs_open_mode_tests.cpp | 28 +++++++++---------- 4 files changed, 20 insertions(+), 35 deletions(-) diff --git a/dev/vfs_name.h b/dev/vfs_name.h index f8aa4300f..9b18996d2 100644 --- a/dev/vfs_name.h +++ b/dev/vfs_name.h @@ -7,6 +7,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_UNIX SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_vfs_name = "unix"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_posix_vfs_name = unix_vfs_name; SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_dotfile_vfs_name = "unix-dotfile"; #ifdef SQLITE_ORM_APPLE SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_afp_vfs_name = "unix-afp"; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 1d29ee200..489cb4cde 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -14025,6 +14025,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_UNIX SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_vfs_name = "unix"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_posix_vfs_name = unix_vfs_name; SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_dotfile_vfs_name = "unix-dotfile"; #ifdef SQLITE_ORM_APPLE SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_afp_vfs_name = "unix-afp"; diff --git a/tests/static_tests/vfs_open_mode_static_tests.cpp b/tests/static_tests/vfs_open_mode_static_tests.cpp index fcc7ad55d..6c20b4a89 100644 --- a/tests/static_tests/vfs_open_mode_static_tests.cpp +++ b/tests/static_tests/vfs_open_mode_static_tests.cpp @@ -3,27 +3,10 @@ using namespace sqlite_orm; -TEST_CASE("vfs string conversion returns expected strings") { - -#ifdef SQLITE_ORM_UNIX - REQUIRE(internal::vfs_object_to_string(vfs_object::unix) == "unix"); - REQUIRE(internal::vfs_object_to_string(vfs_object::unix_posix) == "unix"); - REQUIRE(internal::vfs_object_to_string(vfs_object::unix_dotfile) == "unix-dotfile"); -#ifdef SQLITE_ORM_APPLE - REQUIRE(internal::vfs_object_to_string(vfs_object::unix_afp) == "unix-afp"); -#endif -#endif - -#ifdef SQLITE_ORM_WIN - REQUIRE(internal::vfs_object_to_string(vfs_object::win32) == "win32"); - REQUIRE(internal::vfs_object_to_string(vfs_object::win32_longpath) == "win32-longpath"); -#endif -} - -TEST_CASE("open_mode flag conversion returns expected flags") { - STATIC_REQUIRE(internal::open_mode_to_int_flags(open_mode::default_mode) == +TEST_CASE("db_open_mode flag conversion returns expected flags") { + STATIC_REQUIRE(internal::db_open_mode_to_int_flags(db_open_mode::default_) == (SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE)); - STATIC_REQUIRE(internal::open_mode_to_int_flags(open_mode::create_readwrite) == + STATIC_REQUIRE(internal::db_open_mode_to_int_flags(db_open_mode::create_readwrite) == (SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE)); - STATIC_REQUIRE(internal::open_mode_to_int_flags(open_mode::readonly) == (SQLITE_OPEN_READONLY)); + STATIC_REQUIRE(internal::db_open_mode_to_int_flags(db_open_mode::readonly) == (SQLITE_OPEN_READONLY)); } diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp index dbfdf4ef4..03512aba5 100644 --- a/tests/vfs_open_mode_tests.cpp +++ b/tests/vfs_open_mode_tests.cpp @@ -19,27 +19,27 @@ static const auto default_table = make_table("users", make_column("id", &User::i TEST_CASE("vfs modes open successfully") { #if defined(SQLITE_ORM_APPLE) - vfs_object vfs = GENERATE(vfs_object::unix, vfs_object::unix_posix, vfs_object::unix_dotfile, vfs_object::unix_afp); + internal::string_constant_type vfs = + GENERATE(unix_vfs_name, unix_posix_vfs_name, unix_dotfile_vfs_name, unix_afp_vfs_name); #elif defined(SQLITE_ORM_UNIX) - vfs_object vfs = GENERATE(vfs_object::unix, vfs_object::unix_posix, vfs_object::unix_dotfile); + internal::string_constant_type vfs = GENERATE(unix_vfs_name, unix_posix_vfs_name, unix_dotfile_vfs_name); #elif defined(SQLITE_ORM_WIN) - vfs_object vfs = GENERATE(vfs_object::win32, vfs_object::win32_longpath); + internal::string_constant_type vfs = GENERATE(win32_vfs_name, win32_longpath_vfs_name); #endif - internal::serialize_result_type vfs_string = internal::vfs_object_to_string(vfs); - connection_control options{true, vfs}; + connection_control options{true, std::string(vfs)}; auto storage = make_storage(":memory:", options, default_table); - UNSCOPED_INFO("FAILED VFS: " << vfs_string); + UNSCOPED_INFO("FAILED VFS: " << vfs); REQUIRE_NOTHROW(storage.open_forever()); - REQUIRE(storage.vfs() == vfs); - REQUIRE(storage.open_flags() == open_mode::default_mode); + REQUIRE(storage.vfs_name() == vfs); + REQUIRE(storage.open_mode() == db_open_mode::default_); SECTION("Storage copy operator carries over vfs option") { auto storage_copy = storage; CHECK(storage_copy.is_opened()); - CHECK(storage_copy.vfs() == vfs); - CHECK(storage_copy.open_flags() == open_mode::default_mode); + CHECK(storage_copy.vfs_name() == vfs); + CHECK(storage_copy.open_mode() == db_open_mode::default_); } } @@ -48,8 +48,8 @@ TEST_CASE("readwrite/readonly open modes behaves as expected") { const char* tmp_filename = in_memory ? ":memory:" : "open_mode.sqlite"; connection_control options{true}, readonly_options{true}; - options.open_flags = open_mode::create_readwrite; - readonly_options.open_flags = open_mode::readonly; + options.open_mode = db_open_mode::create_readwrite; + readonly_options.open_mode = db_open_mode::readonly; if (!in_memory) { std::remove(tmp_filename); @@ -58,7 +58,7 @@ TEST_CASE("readwrite/readonly open modes behaves as expected") { SECTION("rw+ro") { auto storage = make_storage(tmp_filename, default_table, options); CHECK(storage.is_opened()); - REQUIRE(storage.open_flags() == open_mode::create_readwrite); + REQUIRE(storage.open_mode() == db_open_mode::create_readwrite); REQUIRE_FALSE(storage.readonly()); storage.sync_schema(); @@ -68,7 +68,7 @@ TEST_CASE("readwrite/readonly open modes behaves as expected") { SECTION("readonly open mode behaves as expected") { auto readonly_storage = make_storage(tmp_filename, readonly_options, default_table); CHECK(readonly_storage.is_opened()); - REQUIRE(readonly_storage.open_flags() == open_mode::readonly); + REQUIRE(readonly_storage.open_mode() == db_open_mode::readonly); REQUIRE(readonly_storage.readonly()); if (in_memory) { From acabb863ec76aa9c7b398956927cf6bb65aa3205 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Mar 2025 17:22:44 -0700 Subject: [PATCH 091/170] Revert "remove label in skip clause" This reverts commit 0744c40116d852d01d69b27b774f0d9484431f16. --- tests/vfs_open_mode_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp index 03512aba5..e970a91ca 100644 --- a/tests/vfs_open_mode_tests.cpp +++ b/tests/vfs_open_mode_tests.cpp @@ -84,7 +84,7 @@ TEST_CASE("readwrite/readonly open modes behaves as expected") { } SECTION("readonly fails with non-existing files") { if (in_memory) { - SKIP(); + SKIP("skipped for in-memory"); } #if __cpp_lib_filesystem >= 201703L From 1da3a83f99ebfb0e7194adef154d631f70fcdef4 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Thu, 20 Mar 2025 17:31:57 -0700 Subject: [PATCH 092/170] change connection holder options arg to const ref --- dev/connection_holder.h | 2 +- dev/storage_base.h | 4 ++-- include/sqlite_orm/sqlite_orm.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index e5d5f1eee..689af8262 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -17,7 +17,7 @@ namespace sqlite_orm { struct connection_holder { connection_holder(std::string filename, std::function didOpenDb, - connection_control options = {}) : + const connection_control& options = {}) : _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)), vfs_name(options.vfs_name), open_mode(options.open_mode) {} diff --git a/dev/storage_base.h b/dev/storage_base.h index 142d1e56d..bc2a5a020 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -609,7 +609,7 @@ namespace sqlite_orm { } backup_t make_backup_to(const std::string& filename) { - auto holder = std::make_unique(filename, nullptr); + auto holder = std::make_unique(filename, nullptr, connection_control{}); connection_ref conRef{*holder}; return {conRef, "main", this->get_connection(), "main", std::move(holder)}; } @@ -619,7 +619,7 @@ namespace sqlite_orm { } backup_t make_backup_from(const std::string& filename) { - auto holder = std::make_unique(filename, nullptr); + auto holder = std::make_unique(filename, nullptr, connection_control{}); connection_ref conRef{*holder}; return {this->get_connection(), "main", conRef, "main", std::move(holder)}; } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 489cb4cde..59bdbd9a9 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -14129,7 +14129,7 @@ namespace sqlite_orm { struct connection_holder { connection_holder(std::string filename, std::function didOpenDb, - connection_control options = {}) : + const connection_control& options = {}) : _didOpenDb{std::move(didOpenDb)}, filename(std::move(filename)), vfs_name(options.vfs_name), open_mode(options.open_mode) {} @@ -18559,7 +18559,7 @@ namespace sqlite_orm { } backup_t make_backup_to(const std::string& filename) { - auto holder = std::make_unique(filename, nullptr); + auto holder = std::make_unique(filename, nullptr, connection_control{}); connection_ref conRef{*holder}; return {conRef, "main", this->get_connection(), "main", std::move(holder)}; } @@ -18569,7 +18569,7 @@ namespace sqlite_orm { } backup_t make_backup_from(const std::string& filename) { - auto holder = std::make_unique(filename, nullptr); + auto holder = std::make_unique(filename, nullptr, connection_control{}); connection_ref conRef{*holder}; return {this->get_connection(), "main", conRef, "main", std::move(holder)}; } From 3b45408cf0dc5fb51d86b49cb232883e571a70dc Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Fri, 21 Mar 2025 09:57:23 -0700 Subject: [PATCH 093/170] final fixups --- dev/connection_holder.h | 9 ++++++--- dev/db_open_mode.h | 2 -- dev/storage_base.h | 6 +++--- dev/vfs_name.h | 3 +-- include/sqlite_orm/sqlite_orm.h | 21 ++++++++++----------- tests/vfs_open_mode_tests.cpp | 4 ++-- 6 files changed, 22 insertions(+), 23 deletions(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 689af8262..6455f9206 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -1,5 +1,7 @@ #pragma once +#include + #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::function @@ -33,9 +35,10 @@ namespace sqlite_orm { // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { - const int open_flags = internal::db_open_mode_to_int_flags(open_mode); - - int rc = sqlite3_open_v2(this->filename.c_str(), &this->db, open_flags, vfs_name.c_str()); + int rc = sqlite3_open_v2(this->filename.c_str(), + &this->db, + internal::db_open_mode_to_int_flags(this->open_mode), + this->vfs_name.c_str()); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { throw_translated_sqlite_error(this->db); diff --git a/dev/db_open_mode.h b/dev/db_open_mode.h index bc85e4255..cf3cef8e6 100644 --- a/dev/db_open_mode.h +++ b/dev/db_open_mode.h @@ -1,7 +1,5 @@ #pragma once -#include "functional/config.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { enum class db_open_mode { diff --git a/dev/storage_base.h b/dev/storage_base.h index bc2a5a020..956082f46 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -657,9 +657,9 @@ namespace sqlite_orm { /** * Return true if this database object is opened in a readonly state. */ - bool readonly() { - sqlite3* db = this->get_connection().get(); - return static_cast(sqlite3_db_readonly(db, "main")); + bool db_readonly() { + auto con = this->get_connection(); + return static_cast(sqlite3_db_readonly(con.get(), "main")); } /* diff --git a/dev/vfs_name.h b/dev/vfs_name.h index 9b18996d2..3dda86f1b 100644 --- a/dev/vfs_name.h +++ b/dev/vfs_name.h @@ -1,6 +1,5 @@ #pragma once -#include "functional/config.h" #include "serialize_result_type.h" SQLITE_ORM_EXPORT namespace sqlite_orm { @@ -12,7 +11,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_APPLE SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_afp_vfs_name = "unix-afp"; #endif - SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = "unix"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = unix_vfs_name; #endif #ifdef SQLITE_ORM_WIN diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 59bdbd9a9..4ae0436e9 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -14007,6 +14007,8 @@ namespace sqlite_orm { // #include "connection_holder.h" +#include + #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::function @@ -14017,8 +14019,6 @@ namespace sqlite_orm { // #include "vfs_name.h" -// #include "functional/config.h" - // #include "serialize_result_type.h" SQLITE_ORM_EXPORT namespace sqlite_orm { @@ -14030,7 +14030,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_APPLE SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type unix_afp_vfs_name = "unix-afp"; #endif - SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = "unix"; + SQLITE_ORM_INLINE_VAR constexpr internal::string_constant_type default_vfs_name = unix_vfs_name; #endif #ifdef SQLITE_ORM_WIN @@ -14043,8 +14043,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "db_open_mode.h" -// #include "functional/config.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { enum class db_open_mode { @@ -14145,9 +14143,10 @@ namespace sqlite_orm { // therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`. if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { - const int open_flags = internal::db_open_mode_to_int_flags(open_mode); - - int rc = sqlite3_open_v2(this->filename.c_str(), &this->db, open_flags, vfs_name.c_str()); + int rc = sqlite3_open_v2(this->filename.c_str(), + &this->db, + internal::db_open_mode_to_int_flags(this->open_mode), + this->vfs_name.c_str()); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { throw_translated_sqlite_error(this->db); @@ -18607,9 +18606,9 @@ namespace sqlite_orm { /** * Return true if this database object is opened in a readonly state. */ - bool readonly() { - sqlite3* db = this->get_connection().get(); - return static_cast(sqlite3_db_readonly(db, "main")); + bool db_readonly() { + auto con = this->get_connection(); + return static_cast(sqlite3_db_readonly(con.get(), "main")); } /* diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp index e970a91ca..f47246686 100644 --- a/tests/vfs_open_mode_tests.cpp +++ b/tests/vfs_open_mode_tests.cpp @@ -59,7 +59,7 @@ TEST_CASE("readwrite/readonly open modes behaves as expected") { auto storage = make_storage(tmp_filename, default_table, options); CHECK(storage.is_opened()); REQUIRE(storage.open_mode() == db_open_mode::create_readwrite); - REQUIRE_FALSE(storage.readonly()); + REQUIRE_FALSE(storage.db_readonly()); storage.sync_schema(); const User dummy{"dummy"}; @@ -69,7 +69,7 @@ TEST_CASE("readwrite/readonly open modes behaves as expected") { auto readonly_storage = make_storage(tmp_filename, readonly_options, default_table); CHECK(readonly_storage.is_opened()); REQUIRE(readonly_storage.open_mode() == db_open_mode::readonly); - REQUIRE(readonly_storage.readonly()); + REQUIRE(readonly_storage.db_readonly()); if (in_memory) { SKIP("skipped for in-memory"); From 13627c8dc8d9362ffac812aedb7e053fe03c5572 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Fri, 21 Mar 2025 10:19:46 -0700 Subject: [PATCH 094/170] formatting fix --- dev/connection_holder.h | 1 - include/sqlite_orm/sqlite_orm.h | 1 - 2 files changed, 2 deletions(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 6455f9206..2e1861b4c 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -1,7 +1,6 @@ #pragma once #include - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::function diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 4ae0436e9..e28d47049 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -14008,7 +14008,6 @@ namespace sqlite_orm { // #include "connection_holder.h" #include - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::function From aaa11e5446ec84571abafd056ab36a246df41c2f Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 23 Mar 2025 11:37:56 +0200 Subject: [PATCH 095/170] Maximum efficient result row stepping loop Returning instead of looping on a condition saves a couple of instructions. --- dev/util.h | 11 +++++------ include/sqlite_orm/sqlite_orm.h | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/dev/util.h b/dev/util.h index cff59dfc5..9bd419e4d 100644 --- a/dev/util.h +++ b/dev/util.h @@ -107,7 +107,7 @@ namespace sqlite_orm { lambda(stmt); } break; case SQLITE_DONE: - break; + return; default: { throw_translated_sqlite_error(stmt); } @@ -116,19 +116,18 @@ namespace sqlite_orm { template void perform_steps(sqlite3_stmt* stmt, L&& lambda) { - int rc; - do { - switch (rc = sqlite3_step(stmt)) { + for (;;) { + switch (int rc = sqlite3_step(stmt)) { case SQLITE_ROW: { lambda(stmt); } break; case SQLITE_DONE: - break; + return; default: { throw_translated_sqlite_error(stmt); } } - } while (rc != SQLITE_DONE); + } } } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a97a62719..bab7a9f51 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13825,7 +13825,7 @@ namespace sqlite_orm { lambda(stmt); } break; case SQLITE_DONE: - break; + return; default: { throw_translated_sqlite_error(stmt); } @@ -13834,19 +13834,18 @@ namespace sqlite_orm { template void perform_steps(sqlite3_stmt* stmt, L&& lambda) { - int rc; - do { - switch (rc = sqlite3_step(stmt)) { + for (;;) { + switch (int rc = sqlite3_step(stmt)) { case SQLITE_ROW: { lambda(stmt); } break; case SQLITE_DONE: - break; + return; default: { throw_translated_sqlite_error(stmt); } } - } while (rc != SQLITE_DONE); + } } } } From bad4c9f898b0706a5c4d179a76063a0a3fed4867 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 23 Mar 2025 11:41:19 +0200 Subject: [PATCH 096/170] Removed superfluous value cast to char --- dev/util.h | 2 +- include/sqlite_orm/sqlite_orm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/util.h b/dev/util.h index 9bd419e4d..eec8e2844 100644 --- a/dev/util.h +++ b/dev/util.h @@ -38,7 +38,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { */ inline std::string quote_blob_literal(std::string v) { constexpr char quoteChar = '\''; - return std::string{char('x'), quoteChar} + std::move(v) + quoteChar; + return std::string{'x', quoteChar} + std::move(v) + quoteChar; } /** diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index bab7a9f51..9ec3236b5 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13756,7 +13756,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { */ inline std::string quote_blob_literal(std::string v) { constexpr char quoteChar = '\''; - return std::string{char('x'), quoteChar} + std::move(v) + quoteChar; + return std::string{'x', quoteChar} + std::move(v) + quoteChar; } /** From 38d1e386f7b4cf96fb49fa13ebc8fbe2d668f4c8 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 23 Mar 2025 11:42:32 +0200 Subject: [PATCH 097/170] Used C API for strings from `std` namespace in unit tests --- tests/prepared_statement_tests/insert.cpp | 7 ++++--- tests/prepared_statement_tests/replace.cpp | 7 ++++--- tests/storage_non_crud_tests.cpp | 7 ++++--- tests/tests.cpp | 5 +++-- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/prepared_statement_tests/insert.cpp b/tests/prepared_statement_tests/insert.cpp index 6dbbc1f8c..c1b3808d6 100644 --- a/tests/prepared_statement_tests/insert.cpp +++ b/tests/prepared_statement_tests/insert.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::strcmp #include "prepared_common.h" @@ -76,7 +77,7 @@ TEST_CASE("Prepared insert") { insert(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie")))); storage.execute(statement); REQUIRE(get<0>(statement) == 1); - REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + REQUIRE(std::strcmp(get<1>(statement), "Ellie") == 0); } SECTION("no statement") { storage.insert(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie"))); @@ -93,9 +94,9 @@ TEST_CASE("Prepared insert") { values(std::make_tuple(1, "Ellie"), std::make_tuple(5, "Calvin")))); storage.execute(statement); REQUIRE(get<0>(statement) == 1); - REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + REQUIRE(std::strcmp(get<1>(statement), "Ellie") == 0); REQUIRE(get<2>(statement) == 5); - REQUIRE(::strcmp(get<3>(statement), "Calvin") == 0); + REQUIRE(std::strcmp(get<3>(statement), "Calvin") == 0); } SECTION("no statement") { storage.insert(into(), diff --git a/tests/prepared_statement_tests/replace.cpp b/tests/prepared_statement_tests/replace.cpp index e91bd5542..de9658fda 100644 --- a/tests/prepared_statement_tests/replace.cpp +++ b/tests/prepared_statement_tests/replace.cpp @@ -1,5 +1,6 @@ #include #include +#include // std::strcmp #include "prepared_common.h" @@ -41,7 +42,7 @@ TEST_CASE("Prepared replace") { replace(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie")))); storage.execute(statement); REQUIRE(get<0>(statement) == 1); - REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + REQUIRE(std::strcmp(get<1>(statement), "Ellie") == 0); } SECTION("no statement") { storage.replace(into(), columns(&User::id, &User::name), values(std::make_tuple(1, "Ellie"))); @@ -56,9 +57,9 @@ TEST_CASE("Prepared replace") { values(std::make_tuple(1, "Ellie"), std::make_tuple(5, "Calvin")))); storage.execute(statement); REQUIRE(get<0>(statement) == 1); - REQUIRE(::strcmp(get<1>(statement), "Ellie") == 0); + REQUIRE(std::strcmp(get<1>(statement), "Ellie") == 0); REQUIRE(get<2>(statement) == 5); - REQUIRE(::strcmp(get<3>(statement), "Calvin") == 0); + REQUIRE(std::strcmp(get<3>(statement), "Calvin") == 0); } SECTION("no statement") { storage.replace(into(), diff --git a/tests/storage_non_crud_tests.cpp b/tests/storage_non_crud_tests.cpp index 54f5e5dd2..9d1cd3e39 100644 --- a/tests/storage_non_crud_tests.cpp +++ b/tests/storage_non_crud_tests.cpp @@ -1,6 +1,7 @@ #include #include #include +#include // std::strcmp using namespace sqlite_orm; @@ -268,9 +269,9 @@ TEST_CASE("Select") { throw std::runtime_error(sqlite3_errmsg(db)); } REQUIRE(sqlite3_column_int(stmt, 0) == firstId); - REQUIRE(::strcmp((const char*)sqlite3_column_text(stmt, 1), "best") == 0); - REQUIRE(::strcmp((const char*)sqlite3_column_text(stmt, 2), "behaviour") == 0); - REQUIRE(::strcmp((const char*)sqlite3_column_text(stmt, 3), "hey") == 0); + REQUIRE(std::strcmp((const char*)sqlite3_column_text(stmt, 1), "best") == 0); + REQUIRE(std::strcmp((const char*)sqlite3_column_text(stmt, 2), "behaviour") == 0); + REQUIRE(std::strcmp((const char*)sqlite3_column_text(stmt, 3), "hey") == 0); REQUIRE(sqlite3_column_int(stmt, 4) == 5); sqlite3_finalize(stmt); } diff --git a/tests/tests.cpp b/tests/tests.cpp index 4b354657f..9de9402b3 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -4,6 +4,7 @@ #include // std::vector #include // std::string #include // std::remove +#include // std::strncmp using namespace sqlite_orm; @@ -111,7 +112,7 @@ TEST_CASE("Custom collate") { struct OtotoCollation { int operator()(int leftLength, const void* lhs, int rightLength, const void* rhs) const { if (leftLength == rightLength) { - return ::strncmp((const char*)lhs, (const char*)rhs, leftLength); + return std::strncmp((const char*)lhs, (const char*)rhs, leftLength); } else { return 1; } @@ -153,7 +154,7 @@ TEST_CASE("Custom collate") { if (useLegacyScript) { storage.create_collation("ototo", [](int leftLength, const void* lhs, int rightLength, const void* rhs) { if (leftLength == rightLength) { - return ::strncmp((const char*)lhs, (const char*)rhs, leftLength); + return std::strncmp((const char*)lhs, (const char*)rhs, leftLength); } else { return 1; } From 932f350b258a1cfe32ee2e09b02c39d134e8433c Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 23 Mar 2025 11:55:00 +0200 Subject: [PATCH 098/170] Enabled SQLite extended error codes --- dev/connection_holder.h | 7 +++++- dev/error_code.h | 4 ++-- dev/storage.h | 4 ++-- include/sqlite_orm/sqlite_orm.h | 15 ++++++++---- tests/constraints/unique.cpp | 14 +++++++---- tests/prepared_statement_tests/get.cpp | 7 +++--- .../insert_explicit.cpp | 9 ++++++-- .../replace_range.cpp | 18 ++++++++------- tests/schema/index_tests.cpp | 9 ++++++-- tests/storage_non_crud_tests.cpp | 17 ++++++++++---- tests/tests.cpp | 23 +++++++++++++------ tests/transaction_tests.cpp | 8 +++---- tests/user_defined_functions.cpp | 5 ++-- 13 files changed, 93 insertions(+), 47 deletions(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 59d093858..d9a5b5b61 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -28,7 +28,12 @@ namespace sqlite_orm { if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { int rc = sqlite3_open_v2(this->filename.c_str(), &this->db, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE +#if SQLITE_VERSION_NUMBER >= 3008008 + | SQLITE_OPEN_EXRESCODE, +#else + | 0, +#endif nullptr); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { throw_translated_sqlite_error(this->db); diff --git a/dev/error_code.h b/dev/error_code.h index 4830b85bd..a473c780f 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -112,8 +112,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { return "SQLite error"; } - std::string message(int c) const override final { - return sqlite3_errstr(c); + std::string message(int ev) const override final { + return sqlite3_errstr(ev); } }; diff --git a/dev/storage.h b/dev/storage.h index 2598e8d48..0b44dc83e 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1604,8 +1604,8 @@ namespace sqlite_orm { return std::move(res).value(); #else auto& table = this->get_table(); - auto stepRes = sqlite3_step(stmt); - switch (stepRes) { + int rc = sqlite3_step(stmt); + switch (rc) { case SQLITE_ROW: { T res; object_from_column_builder builder{res, stmt}; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 9ec3236b5..7fc7dec3d 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -3070,8 +3070,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { return "SQLite error"; } - std::string message(int c) const override final { - return sqlite3_errstr(c); + std::string message(int ev) const override final { + return sqlite3_errstr(ev); } }; @@ -14023,7 +14023,12 @@ namespace sqlite_orm { if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) { int rc = sqlite3_open_v2(this->filename.c_str(), &this->db, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE +#if SQLITE_VERSION_NUMBER >= 3008008 + | SQLITE_OPEN_EXRESCODE, +#else + | 0, +#endif nullptr); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { throw_translated_sqlite_error(this->db); @@ -24167,8 +24172,8 @@ namespace sqlite_orm { return std::move(res).value(); #else auto& table = this->get_table(); - auto stepRes = sqlite3_step(stmt); - switch (stepRes) { + int rc = sqlite3_step(stmt); + switch (rc) { case SQLITE_ROW: { T res; object_from_column_builder builder{res, stmt}; diff --git a/tests/constraints/unique.cpp b/tests/constraints/unique.cpp index bea9d37c2..4d9859bd3 100644 --- a/tests/constraints/unique.cpp +++ b/tests/constraints/unique.cpp @@ -1,10 +1,15 @@ #include #include +#include "../catch_matchers.h" using namespace sqlite_orm; TEST_CASE("Unique") { - using Catch::Matchers::ContainsSubstring; +#if SQLITE_VERSION_NUMBER >= 3008008 + const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_UNIQUE)); +#else + const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); +#endif struct Contact { int id = 0; @@ -39,12 +44,13 @@ TEST_CASE("Unique") { storage.insert(Contact{0, "John", "Doe", "john.doe@gmail.com"}); - REQUIRE_THROWS_WITH(storage.insert(Contact{0, "Johnny", "Doe", "john.doe@gmail.com"}), - ContainsSubstring("constraint failed")); + REQUIRE_THROWS_MATCHES(storage.insert(Contact{0, "Johnny", "Doe", "john.doe@gmail.com"}), + std::system_error, + uniqueExceptionMatcher); storage.insert(Shape{0, "red", "green"}); storage.insert(Shape{0, "red", "blue"}); - REQUIRE_THROWS_WITH(storage.insert(Shape{0, "red", "green"}), ContainsSubstring("constraint failed")); + REQUIRE_THROWS_MATCHES(storage.insert(Shape{0, "red", "green"}), std::system_error, uniqueExceptionMatcher); std::vector lists(2); REQUIRE_NOTHROW(storage.insert_range(lists.begin(), lists.end())); diff --git a/tests/prepared_statement_tests/get.cpp b/tests/prepared_statement_tests/get.cpp index bad7422fd..fb863764c 100644 --- a/tests/prepared_statement_tests/get.cpp +++ b/tests/prepared_statement_tests/get.cpp @@ -1,5 +1,6 @@ #include #include +#include "../catch_matchers.h" #include "prepared_common.h" @@ -8,8 +9,8 @@ using namespace sqlite_orm; TEST_CASE("Prepared get") { using namespace PreparedStatementTests; - using Catch::Matchers::ContainsSubstring; using Catch::Matchers::UnorderedEquals; + const ErrorCodeExceptionMatcher notFoundExceptionMatcher(orm_error_code::not_found); const int defaultVisitTime = 50; @@ -73,7 +74,7 @@ TEST_CASE("Prepared get") { } { get<0>(statement) = 4; - REQUIRE_THROWS_WITH(storage.execute(statement), ContainsSubstring("Not found")); + REQUIRE_THROWS_MATCHES(storage.execute(statement), std::system_error, notFoundExceptionMatcher); } } } @@ -95,7 +96,7 @@ TEST_CASE("Prepared get") { //.. } SECTION("execute") { - REQUIRE_THROWS_WITH(storage.execute(statement), ContainsSubstring("Not found")); + REQUIRE_THROWS_MATCHES(storage.execute(statement), std::system_error, notFoundExceptionMatcher); } } { diff --git a/tests/prepared_statement_tests/insert_explicit.cpp b/tests/prepared_statement_tests/insert_explicit.cpp index b4056e642..7b32cbbeb 100644 --- a/tests/prepared_statement_tests/insert_explicit.cpp +++ b/tests/prepared_statement_tests/insert_explicit.cpp @@ -1,5 +1,6 @@ #include #include +#include "../catch_matchers.h" #include "prepared_common.h" @@ -8,8 +9,12 @@ using namespace sqlite_orm; TEST_CASE("Prepared insert explicit") { using namespace PreparedStatementTests; - using Catch::Matchers::ContainsSubstring; using Catch::Matchers::UnorderedEquals; +#if SQLITE_VERSION_NUMBER >= 3008008 + const ErrorCodeExceptionMatcher primaryKeyExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_PRIMARYKEY)); +#else + const ErrorCodeExceptionMatcher primaryKeyExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); +#endif const int defaultVisitTime = 50; @@ -68,7 +73,7 @@ TEST_CASE("Prepared insert explicit") { { user.id = 6; user.name = "Nate Dogg"; - REQUIRE_THROWS_WITH(storage.execute(statement), ContainsSubstring("constraint failed")); + REQUIRE_THROWS_MATCHES(storage.execute(statement), std::system_error, primaryKeyExceptionMatcher); get<0>(statement) = user; auto insertedId = storage.execute(statement); diff --git a/tests/prepared_statement_tests/replace_range.cpp b/tests/prepared_statement_tests/replace_range.cpp index 022a2bf44..95f1ed66d 100644 --- a/tests/prepared_statement_tests/replace_range.cpp +++ b/tests/prepared_statement_tests/replace_range.cpp @@ -41,9 +41,6 @@ TEST_CASE("Prepared replace range") { std::vector users; std::vector> userPointers; std::vector expected; - auto lambda = [](const std::unique_ptr& pointer) -> const User& { - return *pointer; - }; SECTION("empty") { using Catch::Matchers::ContainsSubstring; @@ -55,8 +52,10 @@ TEST_CASE("Prepared replace range") { ContainsSubstring("incomplete input")); } SECTION("pointers") { - REQUIRE_THROWS_WITH(storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)), - ContainsSubstring("incomplete input")); + REQUIRE_THROWS_WITH( + storage.prepare( + replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)), + ContainsSubstring("incomplete input")); } } SECTION("one existing") { @@ -73,7 +72,8 @@ TEST_CASE("Prepared replace range") { } SECTION("pointers") { userPointers.push_back(std::make_unique(user)); - auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)); + auto statement = storage.prepare( + replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); REQUIRE(get<0>(statement) == userPointers.begin()); REQUIRE(get<1>(statement) == userPointers.end()); storage.execute(statement); @@ -97,7 +97,8 @@ TEST_CASE("Prepared replace range") { SECTION("pointers") { userPointers.push_back(std::make_unique(user)); userPointers.push_back(std::make_unique(user2)); - auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)); + auto statement = storage.prepare( + replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); REQUIRE(get<0>(statement) == userPointers.begin()); REQUIRE(get<1>(statement) == userPointers.end()); storage.execute(statement); @@ -121,7 +122,8 @@ TEST_CASE("Prepared replace range") { userPointers.push_back(std::make_unique(user)); userPointers.push_back(std::make_unique(user2)); userPointers.push_back(std::make_unique(user3)); - auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), lambda)); + auto statement = storage.prepare( + replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)); REQUIRE(get<0>(statement) == userPointers.begin()); REQUIRE(get<1>(statement) == userPointers.end()); storage.execute(statement); diff --git a/tests/schema/index_tests.cpp b/tests/schema/index_tests.cpp index 01328b3f1..b672596d5 100644 --- a/tests/schema/index_tests.cpp +++ b/tests/schema/index_tests.cpp @@ -1,5 +1,6 @@ #include #include +#include "../catch_matchers.h" using namespace sqlite_orm; @@ -66,7 +67,11 @@ TEST_CASE("index") { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED TEST_CASE("filtered index") { - using Catch::Matchers::ContainsSubstring; +#if SQLITE_VERSION_NUMBER >= 3008008 + const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_UNIQUE)); +#else + const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); +#endif struct Test { std::optional field1 = 0; @@ -80,7 +85,7 @@ TEST_CASE("filtered index") { REQUIRE_NOTHROW(storage.sync_schema()); storage.insert(Test{1, std::nullopt}); - REQUIRE_THROWS_WITH(storage.insert(Test{1, std::nullopt}), ContainsSubstring("constraint failed")); + REQUIRE_THROWS_MATCHES(storage.insert(Test{1, std::nullopt}), std::system_error, uniqueExceptionMatcher); } SECTION("2") { auto storage = make_storage( diff --git a/tests/storage_non_crud_tests.cpp b/tests/storage_non_crud_tests.cpp index 9d1cd3e39..f0fc5f0f9 100644 --- a/tests/storage_non_crud_tests.cpp +++ b/tests/storage_non_crud_tests.cpp @@ -2,6 +2,7 @@ #include #include #include // std::strcmp +#include "catch_matchers.h" using namespace sqlite_orm; @@ -504,7 +505,11 @@ TEST_CASE("Remove all") { } TEST_CASE("Explicit insert") { - using Catch::Matchers::ContainsSubstring; +#if SQLITE_VERSION_NUMBER >= 3008008 + const ErrorCodeExceptionMatcher notNullExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_NOTNULL)); +#else + const ErrorCodeExceptionMatcher notNullExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); +#endif struct User { int id; @@ -593,8 +598,9 @@ TEST_CASE("Explicit insert") { SECTION("one column") { User user4; user4.name = "Egor"; - REQUIRE_THROWS_WITH(storage.insert(user4, columns(&User::name)), - ContainsSubstring("NOT NULL constraint failed")); + REQUIRE_THROWS_MATCHES(storage.insert(user4, columns(&User::name)), + std::system_error, + notNullExceptionMatcher); } } SECTION("visit") { @@ -626,8 +632,9 @@ TEST_CASE("Explicit insert") { Visit visit3; visit3.setId(10); SECTION("getter") { - REQUIRE_THROWS_WITH(storage.insert(visit3, columns(&Visit::id)), - ContainsSubstring("NOT NULL constraint failed")); + REQUIRE_THROWS_MATCHES(storage.insert(visit3, columns(&Visit::id)), + std::system_error, + notNullExceptionMatcher); } } } diff --git a/tests/tests.cpp b/tests/tests.cpp index 9de9402b3..c9bfef84f 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -1,5 +1,6 @@ #include #include +#include "catch_matchers.h" #include // std::vector #include // std::string @@ -102,7 +103,11 @@ TEST_CASE("Limits") { } TEST_CASE("Custom collate") { - using Catch::Matchers::ContainsSubstring; +#if SQLITE_VERSION_NUMBER >= 3008008 + const ErrorCodeExceptionMatcher collSequExceptionMatcher(sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ)); +#else + const ErrorCodeExceptionMatcher collSequExceptionMatcher(sqlite_errc(SQLITE_ERROR)); +#endif struct Item { int id; @@ -187,12 +192,16 @@ TEST_CASE("Custom collate") { } else { storage.delete_collation(); } - REQUIRE_THROWS_WITH(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo"))), - ContainsSubstring("no such collation sequence")); - REQUIRE_THROWS_WITH(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate())), - ContainsSubstring("no such collation sequence")); - REQUIRE_THROWS_WITH(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo2"))), - ContainsSubstring("no such collation sequence")); + REQUIRE_THROWS_MATCHES(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo"))), + std::system_error, + collSequExceptionMatcher); + REQUIRE_THROWS_MATCHES( + storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate())), + std::system_error, + collSequExceptionMatcher); + REQUIRE_THROWS_MATCHES(storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("ototo2"))), + std::system_error, + collSequExceptionMatcher); rows = storage.select(&Item::name, where(is_equal(&Item::name, "Mercury").collate("alwaysequal")), diff --git a/tests/transaction_tests.cpp b/tests/transaction_tests.cpp index 129c08146..5654a5c8e 100644 --- a/tests/transaction_tests.cpp +++ b/tests/transaction_tests.cpp @@ -64,6 +64,9 @@ TEST_CASE("begin_transaction") { } TEST_CASE("Transaction guard") { + const ErrorCodeExceptionMatcher notFoundExceptionMatcher(orm_error_code::not_found); + const ErrorCodeExceptionMatcher busyExceptionMatcher(sqlite_errc(SQLITE_BUSY)); + std::remove("guard.sqlite"); auto table = make_table("objects", make_column("id", &Object::id, primary_key()), make_column("name", &Object::name)); @@ -74,7 +77,6 @@ TEST_CASE("Transaction guard") { storage.insert(Object{0, "Jack"}); - const ErrorCodeExceptionMatcher notFoundExceptionMatcher(orm_error_code::not_found); SECTION("insert, call make a storage to call an exception and check that rollback was fired") { auto countBefore = storage.count(); SECTION("transaction_guard") { @@ -329,8 +331,6 @@ TEST_CASE("Transaction guard") { REQUIRE(storage.count() == countBefore); } SECTION("exception propagated from dtor") { - using Catch::Matchers::ContainsSubstring; - // create a second database connection auto storage2 = make_storage("guard.sqlite", table); auto guard2 = storage2.transaction_guard(); @@ -340,7 +340,7 @@ TEST_CASE("Transaction guard") { auto guard = new (&buffer) internal::transaction_guard_t{storage.transaction_guard()}; storage.insert({}); guard->commit_on_destroy = true; - REQUIRE_THROWS_WITH(guard->~transaction_guard_t(), ContainsSubstring("database is locked")); + REQUIRE_THROWS_MATCHES(guard->~transaction_guard_t(), std::system_error, busyExceptionMatcher); } std::remove("guard.sqlite"); } diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index 20e34653d..c53c4d268 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -280,6 +280,7 @@ struct NonDefaultCtorAggregateFunction { TEST_CASE("custom functions") { using Catch::Matchers::ContainsSubstring; + const ErrorCodeExceptionMatcher noMemExceptionMatcher(sqlite_errc(SQLITE_NOMEM)); SqrtFunction::callsCount = 0; StatelessHasPrefixFunction::callsCount = 0; @@ -309,7 +310,7 @@ TEST_CASE("custom functions") { // test w/o a result set, i.e when the final aggregate call is the first to require the aggregate function REQUIRE_THROWS_MATCHES(storage.select(func(&User::id)), std::system_error, - ErrorCodeExceptionMatcher(sqlite_errc(SQLITE_NOMEM))); + noMemExceptionMatcher); storage.delete_aggregate_function(); // call before creation @@ -425,7 +426,7 @@ TEST_CASE("custom functions") { storage.create_aggregate_function(); REQUIRE_THROWS_MATCHES(storage.select(func(&User::id)), std::system_error, - ErrorCodeExceptionMatcher(sqlite_errc(SQLITE_NOMEM))); + noMemExceptionMatcher); storage.delete_aggregate_function(); storage.create_scalar_function(); From 88ed04a3065f0ce46bbfd2da56fd0e71b97296db Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sun, 23 Mar 2025 19:31:10 +0500 Subject: [PATCH 099/170] added sqlite_executor --- .vscode/settings.json | 15 +- dev/implementations/storage_definitions.h | 16 +- dev/mapped_view.h | 4 +- dev/pragma.h | 46 +- dev/result_set_view.h | 4 +- dev/storage.h | 225 +++++++- dev/storage_base.h | 210 ++++--- dev/util.h | 131 +++-- include/sqlite_orm/sqlite_orm.h | 637 +++++++++++++++------- tests/row_extractor.cpp | 3 +- 10 files changed, 921 insertions(+), 370 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7216b3ba8..be9c9d87a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -74,6 +74,19 @@ "type_traits": "cpp", "utility": "cpp", "__config": "cpp", - "any": "cpp" + "any": "cpp", + "__threading_support": "cpp", + "condition_variable": "cpp", + "thread": "cpp", + "future": "cpp", + "complex": "cpp", + "execution": "cpp", + "fstream": "cpp", + "iostream": "cpp", + "numbers": "cpp", + "queue": "cpp", + "semaphore": "cpp", + "stack": "cpp", + "unordered_set": "cpp" } } \ No newline at end of file diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index 78e4302e6..d413610e0 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -142,12 +142,16 @@ namespace sqlite_orm { } }); - std::stringstream ss; - ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" - << streaming_identifiers(columnNames) << ") " - << "SELECT " << streaming_identifiers(columnNames) << " FROM " << streaming_identifier(sourceTableName) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" + << streaming_identifiers(columnNames) << ") " + << "SELECT " << streaming_identifiers(columnNames) << " FROM " + << streaming_identifier(sourceTableName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql); } } } diff --git a/dev/mapped_view.h b/dev/mapped_view.h index bd56901eb..db76673eb 100644 --- a/dev/mapped_view.h +++ b/dev/mapped_view.h @@ -51,12 +51,14 @@ namespace sqlite_orm { mapped_iterator begin() { using context_t = serializer_context; + auto& dbObjects = obtain_db_objects(this->storage); context_t context{dbObjects}; context.skip_table_name = false; context.replace_bindable_with_question = true; - statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->expression, context))}; + const std::string sql = serialize(this->expression, context); + statement_finalizer stmt{prepare_stmt(this->connection.get(), sql)}; iterate_ast(this->expression.conditions, conditional_binder{stmt.get()}); return {dbObjects, std::move(stmt)}; } diff --git a/dev/pragma.h b/dev/pragma.h index aef4dc9e4..20332a1a3 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -22,6 +22,7 @@ namespace sqlite_orm { namespace internal { struct storage_base; + struct sqlite_executor; template int getPragmaCallback(void* data, int argc, char** argv, char** x) { @@ -42,7 +43,8 @@ namespace sqlite_orm { struct pragma_t { using get_connection_t = std::function; - pragma_t(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + pragma_t(get_connection_t get_connection_, const sqlite_executor& executor) : + get_connection(std::move(get_connection_)), executor(executor) {} std::vector module_list() { return this->get_pragma>("module_list"); @@ -156,13 +158,17 @@ namespace sqlite_orm { auto connection = this->get_connection(); std::vector result; - std::ostringstream ss; - ss << "PRAGMA " - "table_xinfo(" - << streaming_identifier(tableName) << ")" << std::flush; - perform_exec( + std::string sql; + { + std::ostringstream ss; + ss << "PRAGMA " + "table_xinfo(" + << streaming_identifier(tableName) << ")" << std::flush; + sql = ss.str(); + } + this->executor.perform_exec( connection.get(), - ss.str(), + sql, [](void* data, int argc, char** argv, char**) -> int { auto& res = *(std::vector*)data; if (argc) { @@ -192,14 +198,18 @@ namespace sqlite_orm { std::vector table_info(const std::string& tableName) const { auto connection = this->get_connection(); - std::ostringstream ss; - ss << "PRAGMA " - "table_info(" - << streaming_identifier(tableName) << ")" << std::flush; + std::string sql; + { + std::ostringstream ss; + ss << "PRAGMA " + "table_info(" + << streaming_identifier(tableName) << ")" << std::flush; + sql = ss.str(); + } std::vector result; - perform_exec( + this->executor.perform_exec( connection.get(), - ss.str(), + sql, [](void* data, int argc, char** argv, char**) -> int { auto& res = *(std::vector*)data; if (argc) { @@ -225,12 +235,14 @@ namespace sqlite_orm { int synchronous_ = -1; signed char journal_mode_ = -1; // if != -1 stores static_cast(journal_mode) get_connection_t get_connection; + const sqlite_executor& executor; template T get_pragma(const std::string& name) { auto connection = this->get_connection(); T result; - perform_exec(connection.get(), "PRAGMA " + name, getPragmaCallback, &result); + const std::string sql = "PRAGMA " + name; + this->executor.perform_exec(connection.get(), sql, getPragmaCallback, &result); return result; } @@ -257,12 +269,12 @@ namespace sqlite_orm { this->set_pragma_impl(ss.str(), db); } - void set_pragma_impl(const std::string& query, sqlite3* db = nullptr) { + void set_pragma_impl(const std::string& sql, sqlite3* db = nullptr) { if (db) { - perform_void_exec(db, query); + this->executor.perform_void_exec(db, sql); } else { auto con = this->get_connection(); - perform_void_exec(con.get(), query); + this->executor.perform_void_exec(con.get(), sql); } } }; diff --git a/dev/result_set_view.h b/dev/result_set_view.h index 371489bbc..652d13f48 100644 --- a/dev/result_set_view.h +++ b/dev/result_set_view.h @@ -51,11 +51,13 @@ namespace sqlite_orm::internal { using select_type = polyfill::detected_or_t; using column_result_type = column_result_of_t; using context_t = serializer_context; + context_t context{exprDBOs}; context.skip_table_name = false; context.replace_bindable_with_question = true; - statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->expression, context))}; + const std::string sql = serialize(this->expression, context); + statement_finalizer stmt{prepare_stmt(this->connection.get(), sql)}; iterate_ast(this->expression, conditional_binder{stmt.get()}); // note: it is enough to only use the 'expression DBOs' at compile-time to determine the column results; diff --git a/dev/storage.h b/dev/storage.h index 2598e8d48..36d53c68f 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -142,8 +142,8 @@ namespace sqlite_orm { context_t context{this->db_objects}; statement_serializer serializer; - std::string sql = serializer.serialize(table, context, tableName); - perform_void_exec(db, std::move(sql)); + const std::string sql = serializer.serialize(table, context, tableName); + this->executor.perform_void_exec(db, sql); } /** @@ -159,10 +159,14 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) void drop_column(sqlite3* db, const std::string& tableName, const std::string& columnName) { - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " - << streaming_identifier(columnName) << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " + << streaming_identifier(columnName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql); } #endif @@ -1130,31 +1134,34 @@ namespace sqlite_orm { template sync_schema_result sync_table(const virtual_table_t& virtualTable, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; - auto query = serialize(virtualTable, context); - perform_void_exec(db, query); + const auto sql = serialize(virtualTable, context); + this->executor.perform_void_exec(db, sql); return res; } template sync_schema_result sync_table(const index_t& index, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; - auto query = serialize(index, context); - perform_void_exec(db, query); + const auto sql = serialize(index, context); + this->executor.perform_void_exec(db, sql); return res; } template sync_schema_result sync_table(const trigger_t& trigger, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; // TODO Change accordingly using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly context_t context{this->db_objects}; - auto query = serialize(trigger, context); - perform_void_exec(db, query); + const auto sql = serialize(trigger, context); + this->executor.perform_void_exec(db, sql); return res; } @@ -1166,10 +1173,14 @@ namespace sqlite_orm { using context_t = serializer_context; context_t context{this->db_objects}; - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " << serialize(column, context) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " + << serialize(column, context) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql); } template @@ -1179,8 +1190,9 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); using R = decltype(make_row_extractor(this->db_objects).extract(nullptr, 0)); + std::vector res; - perform_steps( + this->executor.perform_steps( stmt, [rowExtractor = make_row_extractor(this->db_objects), &res](sqlite3_stmt* stmt) { res.push_back(rowExtractor.extract(stmt, 0)); @@ -1192,7 +1204,9 @@ namespace sqlite_orm { template std::string dump_highest_level(E&& expression, bool parametrized) const { const auto& exprDBOs = db_objects_for_expression(this->db_objects, expression); + using context_t = serializer_context>; + context_t context{exprDBOs}; context.replace_bindable_with_question = parametrized; // just like prepare_impl() @@ -1203,14 +1217,16 @@ namespace sqlite_orm { template prepared_statement_t prepare_impl(S statement) { const auto& exprDBOs = db_objects_for_expression(this->db_objects, statement); + using context_t = serializer_context>; + context_t context{exprDBOs}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto con = this->get_connection(); - std::string sql = serialize(statement, context); - sqlite3_stmt* stmt = prepare_stmt(con.get(), std::move(sql)); + const std::string sql = serialize(statement, context); + sqlite3_stmt* stmt = prepare_stmt(con.get(), sql); return prepared_statement_t{std::forward(statement), stmt, con}; } @@ -1400,7 +1416,18 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -1413,7 +1440,18 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->will_run_query) { + this->will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->did_run_query) { + this->did_run_query(sql); + } +#endif } #endif @@ -1421,7 +1459,18 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } template @@ -1435,7 +1484,18 @@ namespace sqlite_orm { [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { return table.object_field_value(object, memberPointer); }); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -1475,8 +1535,18 @@ namespace sqlite_orm { const object_type& o = get_object(expression); processObject(o); })(statement.expression); - +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } templateget_table(), bindValue = field_value_binder{stmt}](auto& object) mutable { using is_without_rowid = typename std::remove_reference_t::is_without_rowid; + table.template for_each_column_excluding< mpl::conjunction>, mpl::disjunction_fn>>( @@ -1520,8 +1591,18 @@ namespace sqlite_orm { const object_type& o = get_object(expression); processObject(o); })(statement.expression); - +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -1529,7 +1610,18 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.ids, conditional_binder{stmt}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } template @@ -1552,7 +1644,18 @@ namespace sqlite_orm { bindValue(polyfill::invoke(column.member_pointer, object)); } }); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } template @@ -1562,11 +1665,22 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::unique_ptr res; +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { res = std::make_unique(); object_from_column_builder builder{*res, stmt}; table.for_each_column(builder); }); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif return res; } @@ -1578,10 +1692,21 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::optional res; +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { object_from_column_builder builder{res.emplace(), stmt}; table.for_each_column(builder); }); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -1594,17 +1719,39 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED std::optional res; +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { object_from_column_builder builder{res.emplace(), stmt}; table.for_each_column(builder); }); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif if (!res.has_value()) { throw std::system_error{orm_error_code::not_found}; } return std::move(res).value(); #else auto& table = this->get_table(); - auto stepRes = sqlite3_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->will_run_query) { + this->will_run_query(sql); + } +#endif + const auto stepRes = sqlite3_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->did_run_query) { + this->did_run_query(sql); + } +#endif switch (stepRes) { case SQLITE_ROW: { T res; @@ -1626,7 +1773,18 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.conditions, conditional_binder{stmt}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } template @@ -1635,7 +1793,18 @@ namespace sqlite_orm { conditional_binder bindNode{stmt}; iterate_ast(statement.expression.set, bindNode); iterate_ast(statement.expression.conditions, bindNode); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -1662,7 +1831,7 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { O obj; object_from_column_builder builder{obj, stmt}; table.for_each_column(builder); @@ -1683,7 +1852,7 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { auto obj = std::make_unique(); object_from_column_builder builder{*obj, stmt}; table.for_each_column(builder); @@ -1705,7 +1874,7 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { auto obj = std::make_optional(); object_from_column_builder builder{*obj, stmt}; table.for_each_column(builder); diff --git a/dev/storage_base.h b/dev/storage_base.h index 9ee3c0a1a..e45bbf616 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -115,7 +115,8 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_vacuum.html */ void vacuum() { - perform_void_exec(this->get_connection().get(), "VACUUM"); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), "VACUUM"); } /** @@ -124,7 +125,8 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_droptable.html */ void drop_table(const std::string& tableName) { - this->drop_table_internal(this->get_connection().get(), tableName, false); + auto connection = this->get_connection(); + this->drop_table_internal(connection.get(), tableName, false); } /** @@ -133,22 +135,28 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_droptable.html */ void drop_table_if_exists(const std::string& tableName) { - this->drop_table_internal(this->get_connection().get(), tableName, true); + auto connection = this->get_connection(); + this->drop_table_internal(connection.get(), tableName, true); } /** * Rename table named `from` to `to`. */ void rename_table(const std::string& from, const std::string& to) { - this->rename_table(this->get_connection().get(), from, to); + auto connection = this->get_connection(); + this->rename_table(connection.get(), from, to); } protected: void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " << streaming_identifier(newName) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " + << streaming_identifier(newName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql); } /** @@ -157,18 +165,22 @@ namespace sqlite_orm { * @return true if table with a given name exists in db, false otherwise. */ bool table_exists(const std::string& tableName) { - auto con = this->get_connection(); - return this->table_exists(con.get(), tableName); + auto connection = this->get_connection(); + return this->table_exists(connection.get(), tableName); } bool table_exists(sqlite3* db, const std::string& tableName) const { bool result = false; - std::stringstream ss; - ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << quote_string_literal("table") - << " AND name = " << quote_string_literal(tableName) << std::flush; - perform_exec( + std::string sql; + { + std::stringstream ss; + ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << quote_string_literal("table") + << " AND name = " << quote_string_literal(tableName) << std::flush; + sql = ss.str(); + } + this->executor.perform_exec( db, - ss.str(), + sql, [](void* data, int argc, char** argv, char** /*azColName*/) -> int { auto& res = *(bool*)data; if (argc) { @@ -195,26 +207,26 @@ namespace sqlite_orm { * sqlite3_changes function. */ int changes() { - auto con = this->get_connection(); - return sqlite3_changes(con.get()); + auto connection = this->get_connection(); + return sqlite3_changes(connection.get()); } /** * sqlite3_total_changes function. */ int total_changes() { - auto con = this->get_connection(); - return sqlite3_total_changes(con.get()); + auto connection = this->get_connection(); + return sqlite3_total_changes(connection.get()); } int64 last_insert_rowid() { - auto con = this->get_connection(); - return sqlite3_last_insert_rowid(con.get()); + auto connection = this->get_connection(); + return sqlite3_last_insert_rowid(connection.get()); } int busy_timeout(int ms) { - auto con = this->get_connection(); - return sqlite3_busy_timeout(con.get(), ms); + auto connection = this->get_connection(); + return sqlite3_busy_timeout(connection.get(), ms); } /** @@ -224,24 +236,27 @@ namespace sqlite_orm { return sqlite3_libversion(); } - bool transaction(const std::function& f) { + bool transaction(const std::function& function) { + if (!function) { + return false; + } auto guard = this->transaction_guard(); - return guard.commit_on_destroy = f(); + return guard.commit_on_destroy = function(); } std::string current_time() { - auto con = this->get_connection(); - return this->current_time(con.get()); + auto connection = this->get_connection(); + return this->current_time(connection.get()); } std::string current_date() { - auto con = this->get_connection(); - return this->current_date(con.get()); + auto connection = this->get_connection(); + return this->current_date(connection.get()); } std::string current_timestamp() { - auto con = this->get_connection(); - return this->current_timestamp(con.get()); + auto connection = this->get_connection(); + return this->current_timestamp(connection.get()); } #if SQLITE_VERSION_NUMBER >= 3007010 @@ -252,8 +267,8 @@ namespace sqlite_orm { * in 3.7.10 https://sqlite.org/changes.html */ int db_release_memory() { - auto con = this->get_connection(); - return sqlite3_db_release_memory(con.get()); + auto connection = this->get_connection(); + return sqlite3_db_release_memory(connection.get()); } #endif @@ -263,11 +278,12 @@ namespace sqlite_orm { * @return Returns list of tables in database. */ std::vector table_names() { - auto con = this->get_connection(); - std::vector tableNames; using data_t = std::vector; - perform_exec( - con.get(), + + auto connection = this->get_connection(); + data_t tableNames; + this->executor.perform_exec( + connection.get(), "SELECT name FROM sqlite_master WHERE type='table'", [](void* data, int argc, char** argv, char** /*columnName*/) -> int { auto& tableNames_ = *(data_t*)data; @@ -559,6 +575,7 @@ namespace sqlite_orm { } void begin_deferred_transaction() { + ; this->begin_transaction_internal("BEGIN DEFERRED TRANSACTION"); } @@ -572,7 +589,7 @@ namespace sqlite_orm { void commit() { sqlite3* db = this->connection->get(); - perform_void_exec(db, "COMMIT"); + this->executor.perform_void_exec(db, "COMMIT"); this->connection->release(); if (this->connection->retain_count() < 0) { throw std::system_error{orm_error_code::no_active_transaction}; @@ -581,7 +598,7 @@ namespace sqlite_orm { void rollback() { sqlite3* db = this->connection->get(); - perform_void_exec(db, "ROLLBACK"); + this->executor.perform_void_exec(db, "ROLLBACK"); this->connection->release(); if (this->connection->retain_count() < 0) { throw std::system_error{orm_error_code::no_active_transaction}; @@ -645,8 +662,8 @@ namespace sqlite_orm { * otherwise true; function is not const because it has to call get_connection() */ bool get_autocommit() { - auto con = this->get_connection(); - return sqlite3_get_autocommit(con.get()); + auto connection = this->get_connection(); + return sqlite3_get_autocommit(connection.get()); } int busy_handler(std::function handler) { @@ -668,7 +685,7 @@ namespace sqlite_orm { on_open_spec onOpenSpec, int foreignKeysCount) : on_open{std::move(onOpenSpec.onOpen)}, isOpenedForever{connectionCtrl.open_forever}, - pragma(std::bind(&storage_base::get_connection, this)), + pragma(std::bind(&storage_base::get_connection, this), executor), limit(std::bind(&storage_base::get_connection, this)), inMemory(filename.empty() || filename == ":memory:"), connection(std::make_unique( @@ -684,7 +701,7 @@ namespace sqlite_orm { } storage_base(const storage_base& other) : - on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), + on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this), executor), limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), isOpenedForever{other.isOpenedForever}, connection(std::make_unique( @@ -708,10 +725,10 @@ namespace sqlite_orm { } } - void begin_transaction_internal(const std::string& query) { + void begin_transaction_internal(const std::string& sql) { this->connection->retain(); sqlite3* db = this->connection->get(); - perform_void_exec(db, query); + this->executor.perform_void_exec(db, sql); } connection_ref get_connection() { @@ -721,14 +738,18 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 void foreign_keys(sqlite3* db, bool value) { - std::stringstream ss; - ss << "PRAGMA foreign_keys = " << value << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "PRAGMA foreign_keys = " << value << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql); } bool foreign_keys(sqlite3* db) { bool result = false; - perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); + this->executor.perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); return result; } #endif @@ -888,29 +909,29 @@ namespace sqlite_orm { } static void try_to_create_scalar_function(sqlite3* db, udf_proxy& udfProxy) { - int rc = sqlite3_create_function_v2(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - udfProxy.func, - nullptr, - nullptr, - nullptr); + const int rc = sqlite3_create_function_v2(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + udfProxy.func, + nullptr, + nullptr, + nullptr); if (rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } static void try_to_create_aggregate_function(sqlite3* db, udf_proxy& udfProxy) { - int rc = sqlite3_create_function(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - nullptr, - udfProxy.func, - aggregate_function_final_callback); + const int rc = sqlite3_create_function(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + nullptr, + udfProxy.func, + aggregate_function_final_callback); if (rc != SQLITE_OK) { throw_translated_sqlite_error(rc); } @@ -918,50 +939,64 @@ namespace sqlite_orm { std::string current_time(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_TIME", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_TIME", extract_single_value, &result); return result; } std::string current_date(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_DATE", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_DATE", extract_single_value, &result); return result; } std::string current_timestamp(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); return result; } void drop_table_internal(sqlite3* db, const std::string& tableName, bool ifExists) { - std::stringstream ss; - ss << "DROP TABLE"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP TABLE"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << streaming_identifier(tableName) << std::flush; + sql = ss.str(); } - ss << ' ' << streaming_identifier(tableName) << std::flush; - perform_void_exec(db, ss.str()); + this->executor.perform_void_exec(db, sql); } void drop_index_internal(const std::string& indexName, bool ifExists) { - std::stringstream ss; - ss << "DROP INDEX"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP INDEX"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << quote_identifier(indexName) << std::flush; + sql = ss.str(); } - ss << ' ' << quote_identifier(indexName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql); } void drop_trigger_internal(const std::string& triggerName, bool ifExists) { - std::stringstream ss; - ss << "DROP TRIGGER"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP TRIGGER"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << quote_identifier(triggerName) << std::flush; + sql = ss.str(); } - ss << ' ' << quote_identifier(triggerName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql); } static int @@ -1030,6 +1065,7 @@ namespace sqlite_orm { std::function _busy_handler; std::list scalarFunctions; std::list aggregateFunctions; + sqlite_executor executor; }; } } diff --git a/dev/util.h b/dev/util.h index cff59dfc5..d8ae8fb19 100644 --- a/dev/util.h +++ b/dev/util.h @@ -4,9 +4,14 @@ #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move +#include // std::function +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // std::string_view +#endif #endif #include "error_code.h" +#include "serialize_result_type.h" SQLITE_ORM_EXPORT namespace sqlite_orm { @@ -53,6 +58,85 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { namespace sqlite_orm { namespace internal { + + struct sqlite_executor { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::function will_run_query; + std::function did_run_query; +#endif + inline void perform_void_exec(sqlite3* db, const serialize_result_type& sql) const { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->will_run_query) { + this->will_run_query(sql); + } +#endif + const int rc = sqlite3_exec(db, sql.data(), nullptr, nullptr, nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->did_run_query) { + this->did_run_query(sql); + } +#endif + } + + inline void perform_exec(sqlite3* db, + const char* sql, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) const { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->will_run_query) { + this->will_run_query(sql); + } +#endif + const int rc = sqlite3_exec(db, sql, callback, user_data, nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->did_run_query) { + this->did_run_query(sql); + } +#endif + } + + inline void perform_exec(sqlite3* db, + const serialize_result_type& query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) const { + return perform_exec(db, query.data(), callback, user_data); + } + + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string_view sql = sqlite3_sql(stmt); + if (this->will_run_query) { + this->will_run_query(sql); + } +#endif + int rc; + do { + switch (rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(stmt); + } + } + } while (rc != SQLITE_DONE); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->did_run_query) { + this->did_run_query(sql); + } +#endif + } + }; + // Wrapper to reduce boiler-plate code inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { sqlite3_reset(stmt); @@ -60,41 +144,17 @@ namespace sqlite_orm { } // note: query is deliberately taken by value, such that it is thrown away early - inline sqlite3_stmt* prepare_stmt(sqlite3* db, std::string query) { + inline sqlite3_stmt* prepare_stmt(sqlite3* db, const serialize_result_type& query) { sqlite3_stmt* stmt; - if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { + if (sqlite3_prepare_v2(db, query.data(), -1, &stmt, nullptr) != SQLITE_OK) { throw_translated_sqlite_error(db); } return stmt; } - inline void perform_void_exec(sqlite3* db, const std::string& query) { - int rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); - } - } - - inline void perform_exec(sqlite3* db, - const char* query, - int (*callback)(void* data, int argc, char** argv, char**), - void* user_data) { - int rc = sqlite3_exec(db, query, callback, user_data, nullptr); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); - } - } - - inline void perform_exec(sqlite3* db, - const std::string& query, - int (*callback)(void* data, int argc, char** argv, char**), - void* user_data) { - return perform_exec(db, query.c_str(), callback, user_data); - } - template void perform_step(sqlite3_stmt* stmt) { - int rc = sqlite3_step(stmt); + const int rc = sqlite3_step(stmt); if (rc != expected) { throw_translated_sqlite_error(stmt); } @@ -113,22 +173,5 @@ namespace sqlite_orm { } } } - - template - void perform_steps(sqlite3_stmt* stmt, L&& lambda) { - int rc; - do { - switch (rc = sqlite3_step(stmt)) { - case SQLITE_ROW: { - lambda(stmt); - } break; - case SQLITE_DONE: - break; - default: { - throw_translated_sqlite_error(stmt); - } - } - } while (rc != SQLITE_DONE); - } } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a97a62719..bb84731fa 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13722,10 +13722,16 @@ namespace sqlite_orm { #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move +#include // std::function +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // std::string_view +#endif #endif // #include "error_code.h" +// #include "serialize_result_type.h" + SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -13771,6 +13777,85 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { namespace sqlite_orm { namespace internal { + + struct sqlite_executor { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::function will_run_query; + std::function did_run_query; +#endif + inline void perform_void_exec(sqlite3* db, const serialize_result_type& sql) const { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->will_run_query) { + this->will_run_query(sql); + } +#endif + const int rc = sqlite3_exec(db, sql.data(), nullptr, nullptr, nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->did_run_query) { + this->did_run_query(sql); + } +#endif + } + + inline void perform_exec(sqlite3* db, + const char* sql, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) const { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->will_run_query) { + this->will_run_query(sql); + } +#endif + const int rc = sqlite3_exec(db, sql, callback, user_data, nullptr); + if (rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->did_run_query) { + this->did_run_query(sql); + } +#endif + } + + inline void perform_exec(sqlite3* db, + const serialize_result_type& query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) const { + return perform_exec(db, query.data(), callback, user_data); + } + + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string_view sql = sqlite3_sql(stmt); + if (this->will_run_query) { + this->will_run_query(sql); + } +#endif + int rc; + do { + switch (rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(stmt); + } + } + } while (rc != SQLITE_DONE); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->did_run_query) { + this->did_run_query(sql); + } +#endif + } + }; + // Wrapper to reduce boiler-plate code inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { sqlite3_reset(stmt); @@ -13778,41 +13863,17 @@ namespace sqlite_orm { } // note: query is deliberately taken by value, such that it is thrown away early - inline sqlite3_stmt* prepare_stmt(sqlite3* db, std::string query) { + inline sqlite3_stmt* prepare_stmt(sqlite3* db, const serialize_result_type& query) { sqlite3_stmt* stmt; - if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { + if (sqlite3_prepare_v2(db, query.data(), -1, &stmt, nullptr) != SQLITE_OK) { throw_translated_sqlite_error(db); } return stmt; } - inline void perform_void_exec(sqlite3* db, const std::string& query) { - int rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); - } - } - - inline void perform_exec(sqlite3* db, - const char* query, - int (*callback)(void* data, int argc, char** argv, char**), - void* user_data) { - int rc = sqlite3_exec(db, query, callback, user_data, nullptr); - if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); - } - } - - inline void perform_exec(sqlite3* db, - const std::string& query, - int (*callback)(void* data, int argc, char** argv, char**), - void* user_data) { - return perform_exec(db, query.c_str(), callback, user_data); - } - template void perform_step(sqlite3_stmt* stmt) { - int rc = sqlite3_step(stmt); + const int rc = sqlite3_step(stmt); if (rc != expected) { throw_translated_sqlite_error(stmt); } @@ -13831,23 +13892,6 @@ namespace sqlite_orm { } } } - - template - void perform_steps(sqlite3_stmt* stmt, L&& lambda) { - int rc; - do { - switch (rc = sqlite3_step(stmt)) { - case SQLITE_ROW: { - lambda(stmt); - } break; - case SQLITE_DONE: - break; - default: { - throw_translated_sqlite_error(stmt); - } - } - } while (rc != SQLITE_DONE); - } } } @@ -16120,12 +16164,14 @@ namespace sqlite_orm { mapped_iterator begin() { using context_t = serializer_context; + auto& dbObjects = obtain_db_objects(this->storage); context_t context{dbObjects}; context.skip_table_name = false; context.replace_bindable_with_question = true; - statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->expression, context))}; + const std::string sql = serialize(this->expression, context); + statement_finalizer stmt{prepare_stmt(this->connection.get(), sql)}; iterate_ast(this->expression.conditions, conditional_binder{stmt.get()}); return {dbObjects, std::move(stmt)}; } @@ -16294,11 +16340,13 @@ namespace sqlite_orm::internal { using select_type = polyfill::detected_or_t; using column_result_type = column_result_of_t; using context_t = serializer_context; + context_t context{exprDBOs}; context.skip_table_name = false; context.replace_bindable_with_question = true; - statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->expression, context))}; + const std::string sql = serialize(this->expression, context); + statement_finalizer stmt{prepare_stmt(this->connection.get(), sql)}; iterate_ast(this->expression, conditional_binder{stmt.get()}); // note: it is enough to only use the 'expression DBOs' at compile-time to determine the column results; @@ -16864,6 +16912,7 @@ namespace sqlite_orm { namespace internal { struct storage_base; + struct sqlite_executor; template int getPragmaCallback(void* data, int argc, char** argv, char** x) { @@ -16884,7 +16933,8 @@ namespace sqlite_orm { struct pragma_t { using get_connection_t = std::function; - pragma_t(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + pragma_t(get_connection_t get_connection_, const sqlite_executor& executor) : + get_connection(std::move(get_connection_)), executor(executor) {} std::vector module_list() { return this->get_pragma>("module_list"); @@ -16998,13 +17048,17 @@ namespace sqlite_orm { auto connection = this->get_connection(); std::vector result; - std::ostringstream ss; - ss << "PRAGMA " - "table_xinfo(" - << streaming_identifier(tableName) << ")" << std::flush; - perform_exec( + std::string sql; + { + std::ostringstream ss; + ss << "PRAGMA " + "table_xinfo(" + << streaming_identifier(tableName) << ")" << std::flush; + sql = ss.str(); + } + this->executor.perform_exec( connection.get(), - ss.str(), + sql, [](void* data, int argc, char** argv, char**) -> int { auto& res = *(std::vector*)data; if (argc) { @@ -17034,14 +17088,18 @@ namespace sqlite_orm { std::vector table_info(const std::string& tableName) const { auto connection = this->get_connection(); - std::ostringstream ss; - ss << "PRAGMA " - "table_info(" - << streaming_identifier(tableName) << ")" << std::flush; + std::string sql; + { + std::ostringstream ss; + ss << "PRAGMA " + "table_info(" + << streaming_identifier(tableName) << ")" << std::flush; + sql = ss.str(); + } std::vector result; - perform_exec( + this->executor.perform_exec( connection.get(), - ss.str(), + sql, [](void* data, int argc, char** argv, char**) -> int { auto& res = *(std::vector*)data; if (argc) { @@ -17067,12 +17125,14 @@ namespace sqlite_orm { int synchronous_ = -1; signed char journal_mode_ = -1; // if != -1 stores static_cast(journal_mode) get_connection_t get_connection; + const sqlite_executor& executor; template T get_pragma(const std::string& name) { auto connection = this->get_connection(); T result; - perform_exec(connection.get(), "PRAGMA " + name, getPragmaCallback, &result); + const std::string sql = "PRAGMA " + name; + this->executor.perform_exec(connection.get(), sql, getPragmaCallback, &result); return result; } @@ -17099,12 +17159,12 @@ namespace sqlite_orm { this->set_pragma_impl(ss.str(), db); } - void set_pragma_impl(const std::string& query, sqlite3* db = nullptr) { + void set_pragma_impl(const std::string& sql, sqlite3* db = nullptr) { if (db) { - perform_void_exec(db, query); + this->executor.perform_void_exec(db, sql); } else { auto con = this->get_connection(); - perform_void_exec(con.get(), query); + this->executor.perform_void_exec(con.get(), sql); } } }; @@ -17989,7 +18049,8 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_vacuum.html */ void vacuum() { - perform_void_exec(this->get_connection().get(), "VACUUM"); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), "VACUUM"); } /** @@ -17998,7 +18059,8 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_droptable.html */ void drop_table(const std::string& tableName) { - this->drop_table_internal(this->get_connection().get(), tableName, false); + auto connection = this->get_connection(); + this->drop_table_internal(connection.get(), tableName, false); } /** @@ -18007,22 +18069,28 @@ namespace sqlite_orm { * More info: https://www.sqlite.org/lang_droptable.html */ void drop_table_if_exists(const std::string& tableName) { - this->drop_table_internal(this->get_connection().get(), tableName, true); + auto connection = this->get_connection(); + this->drop_table_internal(connection.get(), tableName, true); } /** * Rename table named `from` to `to`. */ void rename_table(const std::string& from, const std::string& to) { - this->rename_table(this->get_connection().get(), from, to); + auto connection = this->get_connection(); + this->rename_table(connection.get(), from, to); } protected: void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " << streaming_identifier(newName) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " + << streaming_identifier(newName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql); } /** @@ -18031,18 +18099,22 @@ namespace sqlite_orm { * @return true if table with a given name exists in db, false otherwise. */ bool table_exists(const std::string& tableName) { - auto con = this->get_connection(); - return this->table_exists(con.get(), tableName); + auto connection = this->get_connection(); + return this->table_exists(connection.get(), tableName); } bool table_exists(sqlite3* db, const std::string& tableName) const { bool result = false; - std::stringstream ss; - ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << quote_string_literal("table") - << " AND name = " << quote_string_literal(tableName) << std::flush; - perform_exec( + std::string sql; + { + std::stringstream ss; + ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << quote_string_literal("table") + << " AND name = " << quote_string_literal(tableName) << std::flush; + sql = ss.str(); + } + this->executor.perform_exec( db, - ss.str(), + sql, [](void* data, int argc, char** argv, char** /*azColName*/) -> int { auto& res = *(bool*)data; if (argc) { @@ -18069,26 +18141,26 @@ namespace sqlite_orm { * sqlite3_changes function. */ int changes() { - auto con = this->get_connection(); - return sqlite3_changes(con.get()); + auto connection = this->get_connection(); + return sqlite3_changes(connection.get()); } /** * sqlite3_total_changes function. */ int total_changes() { - auto con = this->get_connection(); - return sqlite3_total_changes(con.get()); + auto connection = this->get_connection(); + return sqlite3_total_changes(connection.get()); } int64 last_insert_rowid() { - auto con = this->get_connection(); - return sqlite3_last_insert_rowid(con.get()); + auto connection = this->get_connection(); + return sqlite3_last_insert_rowid(connection.get()); } int busy_timeout(int ms) { - auto con = this->get_connection(); - return sqlite3_busy_timeout(con.get(), ms); + auto connection = this->get_connection(); + return sqlite3_busy_timeout(connection.get(), ms); } /** @@ -18098,24 +18170,27 @@ namespace sqlite_orm { return sqlite3_libversion(); } - bool transaction(const std::function& f) { + bool transaction(const std::function& function) { + if (!function) { + return false; + } auto guard = this->transaction_guard(); - return guard.commit_on_destroy = f(); + return guard.commit_on_destroy = function(); } std::string current_time() { - auto con = this->get_connection(); - return this->current_time(con.get()); + auto connection = this->get_connection(); + return this->current_time(connection.get()); } std::string current_date() { - auto con = this->get_connection(); - return this->current_date(con.get()); + auto connection = this->get_connection(); + return this->current_date(connection.get()); } std::string current_timestamp() { - auto con = this->get_connection(); - return this->current_timestamp(con.get()); + auto connection = this->get_connection(); + return this->current_timestamp(connection.get()); } #if SQLITE_VERSION_NUMBER >= 3007010 @@ -18126,8 +18201,8 @@ namespace sqlite_orm { * in 3.7.10 https://sqlite.org/changes.html */ int db_release_memory() { - auto con = this->get_connection(); - return sqlite3_db_release_memory(con.get()); + auto connection = this->get_connection(); + return sqlite3_db_release_memory(connection.get()); } #endif @@ -18137,11 +18212,12 @@ namespace sqlite_orm { * @return Returns list of tables in database. */ std::vector table_names() { - auto con = this->get_connection(); - std::vector tableNames; using data_t = std::vector; - perform_exec( - con.get(), + + auto connection = this->get_connection(); + data_t tableNames; + this->executor.perform_exec( + connection.get(), "SELECT name FROM sqlite_master WHERE type='table'", [](void* data, int argc, char** argv, char** /*columnName*/) -> int { auto& tableNames_ = *(data_t*)data; @@ -18433,6 +18509,7 @@ namespace sqlite_orm { } void begin_deferred_transaction() { + ; this->begin_transaction_internal("BEGIN DEFERRED TRANSACTION"); } @@ -18446,7 +18523,7 @@ namespace sqlite_orm { void commit() { sqlite3* db = this->connection->get(); - perform_void_exec(db, "COMMIT"); + this->executor.perform_void_exec(db, "COMMIT"); this->connection->release(); if (this->connection->retain_count() < 0) { throw std::system_error{orm_error_code::no_active_transaction}; @@ -18455,7 +18532,7 @@ namespace sqlite_orm { void rollback() { sqlite3* db = this->connection->get(); - perform_void_exec(db, "ROLLBACK"); + this->executor.perform_void_exec(db, "ROLLBACK"); this->connection->release(); if (this->connection->retain_count() < 0) { throw std::system_error{orm_error_code::no_active_transaction}; @@ -18519,8 +18596,8 @@ namespace sqlite_orm { * otherwise true; function is not const because it has to call get_connection() */ bool get_autocommit() { - auto con = this->get_connection(); - return sqlite3_get_autocommit(con.get()); + auto connection = this->get_connection(); + return sqlite3_get_autocommit(connection.get()); } int busy_handler(std::function handler) { @@ -18542,7 +18619,7 @@ namespace sqlite_orm { on_open_spec onOpenSpec, int foreignKeysCount) : on_open{std::move(onOpenSpec.onOpen)}, isOpenedForever{connectionCtrl.open_forever}, - pragma(std::bind(&storage_base::get_connection, this)), + pragma(std::bind(&storage_base::get_connection, this), executor), limit(std::bind(&storage_base::get_connection, this)), inMemory(filename.empty() || filename == ":memory:"), connection(std::make_unique( @@ -18558,7 +18635,7 @@ namespace sqlite_orm { } storage_base(const storage_base& other) : - on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this)), + on_open(other.on_open), pragma(std::bind(&storage_base::get_connection, this), executor), limit(std::bind(&storage_base::get_connection, this)), inMemory(other.inMemory), isOpenedForever{other.isOpenedForever}, connection(std::make_unique( @@ -18582,10 +18659,10 @@ namespace sqlite_orm { } } - void begin_transaction_internal(const std::string& query) { + void begin_transaction_internal(const std::string& sql) { this->connection->retain(); sqlite3* db = this->connection->get(); - perform_void_exec(db, query); + this->executor.perform_void_exec(db, sql); } connection_ref get_connection() { @@ -18595,14 +18672,18 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3006019 void foreign_keys(sqlite3* db, bool value) { - std::stringstream ss; - ss << "PRAGMA foreign_keys = " << value << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "PRAGMA foreign_keys = " << value << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql); } bool foreign_keys(sqlite3* db) { bool result = false; - perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); + this->executor.perform_exec(db, "PRAGMA foreign_keys", extract_single_value, &result); return result; } #endif @@ -18762,29 +18843,29 @@ namespace sqlite_orm { } static void try_to_create_scalar_function(sqlite3* db, udf_proxy& udfProxy) { - int rc = sqlite3_create_function_v2(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - udfProxy.func, - nullptr, - nullptr, - nullptr); + const int rc = sqlite3_create_function_v2(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + udfProxy.func, + nullptr, + nullptr, + nullptr); if (rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } static void try_to_create_aggregate_function(sqlite3* db, udf_proxy& udfProxy) { - int rc = sqlite3_create_function(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - nullptr, - udfProxy.func, - aggregate_function_final_callback); + const int rc = sqlite3_create_function(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + nullptr, + udfProxy.func, + aggregate_function_final_callback); if (rc != SQLITE_OK) { throw_translated_sqlite_error(rc); } @@ -18792,50 +18873,64 @@ namespace sqlite_orm { std::string current_time(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_TIME", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_TIME", extract_single_value, &result); return result; } std::string current_date(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_DATE", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_DATE", extract_single_value, &result); return result; } std::string current_timestamp(sqlite3* db) { std::string result; - perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); + this->executor.perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value, &result); return result; } void drop_table_internal(sqlite3* db, const std::string& tableName, bool ifExists) { - std::stringstream ss; - ss << "DROP TABLE"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP TABLE"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << streaming_identifier(tableName) << std::flush; + sql = ss.str(); } - ss << ' ' << streaming_identifier(tableName) << std::flush; - perform_void_exec(db, ss.str()); + this->executor.perform_void_exec(db, sql); } void drop_index_internal(const std::string& indexName, bool ifExists) { - std::stringstream ss; - ss << "DROP INDEX"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP INDEX"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << quote_identifier(indexName) << std::flush; + sql = ss.str(); } - ss << ' ' << quote_identifier(indexName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql); } void drop_trigger_internal(const std::string& triggerName, bool ifExists) { - std::stringstream ss; - ss << "DROP TRIGGER"; - if (ifExists) { - ss << " IF EXISTS"; + std::string sql; + { + std::stringstream ss; + ss << "DROP TRIGGER"; + if (ifExists) { + ss << " IF EXISTS"; + } + ss << ' ' << quote_identifier(triggerName) << std::flush; + sql = ss.str(); } - ss << ' ' << quote_identifier(triggerName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql); } static int @@ -18904,6 +18999,7 @@ namespace sqlite_orm { std::function _busy_handler; std::list scalarFunctions; std::list aggregateFunctions; + sqlite_executor executor; }; } } @@ -22706,8 +22802,8 @@ namespace sqlite_orm { context_t context{this->db_objects}; statement_serializer serializer; - std::string sql = serializer.serialize(table, context, tableName); - perform_void_exec(db, std::move(sql)); + const std::string sql = serializer.serialize(table, context, tableName); + this->executor.perform_void_exec(db, sql); } /** @@ -22723,10 +22819,14 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) void drop_column(sqlite3* db, const std::string& tableName, const std::string& columnName) { - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " - << streaming_identifier(columnName) << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " + << streaming_identifier(columnName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql); } #endif @@ -23694,31 +23794,34 @@ namespace sqlite_orm { template sync_schema_result sync_table(const virtual_table_t& virtualTable, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; - auto query = serialize(virtualTable, context); - perform_void_exec(db, query); + const auto sql = serialize(virtualTable, context); + this->executor.perform_void_exec(db, sql); return res; } template sync_schema_result sync_table(const index_t& index, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; - auto query = serialize(index, context); - perform_void_exec(db, query); + const auto sql = serialize(index, context); + this->executor.perform_void_exec(db, sql); return res; } template sync_schema_result sync_table(const trigger_t& trigger, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; // TODO Change accordingly using context_t = serializer_context; + + const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly context_t context{this->db_objects}; - auto query = serialize(trigger, context); - perform_void_exec(db, query); + const auto sql = serialize(trigger, context); + this->executor.perform_void_exec(db, sql); return res; } @@ -23730,10 +23833,14 @@ namespace sqlite_orm { using context_t = serializer_context; context_t context{this->db_objects}; - std::stringstream ss; - ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " << serialize(column, context) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " + << serialize(column, context) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql); } template @@ -23743,8 +23850,9 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); using R = decltype(make_row_extractor(this->db_objects).extract(nullptr, 0)); + std::vector res; - perform_steps( + this->executor.perform_steps( stmt, [rowExtractor = make_row_extractor(this->db_objects), &res](sqlite3_stmt* stmt) { res.push_back(rowExtractor.extract(stmt, 0)); @@ -23756,7 +23864,9 @@ namespace sqlite_orm { template std::string dump_highest_level(E&& expression, bool parametrized) const { const auto& exprDBOs = db_objects_for_expression(this->db_objects, expression); + using context_t = serializer_context>; + context_t context{exprDBOs}; context.replace_bindable_with_question = parametrized; // just like prepare_impl() @@ -23767,14 +23877,16 @@ namespace sqlite_orm { template prepared_statement_t prepare_impl(S statement) { const auto& exprDBOs = db_objects_for_expression(this->db_objects, statement); + using context_t = serializer_context>; + context_t context{exprDBOs}; context.skip_table_name = false; context.replace_bindable_with_question = true; auto con = this->get_connection(); - std::string sql = serialize(statement, context); - sqlite3_stmt* stmt = prepare_stmt(con.get(), std::move(sql)); + const std::string sql = serialize(statement, context); + sqlite3_stmt* stmt = prepare_stmt(con.get(), sql); return prepared_statement_t{std::forward(statement), stmt, con}; } @@ -23964,7 +24076,18 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -23977,7 +24100,18 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->will_run_query) { + this->will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->did_run_query) { + this->did_run_query(sql); + } +#endif } #endif @@ -23985,7 +24119,18 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } template @@ -23999,7 +24144,18 @@ namespace sqlite_orm { [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { return table.object_field_value(object, memberPointer); }); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -24039,8 +24195,18 @@ namespace sqlite_orm { const object_type& o = get_object(expression); processObject(o); })(statement.expression); - +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } templateget_table(), bindValue = field_value_binder{stmt}](auto& object) mutable { using is_without_rowid = typename std::remove_reference_t::is_without_rowid; + table.template for_each_column_excluding< mpl::conjunction>, mpl::disjunction_fn>>( @@ -24084,8 +24251,18 @@ namespace sqlite_orm { const object_type& o = get_object(expression); processObject(o); })(statement.expression); - +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -24093,7 +24270,18 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.ids, conditional_binder{stmt}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } template @@ -24116,7 +24304,18 @@ namespace sqlite_orm { bindValue(polyfill::invoke(column.member_pointer, object)); } }); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } template @@ -24126,11 +24325,22 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::unique_ptr res; +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { res = std::make_unique(); object_from_column_builder builder{*res, stmt}; table.for_each_column(builder); }); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif return res; } @@ -24142,10 +24352,21 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::optional res; +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { object_from_column_builder builder{res.emplace(), stmt}; table.for_each_column(builder); }); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -24158,17 +24379,39 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED std::optional res; +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { object_from_column_builder builder{res.emplace(), stmt}; table.for_each_column(builder); }); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif if (!res.has_value()) { throw std::system_error{orm_error_code::not_found}; } return std::move(res).value(); #else auto& table = this->get_table(); - auto stepRes = sqlite3_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->will_run_query) { + this->will_run_query(sql); + } +#endif + const auto stepRes = sqlite3_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->did_run_query) { + this->did_run_query(sql); + } +#endif switch (stepRes) { case SQLITE_ROW: { T res; @@ -24190,7 +24433,18 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.conditions, conditional_binder{stmt}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } template @@ -24199,7 +24453,18 @@ namespace sqlite_orm { conditional_binder bindNode{stmt}; iterate_ast(statement.expression.set, bindNode); iterate_ast(statement.expression.conditions, bindNode); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + const std::string sql = statement.sql(); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); + } +#endif perform_step(stmt); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } +#endif } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -24226,7 +24491,7 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { O obj; object_from_column_builder builder{obj, stmt}; table.for_each_column(builder); @@ -24247,7 +24512,7 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { auto obj = std::make_unique(); object_from_column_builder builder{*obj, stmt}; table.for_each_column(builder); @@ -24269,7 +24534,7 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); R res; - perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.perform_steps(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { auto obj = std::make_optional(); object_from_column_builder builder{*obj, stmt}; table.for_each_column(builder); @@ -24732,12 +24997,16 @@ namespace sqlite_orm { } }); - std::stringstream ss; - ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" - << streaming_identifiers(columnNames) << ") " - << "SELECT " << streaming_identifiers(columnNames) << " FROM " << streaming_identifier(sourceTableName) - << std::flush; - perform_void_exec(db, ss.str()); + std::string sql; + { + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" + << streaming_identifiers(columnNames) << ") " + << "SELECT " << streaming_identifiers(columnNames) << " FROM " + << streaming_identifier(sourceTableName) << std::flush; + sql = ss.str(); + } + this->executor.perform_void_exec(db, sql); } } } diff --git a/tests/row_extractor.cpp b/tests/row_extractor.cpp index b62d906ae..6e53001fe 100644 --- a/tests/row_extractor.cpp +++ b/tests/row_extractor.cpp @@ -149,7 +149,8 @@ TEST_CASE("Custom row extractors") { SECTION("column text") { auto firstGender = select(distinct(&SuperHero::gender), order_by(&SuperHero::gender), limit(1)); Gender gender = Gender::Invalid; - internal::perform_exec(db, storage.dump(firstGender), &extract_single_value, &gender); + internal::sqlite_executor executor; + executor.perform_exec(db, storage.dump(firstGender), &extract_single_value, &gender); REQUIRE(gender == Gender::Female); } SECTION("udf") { From 1f118434ee9a16482c6579455bf70a78ad1b7ce6 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sun, 23 Mar 2025 21:27:27 +0500 Subject: [PATCH 100/170] fixed is_insert_raw --- dev/storage.h | 16 ++++++++-------- include/sqlite_orm/sqlite_orm.h | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index 36d53c68f..6a3cc5a4b 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1442,14 +1442,14 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED const std::string sql = statement.sql(); - if (this->will_run_query) { - this->will_run_query(sql); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); } #endif perform_step(stmt); #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - if (this->did_run_query) { - this->did_run_query(sql); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); } #endif } @@ -1742,14 +1742,14 @@ namespace sqlite_orm { auto& table = this->get_table(); #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED const std::string sql = statement.sql(); - if (this->will_run_query) { - this->will_run_query(sql); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); } #endif const auto stepRes = sqlite3_step(stmt); #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - if (this->did_run_query) { - this->did_run_query(sql); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); } #endif switch (stepRes) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index bb84731fa..9c75566be 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -24102,14 +24102,14 @@ namespace sqlite_orm { iterate_ast(statement.expression, conditional_binder{stmt}); #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED const std::string sql = statement.sql(); - if (this->will_run_query) { - this->will_run_query(sql); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); } #endif perform_step(stmt); #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - if (this->did_run_query) { - this->did_run_query(sql); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); } #endif } @@ -24402,14 +24402,14 @@ namespace sqlite_orm { auto& table = this->get_table(); #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED const std::string sql = statement.sql(); - if (this->will_run_query) { - this->will_run_query(sql); + if (this->executor.will_run_query) { + this->executor.will_run_query(sql); } #endif const auto stepRes = sqlite3_step(stmt); #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - if (this->did_run_query) { - this->did_run_query(sql); + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); } #endif switch (stepRes) { From 0344ae02a821a14ff3183ade6b2b33bbf7255c86 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 23 Mar 2025 23:05:40 +0200 Subject: [PATCH 101/170] Handle empty range and unregistered function errors up-front --- dev/ast_iterator.h | 20 ++++-- dev/error_code.h | 1 + dev/storage.h | 48 ++++++++++++- dev/storage_base.h | 2 +- include/sqlite_orm/sqlite_orm.h | 71 +++++++++++++++++-- .../prepared_statement_tests/insert_range.cpp | 18 ++--- .../replace_range.cpp | 14 ++-- tests/user_defined_functions.cpp | 8 ++- 8 files changed, 153 insertions(+), 29 deletions(-) diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index d5a5ceba0..112f81255 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -1,6 +1,7 @@ #pragma once #ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // std::is_invocable #include // std::vector #include // std::reference_wrapper #endif @@ -36,7 +37,7 @@ namespace sqlite_orm { * ast_iterator is used in finding literals to be bound to * a statement, and to collect table names. * - * Note that not all leaves of the expression tree are always visited: + * Note that not all leaves of the expression tree are visited: * Column expressions can be more complex, but are passed as a whole to the callable. * Examples are `column_pointer<>` and `alias_column_t<>`. * @@ -52,17 +53,28 @@ namespace sqlite_orm { * L is a callable type. Mostly is a templated lambda */ template - void operator()(const T& t, L& lambda) const { - lambda(t); + void operator()(const node_type& leaf, L& lambda) const { + lambda(leaf); } }; /** - * Simplified API + * Simplified API. + * + * @param lambda Callable invoked with each leaf expression. + * The callable can opt in to be invoked for a node expression. */ template void iterate_ast(const T& t, L&& lambda) { ast_iterator iterator; + +#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L + // possibly invoke lambda with node itself + if constexpr (std::is_invocable, const T&>::value) { + lambda(polyfill::bool_constant{}, t); + } +#endif + iterator(t, lambda); } diff --git a/dev/error_code.h b/dev/error_code.h index a473c780f..c53268fae 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -41,6 +41,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { index_is_out_of_bounds, value_is_null, no_tables_specified, + empty_range, }; } diff --git a/dev/storage.h b/dev/storage.h index 0b44dc83e..f88b44353 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -94,6 +94,42 @@ namespace sqlite_orm { #endif } +#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L + /* + * AST iteration callable that matches function call node expressions and throws a `orm_error_code::function_not_found` exception + * if an application-defined function is not found among the registered application-defined scalar or aggregate functions. + */ + struct udf_presence_checker { + const std::list& _scalarFunctions; + const std::list& _aggregateFunctions; + + // examine `function_call` node expressions + template + void operator()(polyfill::bool_constant, const function_call& udfCall) const { + auto&& name = udfCall.name(); + if (!_contains(_scalarFunctions, name) && !_contains(_aggregateFunctions, name)) + SQLITE_ORM_CPP_UNLIKELY { + throw std::system_error{orm_error_code::function_not_found, std::string(name)}; + } + } + + // swallow leaf expressions + template + void operator()(const T&) const {} + + static bool _contains(const std::list& functions, const std::string_view& name) { +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED + auto it = std::ranges::find(functions, name, &udf_proxy::name); +#else + auto it = std::find_if(functions.begin(), functions.end(), [&name](const udf_proxy& udfProxy) { + return udfProxy.name == name; + }); +#endif + return it != functions.end(); + } + }; +#endif + /** * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` * function. @@ -1202,6 +1238,10 @@ namespace sqlite_orm { template prepared_statement_t prepare_impl(S statement) { +#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L + iterate_ast(statement, udf_presence_checker{this->scalarFunctions, this->aggregateFunctions}); +#endif + const auto& exprDBOs = db_objects_for_expression(this->db_objects, statement); using context_t = serializer_context>; context_t context{exprDBOs}; @@ -1211,7 +1251,7 @@ namespace sqlite_orm { auto con = this->get_connection(); std::string sql = serialize(statement, context); sqlite3_stmt* stmt = prepare_stmt(con.get(), std::move(sql)); - return prepared_statement_t{std::forward(statement), stmt, con}; + return prepared_statement_t{std::forward(statement), stmt, std::move(con)}; } public: @@ -1379,6 +1419,9 @@ namespace sqlite_orm { using object_type = expression_object_type_t; this->assert_mapped_type(); this->assert_insertable_type(); + if (statement.range.first == statement.range.second) { + throw std::system_error{orm_error_code::empty_range}; + } return this->prepare_impl(std::move(statement)); } @@ -1386,6 +1429,9 @@ namespace sqlite_orm { prepared_statement_t prepare(E statement) { using object_type = expression_object_type_t; this->assert_mapped_type(); + if (statement.range.first == statement.range.second) { + throw std::system_error{orm_error_code::empty_range}; + } return this->prepare_impl(std::move(statement)); } diff --git a/dev/storage_base.h b/dev/storage_base.h index 9ee3c0a1a..3414be16c 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -861,7 +861,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto it = std::ranges::find(functions, name, &udf_proxy::name); #else - auto it = std::find_if(functions.begin(), functions.end(), [&name](auto& udfProxy) { + auto it = std::find_if(functions.begin(), functions.end(), [&name](const udf_proxy& udfProxy) { return udfProxy.name == name; }); #endif diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 7fc7dec3d..168bf1b4c 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -2999,6 +2999,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { index_is_out_of_bounds, value_is_null, no_tables_specified, + empty_range, }; } @@ -13957,6 +13958,7 @@ namespace sqlite_orm { // #include "ast_iterator.h" #ifndef SQLITE_ORM_IMPORT_STD_MODULE +#include // std::is_invocable #include // std::vector #include // std::reference_wrapper #endif @@ -15385,7 +15387,7 @@ namespace sqlite_orm { * ast_iterator is used in finding literals to be bound to * a statement, and to collect table names. * - * Note that not all leaves of the expression tree are always visited: + * Note that not all leaves of the expression tree are visited: * Column expressions can be more complex, but are passed as a whole to the callable. * Examples are `column_pointer<>` and `alias_column_t<>`. * @@ -15401,17 +15403,28 @@ namespace sqlite_orm { * L is a callable type. Mostly is a templated lambda */ template - void operator()(const T& t, L& lambda) const { - lambda(t); + void operator()(const node_type& leaf, L& lambda) const { + lambda(leaf); } }; /** - * Simplified API + * Simplified API. + * + * @param lambda Callable invoked with each leaf expression. + * The callable can opt in to be invoked for a node expression. */ template void iterate_ast(const T& t, L&& lambda) { ast_iterator iterator; + +#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L + // possibly invoke lambda with node itself + if constexpr (std::is_invocable, const T&>::value) { + lambda(polyfill::bool_constant{}, t); + } +#endif + iterator(t, lambda); } @@ -18739,7 +18752,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED auto it = std::ranges::find(functions, name, &udf_proxy::name); #else - auto it = std::find_if(functions.begin(), functions.end(), [&name](auto& udfProxy) { + auto it = std::find_if(functions.begin(), functions.end(), [&name](const udf_proxy& udfProxy) { return udfProxy.name == name; }); #endif @@ -22662,6 +22675,42 @@ namespace sqlite_orm { #endif } +#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L + /* + * AST iteration callable that matches function call node expressions and throws a `orm_error_code::function_not_found` exception + * if an application-defined function is not found among the registered application-defined scalar or aggregate functions. + */ + struct udf_presence_checker { + const std::list& _scalarFunctions; + const std::list& _aggregateFunctions; + + // examine `function_call` node expressions + template + void operator()(polyfill::bool_constant, const function_call& udfCall) const { + auto&& name = udfCall.name(); + if (!_contains(_scalarFunctions, name) && !_contains(_aggregateFunctions, name)) + SQLITE_ORM_CPP_UNLIKELY { + throw std::system_error{orm_error_code::function_not_found, std::string(name)}; + } + } + + // swallow leaf expressions + template + void operator()(const T&) const {} + + static bool _contains(const std::list& functions, const std::string_view& name) { +#ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED + auto it = std::ranges::find(functions, name, &udf_proxy::name); +#else + auto it = std::find_if(functions.begin(), functions.end(), [&name](const udf_proxy& udfProxy) { + return udfProxy.name == name; + }); +#endif + return it != functions.end(); + } + }; +#endif + /** * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` * function. @@ -23770,6 +23819,10 @@ namespace sqlite_orm { template prepared_statement_t prepare_impl(S statement) { +#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L + iterate_ast(statement, udf_presence_checker{this->scalarFunctions, this->aggregateFunctions}); +#endif + const auto& exprDBOs = db_objects_for_expression(this->db_objects, statement); using context_t = serializer_context>; context_t context{exprDBOs}; @@ -23779,7 +23832,7 @@ namespace sqlite_orm { auto con = this->get_connection(); std::string sql = serialize(statement, context); sqlite3_stmt* stmt = prepare_stmt(con.get(), std::move(sql)); - return prepared_statement_t{std::forward(statement), stmt, con}; + return prepared_statement_t{std::forward(statement), stmt, std::move(con)}; } public: @@ -23947,6 +24000,9 @@ namespace sqlite_orm { using object_type = expression_object_type_t; this->assert_mapped_type(); this->assert_insertable_type(); + if (statement.range.first == statement.range.second) { + throw std::system_error{orm_error_code::empty_range}; + } return this->prepare_impl(std::move(statement)); } @@ -23954,6 +24010,9 @@ namespace sqlite_orm { prepared_statement_t prepare(E statement) { using object_type = expression_object_type_t; this->assert_mapped_type(); + if (statement.range.first == statement.range.second) { + throw std::system_error{orm_error_code::empty_range}; + } return this->prepare_impl(std::move(statement)); } diff --git a/tests/prepared_statement_tests/insert_range.cpp b/tests/prepared_statement_tests/insert_range.cpp index 9a94ecd4c..42a1b9ea8 100644 --- a/tests/prepared_statement_tests/insert_range.cpp +++ b/tests/prepared_statement_tests/insert_range.cpp @@ -1,6 +1,7 @@ #include #include #include +#include "../catch_matchers.h" #include "prepared_common.h" @@ -9,8 +10,8 @@ using namespace sqlite_orm; TEST_CASE("Prepared insert range") { using namespace PreparedStatementTests; - using Catch::Matchers::ContainsSubstring; using Catch::Matchers::UnorderedEquals; + const ErrorCodeExceptionMatcher emptyRangeExceptionMatcher(orm_error_code::empty_range); const int defaultVisitTime = 50; @@ -41,22 +42,21 @@ TEST_CASE("Prepared insert range") { storage.replace(UserAndVisit{3, 1, "Shine on"}); std::vector users; - std::vector expected; - expected.push_back(User{1, "Team BS"}); - expected.push_back(User{2, "Shy'm"}); - expected.push_back(User{3, "Maître Gims"}); + std::vector expected{{1, "Team BS"}, {2, "Shy'm"}, {3, "Maître Gims"}}; SECTION("empty") { SECTION("strict") { - REQUIRE_THROWS_WITH(storage.prepare(insert_range(users.begin(), users.end())), - ContainsSubstring("incomplete input")); + REQUIRE_THROWS_MATCHES(storage.prepare(insert_range(users.begin(), users.end())), + std::system_error, + emptyRangeExceptionMatcher); } SECTION("container with pointers") { std::vector> usersPointers; - REQUIRE_THROWS_WITH( + REQUIRE_THROWS_MATCHES( storage.prepare( insert_range(usersPointers.begin(), usersPointers.end(), &std::unique_ptr::operator*)), - ContainsSubstring("incomplete input")); + std::system_error, + emptyRangeExceptionMatcher); } } SECTION("one") { diff --git a/tests/prepared_statement_tests/replace_range.cpp b/tests/prepared_statement_tests/replace_range.cpp index 95f1ed66d..0505bb813 100644 --- a/tests/prepared_statement_tests/replace_range.cpp +++ b/tests/prepared_statement_tests/replace_range.cpp @@ -1,5 +1,6 @@ #include #include +#include "../catch_matchers.h" #include "prepared_common.h" @@ -9,6 +10,7 @@ using namespace sqlite_orm; TEST_CASE("Prepared replace range") { using namespace PreparedStatementTests; using Catch::Matchers::UnorderedEquals; + const ErrorCodeExceptionMatcher emptyRangeExceptionMatcher(orm_error_code::empty_range); const int defaultVisitTime = 50; @@ -42,20 +44,20 @@ TEST_CASE("Prepared replace range") { std::vector> userPointers; std::vector expected; SECTION("empty") { - using Catch::Matchers::ContainsSubstring; - expected.push_back(User{1, "Team BS"}); expected.push_back(User{2, "Shy'm"}); expected.push_back(User{3, "Maître Gims"}); SECTION("straight") { - REQUIRE_THROWS_WITH(storage.prepare(replace_range(users.begin(), users.end())), - ContainsSubstring("incomplete input")); + REQUIRE_THROWS_MATCHES(storage.prepare(replace_range(users.begin(), users.end())), + std::system_error, + emptyRangeExceptionMatcher); } SECTION("pointers") { - REQUIRE_THROWS_WITH( + REQUIRE_THROWS_MATCHES( storage.prepare( replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr::operator*)), - ContainsSubstring("incomplete input")); + std::system_error, + emptyRangeExceptionMatcher); } } SECTION("one existing") { diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index c53c4d268..f420d4257 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -279,8 +279,12 @@ struct NonDefaultCtorAggregateFunction { }; TEST_CASE("custom functions") { - using Catch::Matchers::ContainsSubstring; const ErrorCodeExceptionMatcher noMemExceptionMatcher(sqlite_errc(SQLITE_NOMEM)); +#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L + const ErrorCodeExceptionMatcher notFoundExceptionMatcher(orm_error_code::function_not_found); +#else + const ErrorCodeExceptionMatcher notFoundExceptionMatcher(sqlite_errc(SQLITE_ERROR)); +#endif SqrtFunction::callsCount = 0; StatelessHasPrefixFunction::callsCount = 0; @@ -314,7 +318,7 @@ TEST_CASE("custom functions") { storage.delete_aggregate_function(); // call before creation - REQUIRE_THROWS_WITH(storage.select(func(4)), ContainsSubstring("no such function")); + REQUIRE_THROWS_MATCHES(storage.select(func(4)), std::system_error, notFoundExceptionMatcher); // create function REQUIRE(SqrtFunction::callsCount == 0); From 03057a23d9dec133e57f1d339612ed85a6302783 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 23 Mar 2025 23:25:42 +0200 Subject: [PATCH 102/170] SQLite error handling solely based on return code The problem solved is the race condition between executing a statement and retrieving the last error from SQLite. There are two ways to fix this: Either handle errors solely based on the return code of an API call. Or serialize access to the DB handle. The core team of the library decided to implement handling errors based on the return code. When handling errors solely based on the return code, SQLite obviously cannot provide as much information as to why (or where) a statement failed. Important things to know: * The library can handle empty ranges of insert/replace statements and unregistered function errors up-front. * Checking missing application-defined collation sequences wasn't pursued because SQLite will at least return a dedicated `SQLITE_ERROR_MISSING_COLLSEQ` extended error value. It would be easily implementable. * Detailed information of constraint errors [`SQLITE_CONSTRAINT_*], e.g. about the specific column violating a constraint (e.g. primary key, foreign key, not null) is lost; only the type of constraint violation is known. --- dev/connection_holder.h | 2 +- dev/statement_binder.h | 4 ++-- dev/storage.h | 5 ++--- dev/storage_base.h | 8 ++++---- dev/util.h | 15 ++++++++------- include/sqlite_orm/sqlite_orm.h | 34 ++++++++++++++++----------------- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index d9a5b5b61..bf076c9ef 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -36,7 +36,7 @@ namespace sqlite_orm { #endif nullptr); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { - throw_translated_sqlite_error(this->db); + throw_translated_sqlite_error(rc); } if (_didOpenDb) { diff --git a/dev/statement_binder.h b/dev/statement_binder.h index 5ce7b949e..639a1beb2 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -300,7 +300,7 @@ namespace sqlite_orm { void operator()(const T& t) { int rc = statement_binder{}.bind(this->stmt, this->index++, t); if (SQLITE_OK != rc) { - throw_translated_sqlite_error(this->stmt); + throw_translated_sqlite_error(rc); } } @@ -354,7 +354,7 @@ namespace sqlite_orm { void bind(const T& t, size_t idx) const { int rc = statement_binder{}.bind(this->stmt, int(idx + 1), t); if (SQLITE_OK != rc) { - throw_translated_sqlite_error(this->stmt); + throw_translated_sqlite_error(rc); } } diff --git a/dev/storage.h b/dev/storage.h index f88b44353..d4a3b489d 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1650,8 +1650,7 @@ namespace sqlite_orm { return std::move(res).value(); #else auto& table = this->get_table(); - int rc = sqlite3_step(stmt); - switch (rc) { + switch (int rc = sqlite3_step(stmt)) { case SQLITE_ROW: { T res; object_from_column_builder builder{res, stmt}; @@ -1662,7 +1661,7 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::not_found}; } break; default: { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } #endif diff --git a/dev/storage_base.h b/dev/storage_base.h index 3414be16c..82efa01ec 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -538,7 +538,7 @@ namespace sqlite_orm { function, functionExists ? collate_callback : nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -751,7 +751,7 @@ namespace sqlite_orm { for (auto& p: this->collatingFunctions) { int rc = sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -878,7 +878,7 @@ namespace sqlite_orm { nullptr, nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } it = functions.erase(it); @@ -898,7 +898,7 @@ namespace sqlite_orm { nullptr, nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } diff --git a/dev/util.h b/dev/util.h index eec8e2844..1794eaeea 100644 --- a/dev/util.h +++ b/dev/util.h @@ -62,8 +62,9 @@ namespace sqlite_orm { // note: query is deliberately taken by value, such that it is thrown away early inline sqlite3_stmt* prepare_stmt(sqlite3* db, std::string query) { sqlite3_stmt* stmt; - if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { - throw_translated_sqlite_error(db); + int rc = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(rc); } return stmt; } @@ -71,7 +72,7 @@ namespace sqlite_orm { inline void perform_void_exec(sqlite3* db, const std::string& query) { int rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -81,7 +82,7 @@ namespace sqlite_orm { void* user_data) { int rc = sqlite3_exec(db, query, callback, user_data, nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -96,7 +97,7 @@ namespace sqlite_orm { void perform_step(sqlite3_stmt* stmt) { int rc = sqlite3_step(stmt); if (rc != expected) { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } @@ -109,7 +110,7 @@ namespace sqlite_orm { case SQLITE_DONE: return; default: { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } } @@ -124,7 +125,7 @@ namespace sqlite_orm { case SQLITE_DONE: return; default: { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 168bf1b4c..37b6e731b 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -10474,7 +10474,7 @@ namespace sqlite_orm { void operator()(const T& t) { int rc = statement_binder{}.bind(this->stmt, this->index++, t); if (SQLITE_OK != rc) { - throw_translated_sqlite_error(this->stmt); + throw_translated_sqlite_error(rc); } } @@ -10528,7 +10528,7 @@ namespace sqlite_orm { void bind(const T& t, size_t idx) const { int rc = statement_binder{}.bind(this->stmt, int(idx + 1), t); if (SQLITE_OK != rc) { - throw_translated_sqlite_error(this->stmt); + throw_translated_sqlite_error(rc); } } @@ -13781,8 +13781,9 @@ namespace sqlite_orm { // note: query is deliberately taken by value, such that it is thrown away early inline sqlite3_stmt* prepare_stmt(sqlite3* db, std::string query) { sqlite3_stmt* stmt; - if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { - throw_translated_sqlite_error(db); + int rc = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(rc); } return stmt; } @@ -13790,7 +13791,7 @@ namespace sqlite_orm { inline void perform_void_exec(sqlite3* db, const std::string& query) { int rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -13800,7 +13801,7 @@ namespace sqlite_orm { void* user_data) { int rc = sqlite3_exec(db, query, callback, user_data, nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -13815,7 +13816,7 @@ namespace sqlite_orm { void perform_step(sqlite3_stmt* stmt) { int rc = sqlite3_step(stmt); if (rc != expected) { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } @@ -13828,7 +13829,7 @@ namespace sqlite_orm { case SQLITE_DONE: return; default: { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } } @@ -13843,7 +13844,7 @@ namespace sqlite_orm { case SQLITE_DONE: return; default: { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } } @@ -14033,7 +14034,7 @@ namespace sqlite_orm { #endif nullptr); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ { - throw_translated_sqlite_error(this->db); + throw_translated_sqlite_error(rc); } if (_didOpenDb) { @@ -18429,7 +18430,7 @@ namespace sqlite_orm { function, functionExists ? collate_callback : nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -18642,7 +18643,7 @@ namespace sqlite_orm { for (auto& p: this->collatingFunctions) { int rc = sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -18769,7 +18770,7 @@ namespace sqlite_orm { nullptr, nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } it = functions.erase(it); @@ -18789,7 +18790,7 @@ namespace sqlite_orm { nullptr, nullptr); if (rc != SQLITE_OK) { - throw_translated_sqlite_error(db); + throw_translated_sqlite_error(rc); } } @@ -24231,8 +24232,7 @@ namespace sqlite_orm { return std::move(res).value(); #else auto& table = this->get_table(); - int rc = sqlite3_step(stmt); - switch (rc) { + switch (int rc = sqlite3_step(stmt)) { case SQLITE_ROW: { T res; object_from_column_builder builder{res, stmt}; @@ -24243,7 +24243,7 @@ namespace sqlite_orm { throw std::system_error{orm_error_code::not_found}; } break; default: { - throw_translated_sqlite_error(stmt); + throw_translated_sqlite_error(rc); } } #endif From 1068c7428e240fb9ee883e5b0aa40fdb4ca9d05a Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Mon, 24 Mar 2025 10:31:28 +0500 Subject: [PATCH 103/170] added find_column_name to virtual table --- .vscode/settings.json | 14 +++++++++- dev/schema/table.h | 31 ++++++++++++++++++---- include/sqlite_orm/sqlite_orm.h | 31 ++++++++++++++++++---- tests/schema/virtual_table.cpp | 47 ++++++++++++++++++++++++++++++--- 4 files changed, 109 insertions(+), 14 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7216b3ba8..9deae7caf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -74,6 +74,18 @@ "type_traits": "cpp", "utility": "cpp", "__config": "cpp", - "any": "cpp" + "any": "cpp", + "__threading_support": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "execution": "cpp", + "fstream": "cpp", + "future": "cpp", + "iostream": "cpp", + "numbers": "cpp", + "queue": "cpp", + "semaphore": "cpp", + "stack": "cpp", + "unordered_set": "cpp" } } \ No newline at end of file diff --git a/dev/schema/table.h b/dev/schema/table.h index ee958aef9..52d6856b3 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -231,14 +231,16 @@ namespace sqlite_orm { * @return column name or empty string if nothing found. */ template = true> - const std::string* find_column_name(M m) const { - const std::string* res = nullptr; + 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, m](auto& c) { - if (compare_fields(c.member_pointer, m) || compare_fields(c.setter, m)) { - res = &c.name; + [&res, memberPointer](auto& column) { + if (compare_fields(column.member_pointer, memberPointer) || + compare_fields(column.setter, memberPointer)) { + res = &column.name; } }); return res; @@ -343,6 +345,11 @@ namespace sqlite_orm { 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 @@ -388,6 +395,20 @@ namespace sqlite_orm { using col_index_sequence = filter_tuple_sequence_t; iterate_tuple(this->columns, col_index_sequence{}, 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, [&res, memberPointer](auto& column) { + if (compare_fields(column.member_pointer, memberPointer) || + compare_fields(column.setter, memberPointer)) { + res = &column.name; + } + }); + return res; + } }; #endif diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a97a62719..464172670 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -12479,14 +12479,16 @@ namespace sqlite_orm { * @return column name or empty string if nothing found. */ template = true> - const std::string* find_column_name(M m) const { - const std::string* res = nullptr; + 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, m](auto& c) { - if (compare_fields(c.member_pointer, m) || compare_fields(c.setter, m)) { - res = &c.name; + [&res, memberPointer](auto& column) { + if (compare_fields(column.member_pointer, memberPointer) || + compare_fields(column.setter, memberPointer)) { + res = &column.name; } }); return res; @@ -12591,6 +12593,11 @@ namespace sqlite_orm { 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 @@ -12636,6 +12643,20 @@ namespace sqlite_orm { using col_index_sequence = filter_tuple_sequence_t; iterate_tuple(this->columns, col_index_sequence{}, 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, [&res, memberPointer](auto& column) { + if (compare_fields(column.member_pointer, memberPointer) || + compare_fields(column.setter, memberPointer)) { + res = &column.name; + } + }); + return res; + } }; #endif diff --git a/tests/schema/virtual_table.cpp b/tests/schema/virtual_table.cpp index 1e01bbdaf..43af47089 100644 --- a/tests/schema/virtual_table.cpp +++ b/tests/schema/virtual_table.cpp @@ -20,11 +20,22 @@ TEST_CASE("virtual table") { #endif }; + auto virtualTable = + make_virtual_table("posts", using_fts5(make_column("title", &Post::title), make_column("body", &Post::body))); + { + auto compareColumnName = [](const std::string* foundValue, std::string expectedValue) { + if (!foundValue) { + return false; + } + return *foundValue == expectedValue; + }; + REQUIRE(compareColumnName(virtualTable.find_column_name(&Post::title), std::string("title"))); + REQUIRE(compareColumnName(virtualTable.find_column_name(&Post::body), std::string("body"))); + } + /// CREATE VIRTUAL TABLE posts /// USING FTS5(title, body); - auto storage = make_storage( - "", - make_virtual_table("posts", using_fts5(make_column("title", &Post::title), make_column("body", &Post::body)))); + auto storage = make_storage("", std::move(virtualTable)); storage.sync_schema(); storage.sync_schema_simulate(); @@ -80,4 +91,34 @@ TEST_CASE("virtual table") { where(match("SQLite")), order_by(rank())); } + +TEST_CASE("issue1410") { + struct NormalTable { + int id; + std::string text; + int otherValue; + }; + + struct SearchTable { + int normal_table_id; + std::string text; + }; + + auto storage = + make_storage("", + make_table("normal_table", + make_column("id", &NormalTable::id, primary_key().autoincrement()), + make_column("path", &NormalTable::text), + make_column("other_value", &NormalTable::otherValue)), + + make_virtual_table("search_table", + using_fts5(make_column("text", &SearchTable::text), + make_column("normal_table_id", &SearchTable::normal_table_id)))); + storage.sync_schema(); + auto rows = storage.iterate( + where(eq(&NormalTable::id, select(&SearchTable::normal_table_id, where(match("Some Text")))))); + + for (const auto& row: rows) { + } +} #endif From 946ea02a11ab407ad2da33e49d3046513ee6c0cb Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Mon, 24 Mar 2025 19:39:01 +0500 Subject: [PATCH 104/170] fixed init order in storage_base and fixed non member pointer types in fts5 --- CMakeLists.txt | 6 ++++++ dev/schema/table.h | 14 ++++++++------ dev/storage_base.h | 5 ++--- include/sqlite_orm/sqlite_orm.h | 19 ++++++++++--------- tests/schema/virtual_table.cpp | 5 +++-- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61d2617ad..36df229c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,12 @@ target_sources(sqlite_orm INTERFACE $) +# Enable reorder-ctor warning and make it an error +target_compile_options(sqlite_orm INTERFACE + -Wreorder-ctor + -Werror=reorder-ctor +) + include(ucm) if (MSVC) diff --git a/dev/schema/table.h b/dev/schema/table.h index 52d6856b3..d82233648 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -401,12 +401,14 @@ namespace sqlite_orm { using field_type = member_field_type_t; const std::string* res = nullptr; - iterate_tuple(this->columns, [&res, memberPointer](auto& column) { - if (compare_fields(column.member_pointer, memberPointer) || - compare_fields(column.setter, memberPointer)) { - res = &column.name; - } - }); + 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; } }; diff --git a/dev/storage_base.h b/dev/storage_base.h index 9ee3c0a1a..4c89315a2 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -667,10 +667,9 @@ namespace sqlite_orm { connection_control connectionCtrl, on_open_spec onOpenSpec, int foreignKeysCount) : - on_open{std::move(onOpenSpec.onOpen)}, isOpenedForever{connectionCtrl.open_forever}, - pragma(std::bind(&storage_base::get_connection, this)), + on_open{std::move(onOpenSpec.onOpen)}, pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), - inMemory(filename.empty() || filename == ":memory:"), + inMemory(filename.empty() || filename == ":memory:"), isOpenedForever{connectionCtrl.open_forever}, connection(std::make_unique( std::move(filename), std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 464172670..d5119bc10 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -12649,12 +12649,14 @@ namespace sqlite_orm { using field_type = member_field_type_t; const std::string* res = nullptr; - iterate_tuple(this->columns, [&res, memberPointer](auto& column) { - if (compare_fields(column.member_pointer, memberPointer) || - compare_fields(column.setter, memberPointer)) { - res = &column.name; - } - }); + 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; } }; @@ -18562,10 +18564,9 @@ namespace sqlite_orm { connection_control connectionCtrl, on_open_spec onOpenSpec, int foreignKeysCount) : - on_open{std::move(onOpenSpec.onOpen)}, isOpenedForever{connectionCtrl.open_forever}, - pragma(std::bind(&storage_base::get_connection, this)), + on_open{std::move(onOpenSpec.onOpen)}, pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), - inMemory(filename.empty() || filename == ":memory:"), + inMemory(filename.empty() || filename == ":memory:"), isOpenedForever{connectionCtrl.open_forever}, connection(std::make_unique( std::move(filename), std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), diff --git a/tests/schema/virtual_table.cpp b/tests/schema/virtual_table.cpp index 43af47089..0435c19ab 100644 --- a/tests/schema/virtual_table.cpp +++ b/tests/schema/virtual_table.cpp @@ -113,12 +113,13 @@ TEST_CASE("issue1410") { make_virtual_table("search_table", using_fts5(make_column("text", &SearchTable::text), - make_column("normal_table_id", &SearchTable::normal_table_id)))); + make_column("normal_table_id", &SearchTable::normal_table_id), + tokenize("trigram")))); storage.sync_schema(); auto rows = storage.iterate( where(eq(&NormalTable::id, select(&SearchTable::normal_table_id, where(match("Some Text")))))); for (const auto& row: rows) { - } + } // has to be compiled } #endif From a1579109a2dd7b13c8069e6af7a04f42f8bec4b4 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Mon, 24 Mar 2025 19:48:36 +0500 Subject: [PATCH 105/170] added compiler check --- CMakeLists.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 36df229c6..2e90a2e72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,11 +55,12 @@ target_sources(sqlite_orm INTERFACE $) -# Enable reorder-ctor warning and make it an error -target_compile_options(sqlite_orm INTERFACE - -Wreorder-ctor - -Werror=reorder-ctor -) +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + target_compile_options(sqlite_orm INTERFACE + -Wreorder-ctor + -Werror=reorder-ctor + ) +endif() include(ucm) From eb7ba8a87f1385c532f77fd43b39b443b6e5dcf8 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Mon, 24 Mar 2025 19:52:59 +0500 Subject: [PATCH 106/170] added const --- tests/schema/virtual_table.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/schema/virtual_table.cpp b/tests/schema/virtual_table.cpp index 0435c19ab..460cbd8cc 100644 --- a/tests/schema/virtual_table.cpp +++ b/tests/schema/virtual_table.cpp @@ -23,7 +23,7 @@ TEST_CASE("virtual table") { auto virtualTable = make_virtual_table("posts", using_fts5(make_column("title", &Post::title), make_column("body", &Post::body))); { - auto compareColumnName = [](const std::string* foundValue, std::string expectedValue) { + const auto compareColumnName = [](const std::string* foundValue, std::string expectedValue) { if (!foundValue) { return false; } From 0958cbb9faa48968320d1f0638560df4f1b5568a Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Mon, 24 Mar 2025 21:55:45 +0500 Subject: [PATCH 107/170] changed flag name --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e90a2e72..78b3428a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,10 +55,10 @@ target_sources(sqlite_orm INTERFACE $) -if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") target_compile_options(sqlite_orm INTERFACE - -Wreorder-ctor - -Werror=reorder-ctor + -Wreorder + -Werror=reorder ) endif() From cc80391e004bd9c52142f35c64735f9c27ad16a0 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Mon, 24 Mar 2025 22:06:40 +0200 Subject: [PATCH 108/170] Generalized checking the presence of application-defined functions * Also check for named collating functions. * The table name collector now makes use of being callable with a node expression of type `highlight_t<>`. --- dev/ast_iterator.h | 12 ++--- dev/core_functions.h | 9 ++-- dev/functional/cxx_functional_polyfill.h | 16 ++++++ dev/storage.h | 26 +++++++-- dev/table_name_collector.h | 4 +- include/sqlite_orm/sqlite_orm.h | 69 +++++++++++++++++------- tests/ast_iterator_tests.cpp | 43 ++++++++++++++- tests/table_name_collector.cpp | 5 ++ tests/tests.cpp | 2 +- tests/user_defined_functions.cpp | 2 +- 10 files changed, 144 insertions(+), 44 deletions(-) diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index 112f81255..22d587bd0 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -6,7 +6,9 @@ #include // std::reference_wrapper #endif +#include "functional/cxx_functional_polyfill.h" // std::is_invocable #include "tuple_helper/tuple_iteration.h" +#include "functional/static_magic.h" #include "type_traits.h" #include "conditions.h" #include "alias.h" @@ -68,12 +70,11 @@ namespace sqlite_orm { void iterate_ast(const T& t, L&& lambda) { ast_iterator iterator; -#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L // possibly invoke lambda with node itself - if constexpr (std::is_invocable, const T&>::value) { - lambda(polyfill::bool_constant{}, t); - } -#endif + call_if_constexpr, const T&>::value>( + lambda, + polyfill::bool_constant{}, + t); iterator(t, lambda); } @@ -126,7 +127,6 @@ namespace sqlite_orm { template void operator()(const node_type& expression, L& lambda) const { - lambda(expression); iterate_ast(expression.argument0, lambda); iterate_ast(expression.argument1, lambda); iterate_ast(expression.argument2, lambda); diff --git a/dev/core_functions.h b/dev/core_functions.h index 6d65f41c0..dcf7e379b 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -620,12 +620,9 @@ namespace sqlite_orm { using argument1_type = Y; using argument2_type = Z; - argument0_type argument0; - argument1_type argument1; - argument2_type argument2; - - highlight_t(argument0_type argument0, argument1_type argument1, argument2_type argument2) : - argument0(std::move(argument0)), argument1(std::move(argument1)), argument2(std::move(argument2)) {} + argument0_type argument0{}; + argument1_type argument1{}; + argument2_type argument2{}; }; } } diff --git a/dev/functional/cxx_functional_polyfill.h b/dev/functional/cxx_functional_polyfill.h index 74ec1add6..962f03d40 100644 --- a/dev/functional/cxx_functional_polyfill.h +++ b/dev/functional/cxx_functional_polyfill.h @@ -78,6 +78,22 @@ namespace sqlite_orm { return std::forward(callable)(std::forward(args)...); } #endif + +#if __cpp_lib_is_invocable >= 201703L + using std::is_invocable; +#else + template + struct is_invocable_impl : std::false_type {}; + + template + struct is_invocable_impl< + F(Ts...), + polyfill::void_t(), std::declval()...))>> + : std::true_type {}; + + template + struct is_invocable : is_invocable_impl::type {}; +#endif } } diff --git a/dev/storage.h b/dev/storage.h index d4a3b489d..93b238670 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -94,14 +94,17 @@ namespace sqlite_orm { #endif } -#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED /* - * AST iteration callable that matches function call node expressions and throws a `orm_error_code::function_not_found` exception - * if an application-defined function is not found among the registered application-defined scalar or aggregate functions. + * AST iteration callable that matches function call node expressions. + * * Throws a `orm_error_code::function_not_found` exception + * if an application-defined scalar or aggregate function was not registered. + * * Throws a `sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ)` if a named collation function was not registered. */ struct udf_presence_checker { const std::list& _scalarFunctions; const std::list& _aggregateFunctions; + const std::map& _collatingFunctions; // examine `function_call` node expressions template @@ -113,6 +116,17 @@ namespace sqlite_orm { } } + // examine `named_collate` node expressions + void operator()(polyfill::bool_constant, 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)}; +#else + throw std::system_error{sqlite_errc(SQLITE_ERROR), std::string(collateCall.name)}; +#endif + } + } + // swallow leaf expressions template void operator()(const T&) const {} @@ -1238,8 +1252,10 @@ namespace sqlite_orm { template prepared_statement_t prepare_impl(S statement) { -#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L - iterate_ast(statement, udf_presence_checker{this->scalarFunctions, this->aggregateFunctions}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + iterate_ast( + statement, + udf_presence_checker{this->scalarFunctions, this->aggregateFunctions, this->collatingFunctions}); #endif const auto& exprDBOs = db_objects_for_expression(this->db_objects, statement); diff --git a/dev/table_name_collector.h b/dev/table_name_collector.h index fd0939410..567d54141 100644 --- a/dev/table_name_collector.h +++ b/dev/table_name_collector.h @@ -30,8 +30,6 @@ namespace sqlite_orm { const db_objects_type& db_objects; - table_name_collector() = default; - table_name_collector(const db_objects_type& dbObjects) : db_objects{dbObjects} {} template @@ -90,7 +88,7 @@ namespace sqlite_orm { } template - void operator()(const highlight_t&) { + void operator()(polyfill::bool_constant, const highlight_t&) { this->table_names.emplace(lookup_table_name(this->db_objects), ""); } }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 37b6e731b..e9fd15bbb 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -710,6 +710,22 @@ namespace sqlite_orm { return std::forward(callable)(std::forward(args)...); } #endif + +#if __cpp_lib_is_invocable >= 201703L + using std::is_invocable; +#else + template + struct is_invocable_impl : std::false_type {}; + + template + struct is_invocable_impl< + F(Ts...), + polyfill::void_t(), std::declval()...))>> + : std::true_type {}; + + template + struct is_invocable : is_invocable_impl::type {}; +#endif } } @@ -6928,12 +6944,9 @@ namespace sqlite_orm { using argument1_type = Y; using argument2_type = Z; - argument0_type argument0; - argument1_type argument1; - argument2_type argument2; - - highlight_t(argument0_type argument0, argument1_type argument1, argument2_type argument2) : - argument0(std::move(argument0)), argument1(std::move(argument1)), argument2(std::move(argument2)) {} + argument0_type argument0{}; + argument1_type argument1{}; + argument2_type argument2{}; }; } } @@ -13964,8 +13977,12 @@ namespace sqlite_orm { #include // std::reference_wrapper #endif +// #include "functional/cxx_functional_polyfill.h" +// std::is_invocable // #include "tuple_helper/tuple_iteration.h" +// #include "functional/static_magic.h" + // #include "type_traits.h" // #include "conditions.h" @@ -14296,8 +14313,6 @@ namespace sqlite_orm { const db_objects_type& db_objects; - table_name_collector() = default; - table_name_collector(const db_objects_type& dbObjects) : db_objects{dbObjects} {} template @@ -14356,7 +14371,7 @@ namespace sqlite_orm { } template - void operator()(const highlight_t&) { + void operator()(polyfill::bool_constant, const highlight_t&) { this->table_names.emplace(lookup_table_name(this->db_objects), ""); } }; @@ -15419,12 +15434,11 @@ namespace sqlite_orm { void iterate_ast(const T& t, L&& lambda) { ast_iterator iterator; -#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L // possibly invoke lambda with node itself - if constexpr (std::is_invocable, const T&>::value) { - lambda(polyfill::bool_constant{}, t); - } -#endif + call_if_constexpr, const T&>::value>( + lambda, + polyfill::bool_constant{}, + t); iterator(t, lambda); } @@ -15477,7 +15491,6 @@ namespace sqlite_orm { template void operator()(const node_type& expression, L& lambda) const { - lambda(expression); iterate_ast(expression.argument0, lambda); iterate_ast(expression.argument1, lambda); iterate_ast(expression.argument2, lambda); @@ -22676,14 +22689,17 @@ namespace sqlite_orm { #endif } -#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED /* - * AST iteration callable that matches function call node expressions and throws a `orm_error_code::function_not_found` exception - * if an application-defined function is not found among the registered application-defined scalar or aggregate functions. + * AST iteration callable that matches function call node expressions. + * * Throws a `orm_error_code::function_not_found` exception + * if an application-defined scalar or aggregate function was not registered. + * * Throws a `sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ)` if a named collation function was not registered. */ struct udf_presence_checker { const std::list& _scalarFunctions; const std::list& _aggregateFunctions; + const std::map& _collatingFunctions; // examine `function_call` node expressions template @@ -22695,6 +22711,17 @@ namespace sqlite_orm { } } + // examine `named_collate` node expressions + void operator()(polyfill::bool_constant, 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)}; +#else + throw std::system_error{sqlite_errc(SQLITE_ERROR), std::string(collateCall.name)}; +#endif + } + } + // swallow leaf expressions template void operator()(const T&) const {} @@ -23820,8 +23847,10 @@ namespace sqlite_orm { template prepared_statement_t prepare_impl(S statement) { -#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L - iterate_ast(statement, udf_presence_checker{this->scalarFunctions, this->aggregateFunctions}); +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + iterate_ast( + statement, + udf_presence_checker{this->scalarFunctions, this->aggregateFunctions, this->collatingFunctions}); #endif const auto& exprDBOs = db_objects_for_expression(this->db_objects, statement); diff --git a/tests/ast_iterator_tests.cpp b/tests/ast_iterator_tests.cpp index 1b31a29f5..6f1f733d2 100644 --- a/tests/ast_iterator_tests.cpp +++ b/tests/ast_iterator_tests.cpp @@ -10,16 +10,41 @@ using internal::column_alias; using internal::column_pointer; using internal::iterate_ast; +#if __cpp_generic_lambdas >= 201707L +template +struct overload : L... { + using L::operator()...; +}; +#endif + TEST_CASE("ast_iterator") { struct User { int id = 0; std::string name; }; + std::vector typeIndexes; decltype(typeIndexes) expected; - auto lambda = [&typeIndexes](auto& value) { + const auto lambda = [&typeIndexes](auto& value) { typeIndexes.push_back(typeid(value)); }; +#if __cpp_generic_lambdas >= 201707L + const auto nodeLambda = overload( + [&typeIndexes](polyfill::bool_constant, + const internal::function_call& udfCall) { + typeIndexes.push_back(typeid(udfCall.name)); + }, + [&typeIndexes](polyfill::bool_constant, const internal::named_collate_base& collateCall) { + typeIndexes.push_back(typeid(collateCall)); + }, + [&typeIndexes](polyfill::bool_constant, + const internal::highlight_t&) { + typeIndexes.push_back(typeid(T)); + }, + // swallow leaf expressions + [](auto&) {}); +#endif + SECTION("bindables") { auto node = select(1); expected.push_back(typeid(int)); @@ -411,11 +436,25 @@ TEST_CASE("ast_iterator") { #endif SECTION("highlight") { auto expression = highlight(0, std::string(""), std::string("")); - expected.push_back(typeid(expression)); expected.push_back(typeid(int)); expected.push_back(typeid(std::string)); expected.push_back(typeid(std::string)); iterate_ast(expression, lambda); } +#if __cpp_generic_lambdas >= 201707L + SECTION("node expressions") { + struct Func { + static const char* name(); + const std::string& operator()(const std::string& value) const; + }; + auto expression1 = func(&User::name); + auto expression2 = is_equal("", "").collate(""); + auto expression3 = highlight(0, std::string(""), std::string("")); + expected.assign({typeid(internal::udf_holder), typeid(internal::named_collate_base), typeid(User)}); + iterate_ast(expression1, nodeLambda); + iterate_ast(expression2, nodeLambda); + iterate_ast(expression3, nodeLambda); + } +#endif REQUIRE(typeIndexes == expected); } diff --git a/tests/table_name_collector.cpp b/tests/table_name_collector.cpp index d4a838c17..da8b80d5f 100644 --- a/tests/table_name_collector.cpp +++ b/tests/table_name_collector.cpp @@ -18,6 +18,11 @@ TEST_CASE("table name collector") { internal::serializer_context context{dbObjects}; auto collector = internal::make_table_name_collector(context.db_objects); + SECTION("static tests") { + STATIC_REQUIRE(polyfill::is_invocable>, + polyfill::bool_constant, + internal::highlight_t>::value); + } SECTION("from table") { SECTION("regular column") { auto expression = &User::id; diff --git a/tests/tests.cpp b/tests/tests.cpp index c9bfef84f..39630a41d 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -103,7 +103,7 @@ TEST_CASE("Limits") { } TEST_CASE("Custom collate") { -#if SQLITE_VERSION_NUMBER >= 3008008 +#if defined(SQLITE_ORM_STRING_VIEW_SUPPORTED) && SQLITE_VERSION_NUMBER >= 3008008 const ErrorCodeExceptionMatcher collSequExceptionMatcher(sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ)); #else const ErrorCodeExceptionMatcher collSequExceptionMatcher(sqlite_errc(SQLITE_ERROR)); diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index f420d4257..320b43d82 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -280,7 +280,7 @@ struct NonDefaultCtorAggregateFunction { TEST_CASE("custom functions") { const ErrorCodeExceptionMatcher noMemExceptionMatcher(sqlite_errc(SQLITE_NOMEM)); -#if defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) && __cpp_lib_is_invocable >= 201703L +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED const ErrorCodeExceptionMatcher notFoundExceptionMatcher(orm_error_code::function_not_found); #else const ErrorCodeExceptionMatcher notFoundExceptionMatcher(sqlite_errc(SQLITE_ERROR)); From 637ae0f7192d18d55ab7f988b99c63031ad43e1a Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Mon, 24 Mar 2025 22:16:13 +0200 Subject: [PATCH 109/170] Runner-up: Fixed binding values in several 'order by' clauses Accessing bound values in prepared statements must work as well. --- dev/node_tuple.h | 3 +++ include/sqlite_orm/sqlite_orm.h | 3 +++ tests/static_tests/node_tuple.cpp | 20 ++++++++++++-------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/dev/node_tuple.h b/dev/node_tuple.h index b576c511a..1d5a9c518 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -105,6 +105,9 @@ namespace sqlite_orm { template struct node_tuple, void> : node_tuple {}; + template + struct node_tuple, void> : node_tuple_for {}; + template struct node_tuple, void> : node_tuple {}; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a97a62719..21bc9e266 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -24882,6 +24882,9 @@ namespace sqlite_orm { template struct node_tuple, void> : node_tuple {}; + template + struct node_tuple, void> : node_tuple_for {}; + template struct node_tuple, void> : node_tuple {}; diff --git a/tests/static_tests/node_tuple.cpp b/tests/static_tests/node_tuple.cpp index 7109cbf72..89cf2894a 100644 --- a/tests/static_tests/node_tuple.cpp +++ b/tests/static_tests/node_tuple.cpp @@ -595,14 +595,14 @@ TEST_CASE("Node tuple") { using Like = decltype(lk); using NodeTuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "like(&User::name, \"S%\") type 0"); + static_assert(is_same::value, R"(like(&User::name, "S%") type 0)"); } SECTION("like(&User::name, std::string('pattern'), '%')") { auto lk = like(&User::name, std::string("pattern"), "%"); using Like = decltype(lk); using NodeTuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "like(&User::name, std::string(\"pattern\"), \"%\")"); + static_assert(is_same::value, R"(like(&User::name, std::string("pattern"), "%"))"); } SECTION("like(&User::name, std::string('pattern')).escape('%')") { auto lk = like(&User::name, std::string("pattern")).escape("%"); @@ -610,7 +610,7 @@ TEST_CASE("Node tuple") { using NodeTuple = node_tuple_t; using Expected = tuple; static_assert(is_same::value, - "like(&User::name, std::string(\"pattern\")).escape(\"%\")"); + R"(like(&User::name, std::string("pattern")).escape("%"))"); } } SECTION("order_by_t") { @@ -630,12 +630,16 @@ TEST_CASE("Node tuple") { SECTION("column alias in expression") { STATIC_REQUIRE(is_same() > 1))>, tuple>::value); } + SECTION("multi") { + STATIC_REQUIRE(is_same, + tuple>::value); + } } SECTION("glob_t") { auto gl = glob(&User::name, "H*"); using Glob = decltype(gl); using Tuple = node_tuple_t; - static_assert(is_same>::value, "glob(&User::name, \"H*\")"); + static_assert(is_same>::value, R"(glob(&User::name, "H*"))"); } SECTION("between_t") { auto bet = between(&User::id, 10, 20); @@ -656,7 +660,7 @@ TEST_CASE("Node tuple") { using Con = decltype(c); using Tuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "not is_equal(20, \"20\")"); + static_assert(is_same::value, R"(not is_equal(20, "20"))"); } SECTION("not is_not_equal(&User::id, 15.0)") { auto c = not is_not_equal(&User::id, 15.0); @@ -684,7 +688,7 @@ TEST_CASE("Node tuple") { using Con = decltype(c); using Tuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "not less_than(&User::id, std::string(\"6\"))"); + static_assert(is_same::value, R"(not less_than(&User::id, std::string("6")))"); } SECTION("not less_or_equal(&User::id, 10)") { auto c = not less_or_equal(&User::id, 10); @@ -719,14 +723,14 @@ TEST_CASE("Node tuple") { using Con = decltype(c); using Tuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "not like(&User::name, \"*D*\")"); + static_assert(is_same::value, R"(not like(&User::name, "*D*"))"); } SECTION("not glob(&User::name, std::string('_A_'))") { auto c = not glob(&User::name, std::string("_A_")); using Con = decltype(c); using Tuple = node_tuple_t; using Expected = tuple; - static_assert(is_same::value, "not glob(&User::name, std::string(\"_A_\"))"); + static_assert(is_same::value, R"(not glob(&User::name, std::string("_A_")))"); } SECTION("not exists(select(&User::name, where(in(&User::id, {6, 7, 9}))))") { auto c = not exists(select(&User::name, where(in(&User::id, {6, 7, 9})))); From 8e4b72eb0e798a2dcb6ee763f1e72efd3f082068 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 25 Mar 2025 10:53:00 +0200 Subject: [PATCH 110/170] Corrected `polyfill::is_invocable` type trait --- dev/functional/cxx_functional_polyfill.h | 10 ++++------ include/sqlite_orm/sqlite_orm.h | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/dev/functional/cxx_functional_polyfill.h b/dev/functional/cxx_functional_polyfill.h index 962f03d40..6a3e00564 100644 --- a/dev/functional/cxx_functional_polyfill.h +++ b/dev/functional/cxx_functional_polyfill.h @@ -82,17 +82,15 @@ namespace sqlite_orm { #if __cpp_lib_is_invocable >= 201703L using std::is_invocable; #else - template + template struct is_invocable_impl : std::false_type {}; - template - struct is_invocable_impl< - F(Ts...), - polyfill::void_t(), std::declval()...))>> + template + struct is_invocable_impl()...))>, Ts...> : std::true_type {}; template - struct is_invocable : is_invocable_impl::type {}; + struct is_invocable : is_invocable_impl::type {}; #endif } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index e9fd15bbb..97e237eb1 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -714,17 +714,15 @@ namespace sqlite_orm { #if __cpp_lib_is_invocable >= 201703L using std::is_invocable; #else - template + template struct is_invocable_impl : std::false_type {}; - template - struct is_invocable_impl< - F(Ts...), - polyfill::void_t(), std::declval()...))>> + template + struct is_invocable_impl()...))>, Ts...> : std::true_type {}; template - struct is_invocable : is_invocable_impl::type {}; + struct is_invocable : is_invocable_impl::type {}; #endif } } From f6ac5d13ac6a2ec6207798ce462047f5f4af3fef Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 25 Mar 2025 15:26:38 +0200 Subject: [PATCH 111/170] Corrected `polyfill::is_invocable` type trait --- dev/functional/cxx_functional_polyfill.h | 11 ++++++++++- include/sqlite_orm/sqlite_orm.h | 11 ++++++++++- tests/table_name_collector.cpp | 2 +- tests/tests.cpp | 2 +- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/dev/functional/cxx_functional_polyfill.h b/dev/functional/cxx_functional_polyfill.h index 6a3e00564..f575fa24c 100644 --- a/dev/functional/cxx_functional_polyfill.h +++ b/dev/functional/cxx_functional_polyfill.h @@ -85,12 +85,21 @@ namespace sqlite_orm { template struct is_invocable_impl : std::false_type {}; +#if __cplusplus >= 201703 template struct is_invocable_impl()...))>, Ts...> : std::true_type {}; +#else + template + struct is_invocable_impl< + polyfill::void_t>>()( + std::declval()...))>, + Callable, + Args...> : std::true_type {}; +#endif template - struct is_invocable : is_invocable_impl::type {}; + struct is_invocable : is_invocable_impl::type {}; #endif } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 21c405965..0c97b32b2 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -717,12 +717,21 @@ namespace sqlite_orm { template struct is_invocable_impl : std::false_type {}; +#if __cplusplus >= 201703 template struct is_invocable_impl()...))>, Ts...> : std::true_type {}; +#else + template + struct is_invocable_impl< + polyfill::void_t>>()( + std::declval()...))>, + Callable, + Args...> : std::true_type {}; +#endif template - struct is_invocable : is_invocable_impl::type {}; + struct is_invocable : is_invocable_impl::type {}; #endif } } diff --git a/tests/table_name_collector.cpp b/tests/table_name_collector.cpp index da8b80d5f..36f5a79d6 100644 --- a/tests/table_name_collector.cpp +++ b/tests/table_name_collector.cpp @@ -21,7 +21,7 @@ TEST_CASE("table name collector") { SECTION("static tests") { STATIC_REQUIRE(polyfill::is_invocable>, polyfill::bool_constant, - internal::highlight_t>::value); + const internal::highlight_t&>::value); } SECTION("from table") { SECTION("regular column") { diff --git a/tests/tests.cpp b/tests/tests.cpp index 39630a41d..ef5f507d3 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -103,7 +103,7 @@ TEST_CASE("Limits") { } TEST_CASE("Custom collate") { -#if defined(SQLITE_ORM_STRING_VIEW_SUPPORTED) && SQLITE_VERSION_NUMBER >= 3008008 +#if defined(SQLITE_ORM_STRING_VIEW_SUPPORTED) || SQLITE_VERSION_NUMBER >= 3008008 const ErrorCodeExceptionMatcher collSequExceptionMatcher(sqlite_errc(SQLITE_ERROR_MISSING_COLLSEQ)); #else const ErrorCodeExceptionMatcher collSequExceptionMatcher(sqlite_errc(SQLITE_ERROR)); From b429f84265225e4200cf69f1395f6d02cfe9c269 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 25 Mar 2025 20:23:47 +0200 Subject: [PATCH 112/170] Added a deduction guide in a unit test file --- tests/ast_iterator_tests.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/ast_iterator_tests.cpp b/tests/ast_iterator_tests.cpp index 6f1f733d2..78bbbdc60 100644 --- a/tests/ast_iterator_tests.cpp +++ b/tests/ast_iterator_tests.cpp @@ -10,11 +10,15 @@ using internal::column_alias; using internal::column_pointer; using internal::iterate_ast; -#if __cpp_generic_lambdas >= 201707L +#ifdef SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED template -struct overload : L... { +struct overloaded : L... { using L::operator()...; }; +#if __cpp_deduction_guides < 201907L +template +overloaded(L...) -> overloaded; +#endif #endif TEST_CASE("ast_iterator") { @@ -28,8 +32,8 @@ TEST_CASE("ast_iterator") { const auto lambda = [&typeIndexes](auto& value) { typeIndexes.push_back(typeid(value)); }; -#if __cpp_generic_lambdas >= 201707L - const auto nodeLambda = overload( +#ifdef SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED + const auto nodeLambda = overloaded( [&typeIndexes](polyfill::bool_constant, const internal::function_call& udfCall) { typeIndexes.push_back(typeid(udfCall.name)); @@ -441,7 +445,7 @@ TEST_CASE("ast_iterator") { expected.push_back(typeid(std::string)); iterate_ast(expression, lambda); } -#if __cpp_generic_lambdas >= 201707L +#ifdef SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED SECTION("node expressions") { struct Func { static const char* name(); From 4942e5a5fc82e0e56ee54c06cf55d5071e0f64f8 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 25 Mar 2025 22:25:49 +0200 Subject: [PATCH 113/170] Require `if constexpr` support --- dev/core_functions.h | 2 +- dev/functional/config.h | 8 +- dev/functional/cxx_check_prerequisites.h | 8 +- dev/functional/cxx_core_features.h | 4 - dev/functional/static_magic.h | 30 ----- dev/row_extractor.h | 29 ----- dev/serializing_util.h | 2 +- dev/statement_serializer.h | 16 +-- dev/storage.h | 12 +- dev/storage_base.h | 2 +- dev/tuple_helper/tuple_iteration.h | 4 +- include/sqlite_orm/sqlite_orm.h | 117 ++++-------------- .../core_functions_tests.cpp | 2 +- .../core_function_return_types.cpp | 2 +- 14 files changed, 54 insertions(+), 184 deletions(-) diff --git a/dev/core_functions.h b/dev/core_functions.h index dcf7e379b..8d66c034c 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -1697,7 +1697,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { /** * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif */ -#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) /** * NULLIF(X,Y) using common return type of X and Y */ diff --git a/dev/functional/config.h b/dev/functional/config.h index 2478ee0bc..98c97ceff 100644 --- a/dev/functional/config.h +++ b/dev/functional/config.h @@ -24,12 +24,6 @@ #define SQLITE_ORM_INLINE_VAR #endif -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#define SQLITE_ORM_CONSTEXPR_IF constexpr -#else -#define SQLITE_ORM_CONSTEXPR_IF -#endif - #if __cpp_lib_constexpr_functional >= 201907L #define SQLITE_ORM_CONSTEXPR_CPP20 constexpr #else @@ -82,7 +76,7 @@ #define SQLITE_ORM_WITH_CPP20_ALIASES #endif -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) #define SQLITE_ORM_WITH_CTE #endif diff --git a/dev/functional/cxx_check_prerequisites.h b/dev/functional/cxx_check_prerequisites.h index e79cf6130..c3f21922f 100644 --- a/dev/functional/cxx_check_prerequisites.h +++ b/dev/functional/cxx_check_prerequisites.h @@ -5,9 +5,13 @@ */ #if __cpp_aggregate_nsdmi < 201304L -#error A fully C++14-compliant compiler is required. +#error A fully C++17-compliant compiler is required. #endif #if __cpp_constexpr < 201304L -#error A fully C++14-compliant compiler is required. +#error A fully C++17-compliant compiler is required. +#endif + +#if __cpp_if_constexpr < 201606L +#error A fully C++17-compliant compiler is required. #endif diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index 7bb4d2a06..552564536 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -37,10 +37,6 @@ #define SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED #endif -#if __cpp_if_constexpr >= 201606L -#define SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#endif - #if __cpp_inline_variables >= 201606L #define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED #endif diff --git a/dev/functional/static_magic.h b/dev/functional/static_magic.h index d9ec3c8cd..829e7abd4 100644 --- a/dev/functional/static_magic.h +++ b/dev/functional/static_magic.h @@ -1,9 +1,6 @@ #pragma once #ifndef SQLITE_ORM_IMPORT_STD_MODULE -#ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#include // std::false_type, std::true_type, std::integral_constant -#endif #include // std::forward #endif @@ -25,7 +22,6 @@ namespace sqlite_orm { template constexpr empty_callable_t empty_callable{}; -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED template decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { if constexpr (B) { @@ -50,32 +46,6 @@ namespace sqlite_orm { lambda(std::forward(args)...); } } -#else - template - decltype(auto) static_if(std::true_type, T&& trueFn, const F&) { - return std::forward(trueFn); - } - - template - decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { - return std::forward(falseFn); - } - - template - decltype(auto) static_if(T&& trueFn, F&& falseFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); - } - - template - decltype(auto) static_if(T&& trueFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable<>); - } - - template - void call_if_constexpr(L&& lambda, Args&&... args) { - static_if(std::forward(lambda))(std::forward(args)...); - } -#endif } } diff --git a/dev/row_extractor.h b/dev/row_extractor.h index 078088bc4..12a6828a7 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -473,7 +473,6 @@ namespace sqlite_orm { template struct struct_extractor; -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED /* * Returns a value-based row extractor for an unmapped type, * returns a structure extractor for a table reference, tuple or named struct. @@ -487,34 +486,6 @@ namespace sqlite_orm { return row_value_extractor(); } } -#else - /* - * Overload for an unmapped type returns a common row extractor. - */ - template< - class R, - class DBOs, - std::enable_if_t, - polyfill::is_specialization_of, - is_table_reference>>::value, - bool> = true> - auto make_row_extractor(const DBOs& /*dbObjects*/) { - return row_value_extractor(); - } - - /* - * Overload for a table reference, tuple or aggregate of column results returns a structure extractor. - */ - template, - polyfill::is_specialization_of, - is_table_reference>::value, - bool> = true> - struct_extractor make_row_extractor(const DBOs& dbObjects) { - return {dbObjects}; - } -#endif /** * Specialization for a tuple of top-level column results. diff --git a/dev/serializing_util.h b/dev/serializing_util.h index c7aa5e0de..f1fa3be42 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -425,7 +425,7 @@ namespace sqlite_orm { constexpr bool hasExplicitNullableConstraint = mpl::invoke_t, check_if_has_type>, constraints_tuple>::value; - if SQLITE_ORM_CONSTEXPR_IF (!hasExplicitNullableConstraint) { + if constexpr (!hasExplicitNullableConstraint) { if (isNotNull) { ss << " NOT NULL"; } else { diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index ec95e9dc5..adbb7b185 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -731,11 +731,11 @@ namespace sqlite_orm { std::stringstream ss; ss << expression.serialize(); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << "("; } ss << serialize(get_from_expression(expression.argument), subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << ")"; } return ss.str(); @@ -757,11 +757,11 @@ namespace sqlite_orm { std::stringstream ss; ss << static_cast(expression) << " "; - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << "("; } ss << serialize(get_from_expression(expression.c), subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << ")"; } return ss.str(); @@ -786,19 +786,19 @@ namespace sqlite_orm { is_binary_operator>::value; std::stringstream ss; - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeLeft) { + if constexpr (parenthesizeLeft) { ss << "("; } ss << serialize(statement.lhs, subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeLeft) { + if constexpr (parenthesizeLeft) { ss << ")"; } ss << " " << statement.serialize() << " "; - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeRight) { + if constexpr (parenthesizeRight) { ss << "("; } ss << serialize(statement.rhs, subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeRight) { + if constexpr (parenthesizeRight) { ss << ")"; } return ss.str(); diff --git a/dev/storage.h b/dev/storage.h index 93b238670..e54279c4c 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1729,11 +1729,11 @@ namespace sqlite_orm { table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } @@ -1750,11 +1750,11 @@ namespace sqlite_orm { table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } @@ -1772,11 +1772,11 @@ namespace sqlite_orm { table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED diff --git a/dev/storage_base.h b/dev/storage_base.h index 8c6dd6a76..e1d4b0b1e 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -784,7 +784,7 @@ namespace sqlite_orm { : int(std::tuple_size::value); using is_stateless = std::is_empty; auto udfMemorySpace = preallocate_udf_memory(); - if SQLITE_ORM_CONSTEXPR_IF (is_stateless::value) { + if constexpr (is_stateless::value) { constructAt(udfMemorySpace.first); } this->scalarFunctions.emplace_back( diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index e3f83ef75..b9280e6bf 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -8,7 +8,7 @@ namespace sqlite_orm { namespace internal { -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) template constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr (reversed) { @@ -26,7 +26,7 @@ namespace sqlite_orm { template void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { - if SQLITE_ORM_CONSTEXPR_IF (reversed) { + if constexpr (reversed) { iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); lambda(std::get(tpl)); } else { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 0c97b32b2..0cbe5ba57 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -40,11 +40,15 @@ using std::nullptr_t; */ #if __cpp_aggregate_nsdmi < 201304L -#error A fully C++14-compliant compiler is required. +#error A fully C++17-compliant compiler is required. #endif #if __cpp_constexpr < 201304L -#error A fully C++14-compliant compiler is required. +#error A fully C++17-compliant compiler is required. +#endif + +#if __cpp_if_constexpr < 201606L +#error A fully C++17-compliant compiler is required. #endif // #include "cxx_core_features.h" @@ -86,10 +90,6 @@ using std::nullptr_t; #define SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED #endif -#if __cpp_if_constexpr >= 201606L -#define SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#endif - #if __cpp_inline_variables >= 201606L #define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED #endif @@ -230,12 +230,6 @@ using std::nullptr_t; #define SQLITE_ORM_INLINE_VAR #endif -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#define SQLITE_ORM_CONSTEXPR_IF constexpr -#else -#define SQLITE_ORM_CONSTEXPR_IF -#endif - #if __cpp_lib_constexpr_functional >= 201907L #define SQLITE_ORM_CONSTEXPR_CPP20 constexpr #else @@ -288,7 +282,7 @@ using std::nullptr_t; #define SQLITE_ORM_WITH_CPP20_ALIASES #endif -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) #define SQLITE_ORM_WITH_CTE #endif @@ -742,9 +736,6 @@ namespace sqlite_orm { // #include "functional/static_magic.h" #ifndef SQLITE_ORM_IMPORT_STD_MODULE -#ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#include // std::false_type, std::true_type, std::integral_constant -#endif #include // std::forward #endif @@ -766,7 +757,6 @@ namespace sqlite_orm { template constexpr empty_callable_t empty_callable{}; -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED template decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { if constexpr (B) { @@ -791,32 +781,6 @@ namespace sqlite_orm { lambda(std::forward(args)...); } } -#else - template - decltype(auto) static_if(std::true_type, T&& trueFn, const F&) { - return std::forward(trueFn); - } - - template - decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { - return std::forward(falseFn); - } - - template - decltype(auto) static_if(T&& trueFn, F&& falseFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); - } - - template - decltype(auto) static_if(T&& trueFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable<>); - } - - template - void call_if_constexpr(L&& lambda, Args&&... args) { - static_if(std::forward(lambda))(std::forward(args)...); - } -#endif } } @@ -1730,7 +1694,7 @@ namespace sqlite_orm { namespace sqlite_orm { namespace internal { -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) template constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr (reversed) { @@ -1748,7 +1712,7 @@ namespace sqlite_orm { template void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { - if SQLITE_ORM_CONSTEXPR_IF (reversed) { + if constexpr (reversed) { iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); lambda(std::get(tpl)); } else { @@ -8028,7 +7992,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { /** * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif */ -#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) /** * NULLIF(X,Y) using common return type of X and Y */ @@ -13528,7 +13492,6 @@ namespace sqlite_orm { template struct struct_extractor; -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED /* * Returns a value-based row extractor for an unmapped type, * returns a structure extractor for a table reference, tuple or named struct. @@ -13542,34 +13505,6 @@ namespace sqlite_orm { return row_value_extractor(); } } -#else - /* - * Overload for an unmapped type returns a common row extractor. - */ - template< - class R, - class DBOs, - std::enable_if_t, - polyfill::is_specialization_of, - is_table_reference>>::value, - bool> = true> - auto make_row_extractor(const DBOs& /*dbObjects*/) { - return row_value_extractor(); - } - - /* - * Overload for a table reference, tuple or aggregate of column results returns a structure extractor. - */ - template, - polyfill::is_specialization_of, - is_table_reference>::value, - bool> = true> - struct_extractor make_row_extractor(const DBOs& dbObjects) { - return {dbObjects}; - } -#endif /** * Specialization for a tuple of top-level column results. @@ -16907,7 +16842,7 @@ namespace sqlite_orm { constexpr bool hasExplicitNullableConstraint = mpl::invoke_t, check_if_has_type>, constraints_tuple>::value; - if SQLITE_ORM_CONSTEXPR_IF (!hasExplicitNullableConstraint) { + if constexpr (!hasExplicitNullableConstraint) { if (isNotNull) { ss << " NOT NULL"; } else { @@ -18719,7 +18654,7 @@ namespace sqlite_orm { : int(std::tuple_size::value); using is_stateless = std::is_empty; auto udfMemorySpace = preallocate_udf_memory(); - if SQLITE_ORM_CONSTEXPR_IF (is_stateless::value) { + if constexpr (is_stateless::value) { constructAt(udfMemorySpace.first); } this->scalarFunctions.emplace_back( @@ -20601,11 +20536,11 @@ namespace sqlite_orm { std::stringstream ss; ss << expression.serialize(); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << "("; } ss << serialize(get_from_expression(expression.argument), subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << ")"; } return ss.str(); @@ -20627,11 +20562,11 @@ namespace sqlite_orm { std::stringstream ss; ss << static_cast(expression) << " "; - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << "("; } ss << serialize(get_from_expression(expression.c), subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesize) { + if constexpr (parenthesize) { ss << ")"; } return ss.str(); @@ -20656,19 +20591,19 @@ namespace sqlite_orm { is_binary_operator>::value; std::stringstream ss; - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeLeft) { + if constexpr (parenthesizeLeft) { ss << "("; } ss << serialize(statement.lhs, subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeLeft) { + if constexpr (parenthesizeLeft) { ss << ")"; } ss << " " << statement.serialize() << " "; - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeRight) { + if constexpr (parenthesizeRight) { ss << "("; } ss << serialize(statement.rhs, subCtx); - if SQLITE_ORM_CONSTEXPR_IF (parenthesizeRight) { + if constexpr (parenthesizeRight) { ss << ")"; } return ss.str(); @@ -24353,11 +24288,11 @@ namespace sqlite_orm { table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } @@ -24374,11 +24309,11 @@ namespace sqlite_orm { table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } @@ -24396,11 +24331,11 @@ namespace sqlite_orm { table.for_each_column(builder); res.push_back(std::move(obj)); }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr (polyfill::is_specialization_of_v) { res.shrink_to_fit(); } -#endif + return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED diff --git a/tests/built_in_functions_tests/core_functions_tests.cpp b/tests/built_in_functions_tests/core_functions_tests.cpp index e003f0b06..a86b363a3 100644 --- a/tests/built_in_functions_tests/core_functions_tests.cpp +++ b/tests/built_in_functions_tests/core_functions_tests.cpp @@ -459,7 +459,7 @@ TEST_CASE("nullif") { REQUIRE(rows.size() == 1); REQUIRE(rows[0] == true); } -#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) SECTION("common return type") { auto rows = storage.select(&Foo::field, where(nullif(&Foo::field, false))); REQUIRE(rows.size() == 1); diff --git a/tests/static_tests/core_function_return_types.cpp b/tests/static_tests/core_function_return_types.cpp index 333bc30b4..df9daf928 100644 --- a/tests/static_tests/core_function_return_types.cpp +++ b/tests/static_tests/core_function_return_types.cpp @@ -36,7 +36,7 @@ TEST_CASE("Builtin function return types") { // note: return type nullptr_t doesn't make sense but works for unit tests to assert intention STATIC_REQUIRE(is_same(&User::flag, nullptr))::return_type, nullptr_t>::value); -#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) STATIC_REQUIRE(is_same_v>); STATIC_REQUIRE(is_same_v>); STATIC_REQUIRE(is_same_v>); From 8278f6d65804871c3b80314aea5db12acc20d233 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 25 Mar 2025 22:38:27 +0200 Subject: [PATCH 114/170] `if constexpr`-ification * Replaced `call_if_constexpr` and `static_if` constructs with `if constexpr` * Used `if constexpr` for conditional constant expressions --- dev/ast_iterator.h | 8 +- dev/column_names_getter.h | 10 +- dev/cte_column_names_collector.h | 5 +- dev/functional/static_magic.h | 29 -- dev/get_prepared_statement.h | 31 +- dev/implementations/column_definitions.h | 11 +- dev/implementations/storage_definitions.h | 17 +- dev/object_from_column_builder.h | 13 +- dev/row_extractor.h | 1 - dev/schema/table.h | 3 +- dev/statement_serializer.h | 50 +-- dev/storage.h | 88 +++-- dev/storage_impl.h | 11 +- include/sqlite_orm/sqlite_orm.h | 328 ++++++++---------- tests/schema/virtual_table.cpp | 1 + .../functional/static_if_tests.cpp | 85 ----- tests/static_tests/is_bindable.cpp | 12 - 17 files changed, 253 insertions(+), 450 deletions(-) delete mode 100644 tests/static_tests/functional/static_if_tests.cpp diff --git a/dev/ast_iterator.h b/dev/ast_iterator.h index 22d587bd0..85e376f6f 100644 --- a/dev/ast_iterator.h +++ b/dev/ast_iterator.h @@ -8,7 +8,6 @@ #include "functional/cxx_functional_polyfill.h" // std::is_invocable #include "tuple_helper/tuple_iteration.h" -#include "functional/static_magic.h" #include "type_traits.h" #include "conditions.h" #include "alias.h" @@ -71,10 +70,9 @@ namespace sqlite_orm { ast_iterator iterator; // possibly invoke lambda with node itself - call_if_constexpr, const T&>::value>( - lambda, - polyfill::bool_constant{}, - t); + if constexpr (polyfill::is_invocable, const T&>::value) { + lambda(polyfill::bool_constant{}, t); + } iterator(t, lambda); } diff --git a/dev/column_names_getter.h b/dev/column_names_getter.h index 453c17ffe..3b5dfc658 100644 --- a/dev/column_names_getter.h +++ b/dev/column_names_getter.h @@ -36,7 +36,7 @@ namespace sqlite_orm { table.for_each_column([qualified = !context.skip_table_name, &tableName = table.name, &collectedExpressions](const column_identifier& column) { - if (is_alias::value) { + if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + "." + quote_identifier(column.name)); } else if (qualified) { @@ -48,7 +48,7 @@ namespace sqlite_orm { }); } else { collectedExpressions.reserve(collectedExpressions.size() + 1); - if (is_alias::value) { + if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); } else if (!context.skip_table_name) { const basic_table& table = pick_table>(context.db_objects); @@ -100,8 +100,7 @@ namespace sqlite_orm { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if (tuple_has_template::columns_type, asterisk_t>::value && - this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + if constexpr (tuple_has_template::columns_type, asterisk_t>::value) { this->collectedExpressions.shrink_to_fit(); } return this->collectedExpressions; @@ -114,8 +113,7 @@ namespace sqlite_orm { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if (tuple_has_template::columns_type, asterisk_t>::value && - this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + if constexpr (tuple_has_template::columns_type, asterisk_t>::value) { this->collectedExpressions.shrink_to_fit(); } return this->collectedExpressions; diff --git a/dev/cte_column_names_collector.h b/dev/cte_column_names_collector.h index 548ec1b2e..244e304b7 100644 --- a/dev/cte_column_names_collector.h +++ b/dev/cte_column_names_collector.h @@ -159,8 +159,9 @@ namespace sqlite_orm { std::vector columnNames = get_cte_column_names(sel.col, context); // 2. override column names from cte expression - if (size_t n = std::tuple_size_v) { - if (n != columnNames.size()) { + constexpr size_t nExplicitColumns = std::tuple_size_v; + if constexpr (nExplicitColumns) { + if (nExplicitColumns != columnNames.size()) { throw std::system_error{orm_error_code::column_not_found}; } diff --git a/dev/functional/static_magic.h b/dev/functional/static_magic.h index 829e7abd4..7f2fe0ee9 100644 --- a/dev/functional/static_magic.h +++ b/dev/functional/static_magic.h @@ -1,9 +1,5 @@ #pragma once -#ifndef SQLITE_ORM_IMPORT_STD_MODULE -#include // std::forward -#endif - namespace sqlite_orm { // got from here @@ -21,31 +17,6 @@ namespace sqlite_orm { }; template constexpr empty_callable_t empty_callable{}; - - template - decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { - if constexpr (B) { - return std::forward(trueFn); - } else { - return std::forward(falseFn); - } - } - - template - decltype(auto) static_if([[maybe_unused]] T&& trueFn) { - if constexpr (B) { - return std::forward(trueFn); - } else { - return empty_callable<>; - } - } - - template - void call_if_constexpr([[maybe_unused]] L&& lambda, [[maybe_unused]] Args&&... args) { - if constexpr (B) { - lambda(std::forward(args)...); - } - } } } diff --git a/dev/get_prepared_statement.h b/dev/get_prepared_statement.h index dabf61b56..89a90305f 100644 --- a/dev/get_prepared_statement.h +++ b/dev/get_prepared_statement.h @@ -6,7 +6,6 @@ #endif #include "functional/cxx_type_traits_polyfill.h" -#include "functional/static_magic.h" #include "type_traits.h" #include "prepared_statement.h" #include "ast_iterator.h" @@ -135,16 +134,13 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { const result_type* result = nullptr; internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { using node_type = polyfill::remove_cvref_t; - if (internal::is_bindable::value) { + if constexpr (internal::is_bindable::value) { ++index; - } - if (index == N) { - internal::call_if_constexpr::value>( - [](auto& r, auto& n) { - r = &n; - }, - result, - node); + if constexpr (std::is_same::value) { + if (index == N) { + result = &node; + } + } } }); return internal::get_ref(*result); @@ -161,16 +157,13 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { using node_type = polyfill::remove_cvref_t; - if (internal::is_bindable::value) { + if constexpr (internal::is_bindable::value) { ++index; - } - if (index == N) { - internal::call_if_constexpr::value>( - [](auto& r, auto& n) { - r = const_cast>(&n); - }, - result, - node); + if constexpr (std::is_same::value) { + if (index == N) { + result = const_cast(&node); + } + } } }); return internal::get_ref(*result); diff --git a/dev/implementations/column_definitions.h b/dev/implementations/column_definitions.h index 329fadc44..4ce4792bf 100644 --- a/dev/implementations/column_definitions.h +++ b/dev/implementations/column_definitions.h @@ -8,7 +8,6 @@ #include // std::make_unique #endif -#include "../functional/static_magic.h" #include "../tuple_helper/tuple_traits.h" #include "../default_value_extractor.h" #include "../schema/column.h" @@ -21,12 +20,10 @@ namespace sqlite_orm { static constexpr size_t default_op_index = find_tuple_template::value; std::unique_ptr value; - call_if_constexpr::value>( - [&value](auto& constraints) { - value = - std::make_unique(serialize_default_value(std::get(constraints))); - }, - this->constraints); + if constexpr (default_op_index != std::tuple_size::value) { + value = std::make_unique( + serialize_default_value(std::get(this->constraints))); + } return value; } diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index 78e4302e6..ecd7c2f88 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -22,18 +22,23 @@ namespace sqlite_orm { namespace internal { - template template> sync_schema_result storage_t::sync_table(const Table& table, sqlite3* db, bool preserve) { - if (std::is_same, sqlite_master>::value) { - return sync_schema_result::already_in_sync; - } + if constexpr ( #ifdef SQLITE_ENABLE_DBSTAT_VTAB - if (std::is_same, dbstat>::value) { + std::is_same, dbstat>::value || +#endif + std::is_same, sqlite_master>::value) { return sync_schema_result::already_in_sync; + } else { + return this->_sync_normal_table(table, db, preserve); } -#endif // SQLITE_ENABLE_DBSTAT_VTAB + } + + template + template> + sync_schema_result storage_t::_sync_normal_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/object_from_column_builder.h b/dev/object_from_column_builder.h index 76d776492..6b191cf9e 100644 --- a/dev/object_from_column_builder.h +++ b/dev/object_from_column_builder.h @@ -6,7 +6,6 @@ #include // std::move #endif -#include "functional/static_magic.h" #include "member_traits/member_traits.h" #include "table_reference.h" #include "row_extractor.h" @@ -38,13 +37,11 @@ namespace sqlite_orm { void operator()(const column_field& column) { const auto rowExtractor = row_value_extractor>(); auto value = rowExtractor.extract(this->stmt, ++this->columnIndex); - static_if::value>( - [&value, &object = this->object](const auto& column) { - object.*column.member_pointer = std::move(value); - }, - [&value, &object = this->object](const auto& column) { - (object.*column.setter)(std::move(value)); - })(column); + if constexpr (std::is_member_object_pointer::value) { + object.*column.member_pointer = std::move(value); + } else { + (object.*column.setter)(std::move(value)); + }; } }; diff --git a/dev/row_extractor.h b/dev/row_extractor.h index 12a6828a7..297ce30a8 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -21,7 +21,6 @@ #endif #include "functional/cxx_functional_polyfill.h" -#include "functional/static_magic.h" #include "tuple_helper/tuple_transformer.h" #include "column_result_proxy.h" #include "arithmetic_tag.h" diff --git a/dev/schema/table.h b/dev/schema/table.h index d82233648..7680e029a 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -10,7 +10,6 @@ #include "../functional/cxx_type_traits_polyfill.h" #include "../functional/cxx_functional_polyfill.h" -#include "../functional/static_magic.h" #include "../functional/mpl.h" #include "../functional/index_sequence_util.h" #include "../tuple_helper/tuple_filter.h" @@ -192,7 +191,7 @@ namespace sqlite_orm { std::vector primary_key_column_names() const { using pkcol_index_sequence = col_index_sequence_with; - if (pkcol_index_sequence::size() > 0) { + if constexpr (pkcol_index_sequence::size() > 0) { return create_from_tuple>(this->elements, pkcol_index_sequence{}, &column_identifier::name); diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index adbb7b185..f0a4732b4 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -18,6 +18,7 @@ #include "functional/cxx_type_traits_polyfill.h" // std::remove_cvref, std::disjunction #include "functional/cxx_functional_polyfill.h" // std::identity, std::invoke +#include "functional/static_magic.h" #include "functional/mpl.h" #include "tuple_helper/tuple_filter.h" #include "ast/upsert_clause.h" @@ -159,7 +160,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "CREATE TABLE " << streaming_identifier(tableName) << " (" << streaming_expressions_tuple(statement.elements, context) << ")"; - if (statement_type::is_without_rowid_v) { + if constexpr (statement_type::is_without_rowid_v) { ss << " WITHOUT ROWID"; } return ss.str(); @@ -327,18 +328,18 @@ namespace sqlite_orm { ss << "ON CONFLICT"; iterate_tuple(statement.target_args, [&ss, &context](auto& value) { using value_type = polyfill::remove_cvref_t; - auto needParenthesis = std::is_member_pointer::value; + constexpr bool needParenthesis = std::is_member_pointer::value; ss << ' '; - if (needParenthesis) { + if constexpr (needParenthesis) { ss << '('; } ss << serialize(value, context); - if (needParenthesis) { + if constexpr (needParenthesis) { ss << ')'; } }); ss << ' ' << "DO"; - if (std::tuple_size::value == 0) { + if constexpr (std::tuple_size::value == 0) { ss << " NOTHING"; } else { auto updateContext = context; @@ -849,13 +850,13 @@ namespace sqlite_orm { ss << "NOT IN"; } ss << " "; - if (is_compound_operator::value) { + if constexpr (is_compound_operator::value) { ss << '('; } auto newContext = context; newContext.use_parentheses = true; ss << serialize(statement.argument, newContext); - if (is_compound_operator::value) { + if constexpr (is_compound_operator::value) { ss << ')'; } return ss.str(); @@ -902,7 +903,7 @@ namespace sqlite_orm { using args_type = std::tuple; constexpr bool theOnlySelect = std::tuple_size::value == 1 && is_select>::value; - if (!theOnlySelect) { + if constexpr (!theOnlySelect) { ss << "("; } ss << streaming_expressions_tuple(statement.argument, context); @@ -1049,7 +1050,7 @@ namespace sqlite_orm { } using columns_tuple = typename statement_type::columns_tuple; constexpr size_t columnsCount = std::tuple_size::value; - if (columnsCount) { + if constexpr (columnsCount) { ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; } return ss.str(); @@ -1066,7 +1067,7 @@ namespace sqlite_orm { ss << static_cast(c); using columns_tuple = typename statement_type::columns_tuple; const size_t columnsCount = std::tuple_size::value; - if (columnsCount) { + if constexpr (columnsCount) { ss << "(" << streaming_mapped_columns_expressions(c.columns, context) << ")"; } return ss.str(); @@ -1518,7 +1519,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - if (is_insert_raw::value) { + if constexpr (is_insert_raw::value) { ss << "INSERT"; } else { ss << "REPLACE"; @@ -1526,12 +1527,12 @@ namespace sqlite_orm { iterate_tuple(statement.args, [&context, &ss](auto& value) { using value_type = polyfill::remove_cvref_t; ss << ' '; - if (is_columns::value) { + if constexpr (is_columns::value) { auto newContext = context; newContext.skip_table_name = true; newContext.use_parentheses = true; ss << serialize(value, newContext); - } else if (is_values::value || is_select::value) { + } else if constexpr (is_values::value || is_select::value) { auto newContext = context; newContext.use_parentheses = false; ss << serialize(value, newContext); @@ -1787,23 +1788,22 @@ namespace sqlite_orm { subCtx.use_parentheses = true; std::stringstream ss; - if (!is_compound_operator::value) { + if constexpr (!is_compound_operator::value) { if (!sel.highest_level && context.use_parentheses) { ss << "("; } ss << "SELECT "; - call_if_constexpr>( + if constexpr (is_rowset_deduplicator_v) { // note: make use of implicit to-string conversion - [&ss](std::string keyword) { - ss << keyword << ' '; - }, - sel.col); + std::string keyword = sel.col; + ss << keyword << ' '; + } } ss << streaming_serialized(get_column_names(sel.col, subCtx)); using conditions_tuple = typename statement_type::conditions_type; constexpr bool hasExplicitFrom = tuple_has::value; - if (!hasExplicitFrom) { + if constexpr (!hasExplicitFrom) { auto tableNames = collect_table_names(sel, context); using joins_index_sequence = filter_tuple_sequence_t; // deduplicate table names of constrained join statements @@ -1820,7 +1820,7 @@ namespace sqlite_orm { } } ss << streaming_conditions_tuple(sel.conditions, context); - if (!is_compound_operator::value) { + if constexpr (!is_compound_operator::value) { if (!sel.highest_level && context.use_parentheses) { ss << ")"; } @@ -1903,7 +1903,7 @@ namespace sqlite_orm { std::string whereString; iterate_tuple(statement.elements, [&columnNames, &context, &whereString](auto& value) { using value_type = polyfill::remove_cvref_t; - if (!is_where::value) { + if constexpr (!is_where::value) { auto newContext = context; newContext.use_parentheses = false; auto whereString = serialize(value, newContext); @@ -2091,7 +2091,7 @@ namespace sqlite_orm { ss << " BEGIN "; iterate_tuple(statement.elements, [&ss, &context](auto& element) { using element_type = polyfill::remove_cvref_t; - if (is_select::value) { + if constexpr (is_select::value) { auto newContext = context; newContext.use_parentheses = false; ss << serialize(element, newContext); @@ -2243,8 +2243,8 @@ namespace sqlite_orm { newContext.skip_table_name = false; std::stringstream ss; ss << static_cast(limt) << " "; - if (HO) { - if (OI) { + if constexpr (HO) { + if constexpr (OI) { limt.off.apply([&newContext, &ss](auto& value) { ss << serialize(value, newContext); }); diff --git a/dev/storage.h b/dev/storage.h index e54279c4c..e3551bd9d 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -19,7 +19,6 @@ #include "functional/cxx_type_traits_polyfill.h" #include "functional/cxx_functional_polyfill.h" -#include "functional/static_magic.h" #include "functional/mpl.h" #include "tuple_helper/tuple_traits.h" #include "tuple_helper/tuple_filter.h" @@ -851,15 +850,13 @@ namespace sqlite_orm { std::string dump(E&& expression, bool parametrized = false) const { static_assert(is_preparable_v, "Expression must be a high-level statement"); - decltype(auto) e2 = static_if::value>( - [](auto expression) -> auto { - expression.highest_level = true; - return expression; - }, - [](const auto& expression) -> decltype(auto) { - return (expression); - })(std::forward(expression)); - return this->dump_highest_level(e2, parametrized); + if constexpr (is_select::value) { + auto e2 = std::forward(expression); + e2.highest_level = true; + return this->dump_highest_level(e2, parametrized); + } else { + return this->dump_highest_level(expression, parametrized); + } } /** @@ -1211,6 +1208,9 @@ namespace sqlite_orm { template = true> sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); + template = true> + sync_schema_result _sync_normal_table(const Table& table, sqlite3* db, bool preserve); + template void add_column(sqlite3* db, const std::string& tableName, const C& column) const { using context_t = serializer_context; @@ -1516,27 +1516,25 @@ namespace sqlite_orm { })); }; - static_if::value>( - [&processObject](auto& expression) { + if constexpr (is_replace_range::value) { #ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED - std::ranges::for_each(expression.range.first, - expression.range.second, - std::ref(processObject), - std::ref(expression.transformer)); + std::ranges::for_each(statement.expression.range.first, + statement.expression.range.second, + std::ref(processObject), + std::ref(statement.expression.transformer)); #else - auto& transformer = expression.transformer; - std::for_each(expression.range.first, - expression.range.second, - [&processObject, &transformer](auto& item) { - const object_type& object = polyfill::invoke(transformer, item); - processObject(object); - }); + auto& transformer = statement.expression.transformer; + std::for_each(statement.expression.range.first, + statement.expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); #endif - }, - [&processObject](auto& expression) { - const object_type& o = get_object(expression); - processObject(o); - })(statement.expression); + } else { + const object_type& o = get_object(statement.expression); + processObject(o); + }; perform_step(stmt); } @@ -1561,27 +1559,25 @@ namespace sqlite_orm { })); }; - static_if::value>( - [&processObject](auto& expression) { + if constexpr (is_insert_range::value) { #ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED - std::ranges::for_each(expression.range.first, - expression.range.second, - std::ref(processObject), - std::ref(expression.transformer)); + std::ranges::for_each(statement.expression.range.first, + statement.expression.range.second, + std::ref(processObject), + std::ref(statement.expression.transformer)); #else - auto& transformer = expression.transformer; - std::for_each(expression.range.first, - expression.range.second, - [&processObject, &transformer](auto& item) { - const object_type& object = polyfill::invoke(transformer, item); - processObject(object); - }); + auto& transformer = statement.expression.transformer; + std::for_each(statement.expression.range.first, + statement.expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); #endif - }, - [&processObject](auto& expression) { - const object_type& o = get_object(expression); - processObject(o); - })(statement.expression); + } else { + const object_type& o = get_object(statement.expression); + processObject(o); + } perform_step(stmt); return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); diff --git a/dev/storage_impl.h b/dev/storage_impl.h index e6536ecbc..27e51c72e 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -4,7 +4,6 @@ #include // std::string #endif -#include "functional/static_magic.h" #include "functional/index_sequence_util.h" #include "tuple_helper/tuple_traits.h" #include "tuple_helper/tuple_filter.h" @@ -35,11 +34,11 @@ namespace sqlite_orm { template> decltype(auto) lookup_table_name(const DBOs& dbObjects) { - return static_if::value>( - [](const auto& dbObjects) -> const std::string& { - return pick_table(dbObjects).name; - }, - empty_callable)(dbObjects); + if constexpr (is_mapped::value) { + return (pick_table(dbObjects).name); + } else { + return std::string{}; + } } /** diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 0cbe5ba57..bee7fca6c 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -733,58 +733,6 @@ namespace sqlite_orm { namespace polyfill = internal::polyfill; } -// #include "functional/static_magic.h" - -#ifndef SQLITE_ORM_IMPORT_STD_MODULE -#include // std::forward -#endif - -namespace sqlite_orm { - - // got from here - // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co - namespace internal { - - // note: this is a class template accompanied with a variable template because older compilers (e.g. VC 2017) - // cannot handle a static lambda variable inside a template function - template - struct empty_callable_t { - template - R operator()(Args&&...) const { - return R(); - } - }; - template - constexpr empty_callable_t empty_callable{}; - - template - decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { - if constexpr (B) { - return std::forward(trueFn); - } else { - return std::forward(falseFn); - } - } - - template - decltype(auto) static_if([[maybe_unused]] T&& trueFn) { - if constexpr (B) { - return std::forward(trueFn); - } else { - return empty_callable<>; - } - } - - template - void call_if_constexpr([[maybe_unused]] L&& lambda, [[maybe_unused]] Args&&... args) { - if constexpr (B) { - lambda(std::forward(args)...); - } - } - } - -} - // #include "functional/mpl.h" /* @@ -12060,8 +12008,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #include // std::string #endif -// #include "functional/static_magic.h" - // #include "functional/index_sequence_util.h" // #include "tuple_helper/tuple_traits.h" @@ -12092,8 +12038,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../functional/cxx_functional_polyfill.h" -// #include "../functional/static_magic.h" - // #include "../functional/mpl.h" // #include "../functional/index_sequence_util.h" @@ -12425,7 +12369,7 @@ namespace sqlite_orm { std::vector primary_key_column_names() const { using pkcol_index_sequence = col_index_sequence_with; - if (pkcol_index_sequence::size() > 0) { + if constexpr (pkcol_index_sequence::size() > 0) { return create_from_tuple>(this->elements, pkcol_index_sequence{}, &column_identifier::name); @@ -12761,11 +12705,11 @@ namespace sqlite_orm { template> decltype(auto) lookup_table_name(const DBOs& dbObjects) { - return static_if::value>( - [](const auto& dbObjects) -> const std::string& { - return pick_table(dbObjects).name; - }, - empty_callable)(dbObjects); + if constexpr (is_mapped::value) { + return (pick_table(dbObjects).name); + } else { + return std::string{}; + } } /** @@ -12972,8 +12916,6 @@ namespace sqlite_orm { // #include "functional/cxx_functional_polyfill.h" -// #include "functional/static_magic.h" - // #include "tuple_helper/tuple_transformer.h" // #include "column_result_proxy.h" @@ -13615,8 +13557,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #include // std::move #endif -// #include "functional/static_magic.h" - // #include "member_traits/member_traits.h" // #include "table_reference.h" @@ -13652,13 +13592,11 @@ namespace sqlite_orm { void operator()(const column_field& column) { const auto rowExtractor = row_value_extractor>(); auto value = rowExtractor.extract(this->stmt, ++this->columnIndex); - static_if::value>( - [&value, &object = this->object](const auto& column) { - object.*column.member_pointer = std::move(value); - }, - [&value, &object = this->object](const auto& column) { - (object.*column.setter)(std::move(value)); - })(column); + if constexpr (std::is_member_object_pointer::value) { + object.*column.member_pointer = std::move(value); + } else { + (object.*column.setter)(std::move(value)); + }; } }; @@ -13946,8 +13884,6 @@ namespace sqlite_orm { // std::is_invocable // #include "tuple_helper/tuple_iteration.h" -// #include "functional/static_magic.h" - // #include "type_traits.h" // #include "conditions.h" @@ -15400,10 +15336,9 @@ namespace sqlite_orm { ast_iterator iterator; // possibly invoke lambda with node itself - call_if_constexpr, const T&>::value>( - lambda, - polyfill::bool_constant{}, - t); + if constexpr (polyfill::is_invocable, const T&>::value) { + lambda(polyfill::bool_constant{}, t); + } iterator(t, lambda); } @@ -19047,6 +18982,29 @@ namespace sqlite_orm { // std::remove_cvref, std::disjunction // #include "functional/cxx_functional_polyfill.h" // std::identity, std::invoke +// #include "functional/static_magic.h" + +namespace sqlite_orm { + + // got from here + // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co + namespace internal { + + // note: this is a class template accompanied with a variable template because older compilers (e.g. VC 2017) + // cannot handle a static lambda variable inside a template function + template + struct empty_callable_t { + template + R operator()(Args&&...) const { + return R(); + } + }; + template + constexpr empty_callable_t empty_callable{}; + } + +} + // #include "functional/mpl.h" // #include "tuple_helper/tuple_filter.h" @@ -19149,7 +19107,7 @@ namespace sqlite_orm { table.for_each_column([qualified = !context.skip_table_name, &tableName = table.name, &collectedExpressions](const column_identifier& column) { - if (is_alias::value) { + if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + "." + quote_identifier(column.name)); } else if (qualified) { @@ -19161,7 +19119,7 @@ namespace sqlite_orm { }); } else { collectedExpressions.reserve(collectedExpressions.size() + 1); - if (is_alias::value) { + if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); } else if (!context.skip_table_name) { const basic_table& table = pick_table>(context.db_objects); @@ -19213,8 +19171,7 @@ namespace sqlite_orm { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if (tuple_has_template::columns_type, asterisk_t>::value && - this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + if constexpr (tuple_has_template::columns_type, asterisk_t>::value) { this->collectedExpressions.shrink_to_fit(); } return this->collectedExpressions; @@ -19227,8 +19184,7 @@ namespace sqlite_orm { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if (tuple_has_template::columns_type, asterisk_t>::value && - this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + if constexpr (tuple_has_template::columns_type, asterisk_t>::value) { this->collectedExpressions.shrink_to_fit(); } return this->collectedExpressions; @@ -19412,8 +19368,9 @@ namespace sqlite_orm { std::vector columnNames = get_cte_column_names(sel.col, context); // 2. override column names from cte expression - if (size_t n = std::tuple_size_v) { - if (n != columnNames.size()) { + constexpr size_t nExplicitColumns = std::tuple_size_v; + if constexpr (nExplicitColumns) { + if (nExplicitColumns != columnNames.size()) { throw std::system_error{orm_error_code::column_not_found}; } @@ -19964,7 +19921,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "CREATE TABLE " << streaming_identifier(tableName) << " (" << streaming_expressions_tuple(statement.elements, context) << ")"; - if (statement_type::is_without_rowid_v) { + if constexpr (statement_type::is_without_rowid_v) { ss << " WITHOUT ROWID"; } return ss.str(); @@ -20132,18 +20089,18 @@ namespace sqlite_orm { ss << "ON CONFLICT"; iterate_tuple(statement.target_args, [&ss, &context](auto& value) { using value_type = polyfill::remove_cvref_t; - auto needParenthesis = std::is_member_pointer::value; + constexpr bool needParenthesis = std::is_member_pointer::value; ss << ' '; - if (needParenthesis) { + if constexpr (needParenthesis) { ss << '('; } ss << serialize(value, context); - if (needParenthesis) { + if constexpr (needParenthesis) { ss << ')'; } }); ss << ' ' << "DO"; - if (std::tuple_size::value == 0) { + if constexpr (std::tuple_size::value == 0) { ss << " NOTHING"; } else { auto updateContext = context; @@ -20654,13 +20611,13 @@ namespace sqlite_orm { ss << "NOT IN"; } ss << " "; - if (is_compound_operator::value) { + if constexpr (is_compound_operator::value) { ss << '('; } auto newContext = context; newContext.use_parentheses = true; ss << serialize(statement.argument, newContext); - if (is_compound_operator::value) { + if constexpr (is_compound_operator::value) { ss << ')'; } return ss.str(); @@ -20707,7 +20664,7 @@ namespace sqlite_orm { using args_type = std::tuple; constexpr bool theOnlySelect = std::tuple_size::value == 1 && is_select>::value; - if (!theOnlySelect) { + if constexpr (!theOnlySelect) { ss << "("; } ss << streaming_expressions_tuple(statement.argument, context); @@ -20854,7 +20811,7 @@ namespace sqlite_orm { } using columns_tuple = typename statement_type::columns_tuple; constexpr size_t columnsCount = std::tuple_size::value; - if (columnsCount) { + if constexpr (columnsCount) { ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; } return ss.str(); @@ -20871,7 +20828,7 @@ namespace sqlite_orm { ss << static_cast(c); using columns_tuple = typename statement_type::columns_tuple; const size_t columnsCount = std::tuple_size::value; - if (columnsCount) { + if constexpr (columnsCount) { ss << "(" << streaming_mapped_columns_expressions(c.columns, context) << ")"; } return ss.str(); @@ -21323,7 +21280,7 @@ namespace sqlite_orm { template std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - if (is_insert_raw::value) { + if constexpr (is_insert_raw::value) { ss << "INSERT"; } else { ss << "REPLACE"; @@ -21331,12 +21288,12 @@ namespace sqlite_orm { iterate_tuple(statement.args, [&context, &ss](auto& value) { using value_type = polyfill::remove_cvref_t; ss << ' '; - if (is_columns::value) { + if constexpr (is_columns::value) { auto newContext = context; newContext.skip_table_name = true; newContext.use_parentheses = true; ss << serialize(value, newContext); - } else if (is_values::value || is_select::value) { + } else if constexpr (is_values::value || is_select::value) { auto newContext = context; newContext.use_parentheses = false; ss << serialize(value, newContext); @@ -21592,23 +21549,22 @@ namespace sqlite_orm { subCtx.use_parentheses = true; std::stringstream ss; - if (!is_compound_operator::value) { + if constexpr (!is_compound_operator::value) { if (!sel.highest_level && context.use_parentheses) { ss << "("; } ss << "SELECT "; - call_if_constexpr>( + if constexpr (is_rowset_deduplicator_v) { // note: make use of implicit to-string conversion - [&ss](std::string keyword) { - ss << keyword << ' '; - }, - sel.col); + std::string keyword = sel.col; + ss << keyword << ' '; + } } ss << streaming_serialized(get_column_names(sel.col, subCtx)); using conditions_tuple = typename statement_type::conditions_type; constexpr bool hasExplicitFrom = tuple_has::value; - if (!hasExplicitFrom) { + if constexpr (!hasExplicitFrom) { auto tableNames = collect_table_names(sel, context); using joins_index_sequence = filter_tuple_sequence_t; // deduplicate table names of constrained join statements @@ -21625,7 +21581,7 @@ namespace sqlite_orm { } } ss << streaming_conditions_tuple(sel.conditions, context); - if (!is_compound_operator::value) { + if constexpr (!is_compound_operator::value) { if (!sel.highest_level && context.use_parentheses) { ss << ")"; } @@ -21708,7 +21664,7 @@ namespace sqlite_orm { std::string whereString; iterate_tuple(statement.elements, [&columnNames, &context, &whereString](auto& value) { using value_type = polyfill::remove_cvref_t; - if (!is_where::value) { + if constexpr (!is_where::value) { auto newContext = context; newContext.use_parentheses = false; auto whereString = serialize(value, newContext); @@ -21896,7 +21852,7 @@ namespace sqlite_orm { ss << " BEGIN "; iterate_tuple(statement.elements, [&ss, &context](auto& element) { using element_type = polyfill::remove_cvref_t; - if (is_select::value) { + if constexpr (is_select::value) { auto newContext = context; newContext.use_parentheses = false; ss << serialize(element, newContext); @@ -22048,8 +22004,8 @@ namespace sqlite_orm { newContext.skip_table_name = false; std::stringstream ss; ss << static_cast(limt) << " "; - if (HO) { - if (OI) { + if constexpr (HO) { + if constexpr (OI) { limt.off.apply([&newContext, &ss](auto& value) { ss << serialize(value, newContext); }); @@ -23410,15 +23366,13 @@ namespace sqlite_orm { std::string dump(E&& expression, bool parametrized = false) const { static_assert(is_preparable_v, "Expression must be a high-level statement"); - decltype(auto) e2 = static_if::value>( - [](auto expression) -> auto { - expression.highest_level = true; - return expression; - }, - [](const auto& expression) -> decltype(auto) { - return (expression); - })(std::forward(expression)); - return this->dump_highest_level(e2, parametrized); + if constexpr (is_select::value) { + auto e2 = std::forward(expression); + e2.highest_level = true; + return this->dump_highest_level(e2, parametrized); + } else { + return this->dump_highest_level(expression, parametrized); + } } /** @@ -23770,6 +23724,9 @@ namespace sqlite_orm { template = true> sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); + template = true> + sync_schema_result _sync_normal_table(const Table& table, sqlite3* db, bool preserve); + template void add_column(sqlite3* db, const std::string& tableName, const C& column) const { using context_t = serializer_context; @@ -24075,27 +24032,25 @@ namespace sqlite_orm { })); }; - static_if::value>( - [&processObject](auto& expression) { + if constexpr (is_replace_range::value) { #ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED - std::ranges::for_each(expression.range.first, - expression.range.second, - std::ref(processObject), - std::ref(expression.transformer)); + std::ranges::for_each(statement.expression.range.first, + statement.expression.range.second, + std::ref(processObject), + std::ref(statement.expression.transformer)); #else - auto& transformer = expression.transformer; - std::for_each(expression.range.first, - expression.range.second, - [&processObject, &transformer](auto& item) { - const object_type& object = polyfill::invoke(transformer, item); - processObject(object); - }); + auto& transformer = statement.expression.transformer; + std::for_each(statement.expression.range.first, + statement.expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); #endif - }, - [&processObject](auto& expression) { - const object_type& o = get_object(expression); - processObject(o); - })(statement.expression); + } else { + const object_type& o = get_object(statement.expression); + processObject(o); + }; perform_step(stmt); } @@ -24120,27 +24075,25 @@ namespace sqlite_orm { })); }; - static_if::value>( - [&processObject](auto& expression) { + if constexpr (is_insert_range::value) { #ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED - std::ranges::for_each(expression.range.first, - expression.range.second, - std::ref(processObject), - std::ref(expression.transformer)); + std::ranges::for_each(statement.expression.range.first, + statement.expression.range.second, + std::ref(processObject), + std::ref(statement.expression.transformer)); #else - auto& transformer = expression.transformer; - std::for_each(expression.range.first, - expression.range.second, - [&processObject, &transformer](auto& item) { - const object_type& object = polyfill::invoke(transformer, item); - processObject(object); - }); + auto& transformer = statement.expression.transformer; + std::for_each(statement.expression.range.first, + statement.expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); #endif - }, - [&processObject](auto& expression) { - const object_type& o = get_object(expression); - processObject(o); - })(statement.expression); + } else { + const object_type& o = get_object(statement.expression); + processObject(o); + } perform_step(stmt); return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); @@ -24408,8 +24361,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #include // std::make_unique #endif -// #include "../functional/static_magic.h" - // #include "../tuple_helper/tuple_traits.h" // #include "../default_value_extractor.h" @@ -24455,12 +24406,10 @@ namespace sqlite_orm { static constexpr size_t default_op_index = find_tuple_template::value; std::unique_ptr value; - call_if_constexpr::value>( - [&value](auto& constraints) { - value = - std::make_unique(serialize_default_value(std::get(constraints))); - }, - this->constraints); + if constexpr (default_op_index != std::tuple_size::value) { + value = std::make_unique( + serialize_default_value(std::get(this->constraints))); + } return value; } @@ -24668,18 +24617,23 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { namespace sqlite_orm { namespace internal { - template template> sync_schema_result storage_t::sync_table(const Table& table, sqlite3* db, bool preserve) { - if (std::is_same, sqlite_master>::value) { - return sync_schema_result::already_in_sync; - } + if constexpr ( #ifdef SQLITE_ENABLE_DBSTAT_VTAB - if (std::is_same, dbstat>::value) { + std::is_same, dbstat>::value || +#endif + std::is_same, sqlite_master>::value) { return sync_schema_result::already_in_sync; + } else { + return this->_sync_normal_table(table, db, preserve); } -#endif // SQLITE_ENABLE_DBSTAT_VTAB + } + + template + template> + sync_schema_result storage_t::_sync_normal_table(const Table& table, sqlite3* db, bool preserve) { auto res = sync_schema_result::already_in_sync; bool attempt_to_preserve = true; @@ -24807,8 +24761,6 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" -// #include "functional/static_magic.h" - // #include "type_traits.h" // #include "prepared_statement.h" @@ -25219,16 +25171,13 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { const result_type* result = nullptr; internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { using node_type = polyfill::remove_cvref_t; - if (internal::is_bindable::value) { + if constexpr (internal::is_bindable::value) { ++index; - } - if (index == N) { - internal::call_if_constexpr::value>( - [](auto& r, auto& n) { - r = &n; - }, - result, - node); + if constexpr (std::is_same::value) { + if (index == N) { + result = &node; + } + } } }); return internal::get_ref(*result); @@ -25245,16 +25194,13 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { using node_type = polyfill::remove_cvref_t; - if (internal::is_bindable::value) { + if constexpr (internal::is_bindable::value) { ++index; - } - if (index == N) { - internal::call_if_constexpr::value>( - [](auto& r, auto& n) { - r = const_cast>(&n); - }, - result, - node); + if constexpr (std::is_same::value) { + if (index == N) { + result = const_cast(&node); + } + } } }); return internal::get_ref(*result); diff --git a/tests/schema/virtual_table.cpp b/tests/schema/virtual_table.cpp index 460cbd8cc..3ff7c2759 100644 --- a/tests/schema/virtual_table.cpp +++ b/tests/schema/virtual_table.cpp @@ -120,6 +120,7 @@ TEST_CASE("issue1410") { where(eq(&NormalTable::id, select(&SearchTable::normal_table_id, where(match("Some Text")))))); for (const auto& row: rows) { + std::ignore = row; } // has to be compiled } #endif diff --git a/tests/static_tests/functional/static_if_tests.cpp b/tests/static_tests/functional/static_if_tests.cpp deleted file mode 100644 index feacbc500..000000000 --- a/tests/static_tests/functional/static_if_tests.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include - -using namespace sqlite_orm; -using internal::call_if_constexpr; -using internal::static_if; - -TEST_CASE("static_if") { - using called_pair = std::pair; - called_pair called; - { // simple true - called = {}; - static_if( - [&called] { - ++called.first; - }, - [&called] { - ++called.second; - })(); - REQUIRE(called == called_pair{1, 0}); - } - { // simple false - called = {}; - static_if( - [&called] { - ++called.first; - }, - [&called] { - ++called.second; - })(); - REQUIRE(called == called_pair{0, 1}); - } - { // tuple is empty - called = {}; - static_if>::value>( - [&called] { - ++called.first; - }, - [&called] { - ++called.second; - })(); - REQUIRE(called == called_pair{1, 0}); - } - { // tuple is not empty - called = {}; - static_if>>>( - [&called] { - ++called.first; - }, - [&called] { - ++called.second; - })(); - REQUIRE(called == called_pair{0, 1}); - } - { - struct User { - std::string name; - }; - auto ch = check(length(&User::name) > 5); - STATIC_REQUIRE_FALSE(internal::is_column_v); - called = {}; - static_if>( - [&called] { - ++called.first; - }, - [&called] { - ++called.second; - })(); - REQUIRE(called == called_pair{0, 1}); - } - { // simple true - called = {}; - call_if_constexpr([&called] { - ++called.first; - }); - REQUIRE(called == called_pair{1, 0}); - } - { // simple false - called = {}; - call_if_constexpr([&called] { - ++called.first; - }); - REQUIRE(called == called_pair{0, 0}); - } -} diff --git a/tests/static_tests/is_bindable.cpp b/tests/static_tests/is_bindable.cpp index 4dc6ab529..71fde2e89 100644 --- a/tests/static_tests/is_bindable.cpp +++ b/tests/static_tests/is_bindable.cpp @@ -111,17 +111,5 @@ TEST_CASE("is_bindable") { { auto func = datetime("now"); STATIC_REQUIRE_FALSE(is_bindable_v); - bool trueCalled = false; - bool falseCalled = false; - auto dummy = 5; // for gcc compilation - internal::static_if>( - [&trueCalled](int&) { - trueCalled = true; - }, - [&falseCalled](int&) { - falseCalled = true; - })(dummy); - REQUIRE_FALSE(trueCalled); - REQUIRE(falseCalled); } } From b80473be120f01b6287d1f5d6906fa3e15845cf8 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 25 Mar 2025 23:13:56 +0200 Subject: [PATCH 115/170] Corrected construction of "overloaded lambda" --- tests/ast_iterator_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ast_iterator_tests.cpp b/tests/ast_iterator_tests.cpp index 78bbbdc60..3e197f8ca 100644 --- a/tests/ast_iterator_tests.cpp +++ b/tests/ast_iterator_tests.cpp @@ -33,7 +33,7 @@ TEST_CASE("ast_iterator") { typeIndexes.push_back(typeid(value)); }; #ifdef SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED - const auto nodeLambda = overloaded( + const auto nodeLambda = overloaded{ [&typeIndexes](polyfill::bool_constant, const internal::function_call& udfCall) { typeIndexes.push_back(typeid(udfCall.name)); @@ -46,7 +46,7 @@ TEST_CASE("ast_iterator") { typeIndexes.push_back(typeid(T)); }, // swallow leaf expressions - [](auto&) {}); + [](auto&) {}}; #endif SECTION("bindables") { From a07bb982075ca587bdaac2817cf5c8637f3fb2ad Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 25 Mar 2025 23:21:07 +0200 Subject: [PATCH 116/170] Enabled msvc warning about constant conditional expression --- tests/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 34ece366e..fc6fb4cac 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -69,8 +69,6 @@ if (MSVC) target_compile_options(unit_tests PUBLIC # warning-level 4 /W4 - # C4127: conditional expression is constant - /wd4127 # C4456: declaration of 'symbol' hides previous local declaration /wd4456 # C4458: declaration of 'symbol' hides class member From 54df3f05eccc7406f12d35423fa757b06534a455 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 10:42:58 +0200 Subject: [PATCH 117/170] Added a comment about multi-threading and the last insert rowid --- dev/storage.h | 8 ++++++++ include/sqlite_orm/sqlite_orm.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/dev/storage.h b/dev/storage.h index 93b238670..f575cad60 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1486,6 +1486,10 @@ namespace sqlite_orm { perform_step(stmt); } + /** + * @return The rowid of the last inserted row. + * @note The returned rowid is only meaningful in single-thread contexts. + */ template int64 execute(const prepared_statement_t>& statement) { using object_type = statement_object_type_t; @@ -1541,6 +1545,10 @@ namespace sqlite_orm { perform_step(stmt); } + /** + * @return The rowid of the last inserted row. + * @note The returned rowid is only meaningful in single-thread contexts. + */ template, is_insert_range>::value, bool> = true> int64 execute(const prepared_statement_t& statement) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 0c97b32b2..bfd232fcd 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -24110,6 +24110,10 @@ namespace sqlite_orm { perform_step(stmt); } + /** + * @return The rowid of the last inserted row. + * @note The returned rowid is only meaningful in single-thread contexts. + */ template int64 execute(const prepared_statement_t>& statement) { using object_type = statement_object_type_t; @@ -24165,6 +24169,10 @@ namespace sqlite_orm { perform_step(stmt); } + /** + * @return The rowid of the last inserted row. + * @note The returned rowid is only meaningful in single-thread contexts. + */ template, is_insert_range>::value, bool> = true> int64 execute(const prepared_statement_t& statement) { From 971ce8f3b9a280f7292564fe731afea84b90cb13 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 12:13:11 +0200 Subject: [PATCH 118/170] Treat member initialization reorder as error for msvc as well --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78b3428a4..8e5dc2034 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") -Wreorder -Werror=reorder ) +elseif(MSVC) + target_compile_options(/we5038) endif() include(ucm) From c8f2118c3876a900453b6d8ce3197104eb445862 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 12:13:38 +0200 Subject: [PATCH 119/170] appveyor: Updated vcpkg environment to 2025.03.19 --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 685b2c87e..71cef8436 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -113,7 +113,7 @@ for: install: - |- cd C:\Tools\vcpkg - git fetch --tags && git checkout 2025.02.14 + git fetch --tags && git checkout 2025.03.19 cd %APPVEYOR_BUILD_FOLDER% C:\Tools\vcpkg\bootstrap-vcpkg.bat -disableMetrics C:\Tools\vcpkg\vcpkg integrate install @@ -146,7 +146,7 @@ for: install: - |- pushd $HOME/vcpkg - git fetch --tags && git checkout 2025.02.14 + git fetch --tags && git checkout 2025.03.19 popd $HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics $HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets @@ -174,7 +174,7 @@ for: # using custom vcpkg triplets for building and linking dynamic dependent libraries install: - |- - git clone --depth 1 --branch 2025.02.14 https://github.com/microsoft/vcpkg.git $HOME/vcpkg + git clone --depth 1 --branch 2025.03.19 https://github.com/microsoft/vcpkg.git $HOME/vcpkg $HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics $HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets vcpkg install sqlite3[core,dbstat,math,json1,fts5,soundex] catch2 --overlay-triplets=vcpkg/triplets From 254d0fa554bd9851c9b657928be81ee3518ed17b Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 12:55:06 +0200 Subject: [PATCH 120/170] Attached a few more `maybe_unused` attributes --- dev/cte_column_names_collector.h | 2 +- dev/implementations/storage_definitions.h | 4 +++- dev/statement_serializer.h | 4 ++-- include/sqlite_orm/sqlite_orm.h | 10 ++++++---- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/dev/cte_column_names_collector.h b/dev/cte_column_names_collector.h index 244e304b7..37a8e1ee8 100644 --- a/dev/cte_column_names_collector.h +++ b/dev/cte_column_names_collector.h @@ -160,7 +160,7 @@ namespace sqlite_orm { // 2. override column names from cte expression constexpr size_t nExplicitColumns = std::tuple_size_v; - if constexpr (nExplicitColumns) { + if constexpr (nExplicitColumns > 0) { if (nExplicitColumns != columnNames.size()) { throw std::system_error{orm_error_code::column_not_found}; } diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index ecd7c2f88..7fd6f1d1b 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -24,7 +24,9 @@ namespace sqlite_orm { namespace internal { template template> - sync_schema_result storage_t::sync_table(const Table& table, sqlite3* db, bool preserve) { + sync_schema_result storage_t::sync_table([[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 || diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index f0a4732b4..a6890635d 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1050,7 +1050,7 @@ namespace sqlite_orm { } using columns_tuple = typename statement_type::columns_tuple; constexpr size_t columnsCount = std::tuple_size::value; - if constexpr (columnsCount) { + if constexpr (columnsCount > 0) { ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; } return ss.str(); @@ -1067,7 +1067,7 @@ namespace sqlite_orm { ss << static_cast(c); using columns_tuple = typename statement_type::columns_tuple; const size_t columnsCount = std::tuple_size::value; - if constexpr (columnsCount) { + if constexpr (columnsCount > 0) { ss << "(" << streaming_mapped_columns_expressions(c.columns, context) << ")"; } return ss.str(); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index d70b2906f..d996e1356 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -19369,7 +19369,7 @@ namespace sqlite_orm { // 2. override column names from cte expression constexpr size_t nExplicitColumns = std::tuple_size_v; - if constexpr (nExplicitColumns) { + if constexpr (nExplicitColumns > 0) { if (nExplicitColumns != columnNames.size()) { throw std::system_error{orm_error_code::column_not_found}; } @@ -20811,7 +20811,7 @@ namespace sqlite_orm { } using columns_tuple = typename statement_type::columns_tuple; constexpr size_t columnsCount = std::tuple_size::value; - if constexpr (columnsCount) { + if constexpr (columnsCount > 0) { ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; } return ss.str(); @@ -20828,7 +20828,7 @@ namespace sqlite_orm { ss << static_cast(c); using columns_tuple = typename statement_type::columns_tuple; const size_t columnsCount = std::tuple_size::value; - if constexpr (columnsCount) { + if constexpr (columnsCount > 0) { ss << "(" << streaming_mapped_columns_expressions(c.columns, context) << ")"; } return ss.str(); @@ -24627,7 +24627,9 @@ namespace sqlite_orm { namespace internal { template template> - sync_schema_result storage_t::sync_table(const Table& table, sqlite3* db, bool preserve) { + sync_schema_result storage_t::sync_table([[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 || From 59d4e8a0391638164ad741f9bd2c74a0271ff2fd Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 13:01:13 +0200 Subject: [PATCH 121/170] appveyor: Replaced C++14 build jobs by C++23 --- appveyor.yml | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 71cef8436..18e25e96a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,27 +25,21 @@ environment: platform: x64 SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" - - job_name: clang, C++14 + - job_name: clang, C++23 appveyor_build_worker_image: Ubuntu CC: clang CXX: clang++ - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_14=ON" + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" cmake_build_parallel: "" - - job_name: gcc, C++14 + - job_name: gcc, C++23 appveyor_build_worker_image: Ubuntu CC: gcc CXX: g++ - SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_14=ON" + SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" # gcc was stuck with a parallel build cmake_build_parallel: "" - # Representative for C++14 - - job_name: Visual Studio 2017, x64, C++14 - appveyor_build_worker_image: Visual Studio 2017 - platform: x64 - SQLITE_ORM_CXX_STANDARD: "" - - job_name: clang, C++17 appveyor_build_worker_image: Ubuntu CC: clang @@ -77,8 +71,9 @@ environment: SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_20=ON" cmake_build_parallel: "" - - job_name: Visual Studio 2022, x64, C++17 - appveyor_build_worker_image: Visual Studio 2022 + # Representative for C++17 + - job_name: Visual Studio 2019, x64, C++17 + appveyor_build_worker_image: Visual Studio 2019 platform: x64 SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" @@ -100,7 +95,7 @@ for: # Windows matrix: only: - - appveyor_build_worker_image: Visual Studio 2017 + - appveyor_build_worker_image: Visual Studio 2019 - appveyor_build_worker_image: Visual Studio 2022 init: - |- From 1de4c41daa25c177756afd51d1436f10912fb4a5 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 15:59:02 +0200 Subject: [PATCH 122/170] Fixed CMake target compile options for msvc --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e5dc2034..9fb4d5f9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,9 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") -Werror=reorder ) elseif(MSVC) - target_compile_options(/we5038) + target_compile_options(sqlite_orm INTERFACE + /we5038 + ) endif() include(ucm) From 29cdd6dc3f572a8ebc31fa37c6593175d93ac867 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 16:16:42 +0200 Subject: [PATCH 123/170] Require C++ fold expression support --- dev/functional/config.h | 2 - dev/functional/cxx_check_prerequisites.h | 8 +-- dev/functional/cxx_core_features.h | 4 -- dev/functional/index_sequence_util.h | 12 +--- dev/statement_binder.h | 8 --- dev/storage.h | 6 +- dev/tuple_helper/tuple_iteration.h | 24 +------ dev/tuple_helper/tuple_transformer.h | 3 - include/sqlite_orm/sqlite_orm.h | 70 ++----------------- .../functional/index_sequence_util.cpp | 2 - .../functional/tuple_transform.cpp | 8 +-- .../static_tests_storage_traits.h | 8 --- tests/static_tests/table_static_tests.cpp | 24 ------- 13 files changed, 15 insertions(+), 164 deletions(-) diff --git a/dev/functional/config.h b/dev/functional/config.h index 98c97ceff..3dd2e0329 100644 --- a/dev/functional/config.h +++ b/dev/functional/config.h @@ -76,9 +76,7 @@ #define SQLITE_ORM_WITH_CPP20_ALIASES #endif -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) #define SQLITE_ORM_WITH_CTE -#endif // define the inline namespace "literals" so that it is available even if it was not introduced by a feature namespace sqlite_orm { diff --git a/dev/functional/cxx_check_prerequisites.h b/dev/functional/cxx_check_prerequisites.h index c3f21922f..38c7f90cb 100644 --- a/dev/functional/cxx_check_prerequisites.h +++ b/dev/functional/cxx_check_prerequisites.h @@ -4,14 +4,10 @@ * This header detects missing core C++ language features on which sqlite_orm depends, bailing out with a hard error. */ -#if __cpp_aggregate_nsdmi < 201304L +#if __cpp_aggregate_nsdmi < 201304L || __cpp_constexpr < 201304L #error A fully C++17-compliant compiler is required. #endif -#if __cpp_constexpr < 201304L -#error A fully C++17-compliant compiler is required. -#endif - -#if __cpp_if_constexpr < 201606L +#if __cpp_if_constexpr < 201606L || __cpp_fold_expressions < 201603L #error A fully C++17-compliant compiler is required. #endif diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index 552564536..086672393 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -25,10 +25,6 @@ #define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED #endif -#if __cpp_fold_expressions >= 201603L -#define SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED -#endif - #if __cpp_constexpr >= 201603L #define SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED #endif diff --git a/dev/functional/index_sequence_util.h b/dev/functional/index_sequence_util.h index dc2d2da03..465441398 100644 --- a/dev/functional/index_sequence_util.h +++ b/dev/functional/index_sequence_util.h @@ -14,7 +14,7 @@ namespace sqlite_orm { SQLITE_ORM_CONSTEVAL auto index_sequence_value_at(std::index_sequence) { return Idx...[Pos]; } -#elif defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) +#else /** * Get the index value of an `index_sequence` at a specific position. */ @@ -31,16 +31,6 @@ namespace sqlite_orm { (void)((result = Idx, i++ == Pos) || ...); return result; } -#else - /** - * Get the index value of an `index_sequence` at a specific position. - * `Pos` must always be `0`. - */ - template - SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { - static_assert(Pos == 0, ""); - return I; - } #endif template diff --git a/dev/statement_binder.h b/dev/statement_binder.h index 639a1beb2..338c4b6eb 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -337,18 +337,10 @@ namespace sqlite_orm { } private: -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED template void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); } -#else - template - void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { - using Sink = int[sizeof...(Idx)]; - (void)Sink{(this->bind(polyfill::invoke(project, std::get(tpl)), Idx), 0)...}; - } -#endif template void bind(const T& t, size_t idx) const { diff --git a/dev/storage.h b/dev/storage.h index 6cf1a74dd..215695d86 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -258,9 +258,8 @@ namespace sqlite_orm { template void assert_updatable_type() const { -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) - using Table = storage_pick_table_t; - using elements_type = elements_type_t

; + using table_type = storage_pick_table_t; + using elements_type = elements_type_t; using col_index_sequence = filter_tuple_sequence_t; using pk_index_sequence = filter_tuple_sequence_t; using pkcol_index_sequence = col_index_sequence_with; @@ -274,7 +273,6 @@ namespace sqlite_orm { static_assert( nonPrimaryKeysColumnsCount > 0, "A table with only primary keys cannot be updated. You need at least 1 non-primary key column"); -#endif } template constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr (reversed) { @@ -20,21 +19,7 @@ namespace sqlite_orm { (lambda(std::get(tpl)), ...); } } -#else - template - void iterate_tuple(Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} - template - void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { - if constexpr (reversed) { - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - lambda(std::get(tpl)); - } else { - lambda(std::get(tpl)); - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - } - } -#endif template constexpr void iterate_tuple(Tpl&& tpl, L&& lambda) { iterate_tuple(tpl, @@ -42,18 +27,11 @@ namespace sqlite_orm { std::forward(lambda)); } -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED template void iterate_tuple(std::index_sequence, L&& lambda) { (lambda((std::tuple_element_t*)nullptr), ...); } -#else - template - void iterate_tuple(std::index_sequence, L&& lambda) { - using Sink = int[sizeof...(Idx)]; - (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; - } -#endif + template void iterate_tuple(L&& lambda) { iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index 57fef52d5..994688597 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -28,8 +28,6 @@ namespace sqlite_orm { template class Op> using transform_tuple_t = typename tuple_transformer::type; - // note: applying a combiner like `plus_fold_integrals` needs fold expressions -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) /* * Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results. * @@ -95,7 +93,6 @@ namespace sqlite_orm { IdxSeq{}, project_nested_tuple_size{}, std::integral_constant{})); -#endif template constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index d996e1356..f1c30bc48 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -39,15 +39,11 @@ using std::nullptr_t; * This header detects missing core C++ language features on which sqlite_orm depends, bailing out with a hard error. */ -#if __cpp_aggregate_nsdmi < 201304L +#if __cpp_aggregate_nsdmi < 201304L || __cpp_constexpr < 201304L #error A fully C++17-compliant compiler is required. #endif -#if __cpp_constexpr < 201304L -#error A fully C++17-compliant compiler is required. -#endif - -#if __cpp_if_constexpr < 201606L +#if __cpp_if_constexpr < 201606L || __cpp_fold_expressions < 201603L #error A fully C++17-compliant compiler is required. #endif @@ -78,10 +74,6 @@ using std::nullptr_t; #define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED #endif -#if __cpp_fold_expressions >= 201603L -#define SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED -#endif - #if __cpp_constexpr >= 201603L #define SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED #endif @@ -282,9 +274,7 @@ using std::nullptr_t; #define SQLITE_ORM_WITH_CPP20_ALIASES #endif -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) #define SQLITE_ORM_WITH_CTE -#endif // define the inline namespace "literals" so that it is available even if it was not introduced by a feature namespace sqlite_orm { @@ -1355,7 +1345,7 @@ namespace sqlite_orm { SQLITE_ORM_CONSTEVAL auto index_sequence_value_at(std::index_sequence) { return Idx...[Pos]; } -#elif defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) +#else /** * Get the index value of an `index_sequence` at a specific position. @@ -1374,19 +1364,6 @@ namespace sqlite_orm { (void)((result = Idx, i++ == Pos) || ...); return result; } -#else - /** - - * Get the index value of an `index_sequence` at a specific position. - - * `Pos` must always be `0`. - - */ - template - SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { - static_assert(Pos == 0, ""); - return I; - } #endif template @@ -1527,8 +1504,6 @@ namespace sqlite_orm { template class Op> using transform_tuple_t = typename tuple_transformer::type; - // note: applying a combiner like `plus_fold_integrals` needs fold expressions -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) /* * Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results. * @@ -1594,7 +1569,6 @@ namespace sqlite_orm { IdxSeq{}, project_nested_tuple_size{}, std::integral_constant{})); -#endif template constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { @@ -1642,7 +1616,6 @@ namespace sqlite_orm { namespace sqlite_orm { namespace internal { -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) template constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr (reversed) { @@ -1654,21 +1627,7 @@ namespace sqlite_orm { (lambda(std::get(tpl)), ...); } } -#else - template - void iterate_tuple(Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} - template - void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { - if constexpr (reversed) { - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - lambda(std::get(tpl)); - } else { - lambda(std::get(tpl)); - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - } - } -#endif template constexpr void iterate_tuple(Tpl&& tpl, L&& lambda) { iterate_tuple(tpl, @@ -1676,18 +1635,11 @@ namespace sqlite_orm { std::forward(lambda)); } -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED template void iterate_tuple(std::index_sequence, L&& lambda) { (lambda((std::tuple_element_t*)nullptr), ...); } -#else - template - void iterate_tuple(std::index_sequence, L&& lambda) { - using Sink = int[sizeof...(Idx)]; - (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; - } -#endif + template void iterate_tuple(L&& lambda) { iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); @@ -10443,18 +10395,10 @@ namespace sqlite_orm { } private: -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED template void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); } -#else - template - void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { - using Sink = int[sizeof...(Idx)]; - (void)Sink{(this->bind(polyfill::invoke(project, std::get(tpl)), Idx), 0)...}; - } -#endif template void bind(const T& t, size_t idx) const { @@ -22774,9 +22718,8 @@ namespace sqlite_orm { template void assert_updatable_type() const { -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) - using Table = storage_pick_table_t; - using elements_type = elements_type_t
; + using table_type = storage_pick_table_t; + using elements_type = elements_type_t; using col_index_sequence = filter_tuple_sequence_t; using pk_index_sequence = filter_tuple_sequence_t; using pkcol_index_sequence = col_index_sequence_with; @@ -22790,7 +22733,6 @@ namespace sqlite_orm { static_assert( nonPrimaryKeysColumnsCount > 0, "A table with only primary keys cannot be updated. You need at least 1 non-primary key column"); -#endif } template(index_sequence<1, 3>{}) == 1); -#if defined(SQLITE_ORM_PACK_INDEXING_SUPPORTED) || defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(index_sequence_value_at<1>(index_sequence<1, 3>{}) == 3); -#endif } TEST_CASE("flatten index sequence") { diff --git a/tests/static_tests/functional/tuple_transform.cpp b/tests/static_tests/functional/tuple_transform.cpp index 26bcab109..5fab693e5 100644 --- a/tests/static_tests/functional/tuple_transform.cpp +++ b/tests/static_tests/functional/tuple_transform.cpp @@ -7,16 +7,14 @@ using namespace sqlite_orm; using internal::create_from_tuple; using internal::field_type_t; using internal::literal_holder; -using internal::transform_tuple_t; -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) using internal::nested_tuple_size_for_t; using internal::recombine_tuple; -#endif +using internal::transform_tuple_t; template using make_literal_holder = literal_holder; -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && (__cpp_lib_constexpr_functional >= 201907L) +#if __cpp_lib_constexpr_functional >= 201907L struct tuple_maker { template constexpr auto operator()(Types&&... types) const { @@ -52,7 +50,7 @@ TEST_CASE("tuple_helper static") { STATIC_REQUIRE(create_from_tuple(std::make_tuple(1, 2), polyfill::identity{}) == std::tuple{1, 2}); #endif -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && (__cpp_lib_constexpr_functional >= 201907L) +#if __cpp_lib_constexpr_functional >= 201907L STATIC_REQUIRE(recombine_tuple(tuple_maker{}, std::make_tuple(1, 2), polyfill::identity{}, 3) == std::make_tuple(3, 1, 2)); diff --git a/tests/static_tests/static_tests_storage_traits.h b/tests/static_tests/static_tests_storage_traits.h index 453cf2955..eef647791 100644 --- a/tests/static_tests/static_tests_storage_traits.h +++ b/tests/static_tests/static_tests_storage_traits.h @@ -51,18 +51,10 @@ namespace sqlite_orm { template struct storage_foreign_keys_count_impl : std::integral_constant {}; -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED template struct storage_foreign_keys_count_impl, Lookup> { static constexpr int value = (table_foreign_keys_count::value + ...); }; -#else - template - struct storage_foreign_keys_count_impl, Lookup> { - static constexpr int value = table_foreign_keys_count::value + - storage_foreign_keys_count_impl, Lookup>::value; - }; -#endif /** * S - storage class diff --git a/tests/static_tests/table_static_tests.cpp b/tests/static_tests/table_static_tests.cpp index ff782f178..7020a7969 100644 --- a/tests/static_tests/table_static_tests.cpp +++ b/tests/static_tests/table_static_tests.cpp @@ -5,13 +5,11 @@ using namespace sqlite_orm; using internal::is_column; using internal::is_primary_key; -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) template using dedicated_pk_columns_count_t = internal::nested_tuple_size_for_t>; -#endif TEST_CASE("table static count_of()") { struct User { @@ -23,72 +21,56 @@ TEST_CASE("table static count_of()") { STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 1 column with 1 inline pk auto table = make_table("users", make_column("id", &User::id, primary_key())); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 1 column with 1 inline pk autoincrement auto table = make_table("users", make_column("id", &User::id, primary_key().autoincrement())); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 1 column with 1 dedicated pk auto table = make_table("users", make_column("id", &User::id), primary_key(&User::id)); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); -#endif } { // 1 column with 1 dedicated pk autoincrement auto table = make_table("users", make_column("id", &User::id), primary_key(&User::id).autoincrement()); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); -#endif } { // 2 columns no pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name)); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 2 columns with 1 inline id pk auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("id", &User::name)); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 2 columns with 1 inline name pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name, primary_key())); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); -#endif } { // 2 columns with 1 dedicated id pk auto table = @@ -96,9 +78,7 @@ TEST_CASE("table static count_of()") { STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); -#endif } { // 2 columns with 1 dedicated name pk auto table = @@ -106,9 +86,7 @@ TEST_CASE("table static count_of()") { STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); -#endif } { // 2 columns with 2 dedicated pks auto table = make_table("users", @@ -118,8 +96,6 @@ TEST_CASE("table static count_of()") { STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 2); -#endif } } From 0d2584799d140a931506d0e8aada47c2ad41fdfa Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 16:33:10 +0200 Subject: [PATCH 124/170] Required C++ exception specifications be part of the type system --- dev/functional/cxx_check_prerequisites.h | 2 +- dev/functional/cxx_core_features.h | 4 ---- dev/functional/function_traits.h | 2 -- dev/member_traits/member_traits.h | 4 ---- include/sqlite_orm/sqlite_orm.h | 12 +----------- tests/static_tests/member_traits_tests.cpp | 22 ++++++++++------------ 6 files changed, 12 insertions(+), 34 deletions(-) diff --git a/dev/functional/cxx_check_prerequisites.h b/dev/functional/cxx_check_prerequisites.h index 38c7f90cb..437b04b40 100644 --- a/dev/functional/cxx_check_prerequisites.h +++ b/dev/functional/cxx_check_prerequisites.h @@ -8,6 +8,6 @@ #error A fully C++17-compliant compiler is required. #endif -#if __cpp_if_constexpr < 201606L || __cpp_fold_expressions < 201603L +#if __cpp_noexcept_function_type < 201510L || __cpp_fold_expressions < 201603L || __cpp_if_constexpr < 201606L #error A fully C++17-compliant compiler is required. #endif diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index 086672393..79aba84e6 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -17,10 +17,6 @@ #define SQLITE_ORM_HAS_INCLUDE(file) 0L #endif -#if __cpp_noexcept_function_type >= 201510L -#define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED -#endif - #if __cpp_aggregate_bases >= 201603L #define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED #endif diff --git a/dev/functional/function_traits.h b/dev/functional/function_traits.h index c814eaf25..4a44d3978 100644 --- a/dev/functional/function_traits.h +++ b/dev/functional/function_traits.h @@ -51,7 +51,6 @@ namespace sqlite_orm { using signature_type = R(Args...) const; }; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct function_traits : function_traits { using signature_type = R(Args...) noexcept; @@ -61,7 +60,6 @@ namespace sqlite_orm { struct function_traits : function_traits { using signature_type = R(Args...) const noexcept; }; -#endif /* * Pick signature of function pointer diff --git a/dev/member_traits/member_traits.h b/dev/member_traits/member_traits.h index e130c21ee..a2b90baef 100644 --- a/dev/member_traits/member_traits.h +++ b/dev/member_traits/member_traits.h @@ -34,13 +34,11 @@ namespace sqlite_orm { template struct getter_field_type : polyfill::remove_cvref {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct getter_field_type : polyfill::remove_cvref {}; template struct getter_field_type : polyfill::remove_cvref {}; -#endif // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) template @@ -55,10 +53,8 @@ namespace sqlite_orm { template struct setter_field_type : polyfill::remove_cvref {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct setter_field_type : polyfill::remove_cvref {}; -#endif template struct is_getter : std::false_type {}; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index f1c30bc48..6f54dcf28 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -43,7 +43,7 @@ using std::nullptr_t; #error A fully C++17-compliant compiler is required. #endif -#if __cpp_if_constexpr < 201606L || __cpp_fold_expressions < 201603L +#if __cpp_noexcept_function_type < 201510L || __cpp_fold_expressions < 201603L || __cpp_if_constexpr < 201606L #error A fully C++17-compliant compiler is required. #endif @@ -66,10 +66,6 @@ using std::nullptr_t; #define SQLITE_ORM_HAS_INCLUDE(file) 0L #endif -#if __cpp_noexcept_function_type >= 201510L -#define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED -#endif - #if __cpp_aggregate_bases >= 201603L #define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED #endif @@ -554,13 +550,11 @@ namespace sqlite_orm { template struct getter_field_type : polyfill::remove_cvref {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct getter_field_type : polyfill::remove_cvref {}; template struct getter_field_type : polyfill::remove_cvref {}; -#endif // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) template @@ -575,10 +569,8 @@ namespace sqlite_orm { template struct setter_field_type : polyfill::remove_cvref {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct setter_field_type : polyfill::remove_cvref {}; -#endif template struct is_getter : std::false_type {}; @@ -10914,7 +10906,6 @@ namespace sqlite_orm { using signature_type = R(Args...) const; }; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED template struct function_traits : function_traits { using signature_type = R(Args...) noexcept; @@ -10924,7 +10915,6 @@ namespace sqlite_orm { struct function_traits : function_traits { using signature_type = R(Args...) const noexcept; }; -#endif /* * Pick signature of function pointer diff --git a/tests/static_tests/member_traits_tests.cpp b/tests/static_tests/member_traits_tests.cpp index bbcf9dc2f..3062d9850 100644 --- a/tests/static_tests/member_traits_tests.cpp +++ b/tests/static_tests/member_traits_tests.cpp @@ -115,14 +115,14 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); STATIC_REQUIRE(is_getter::value); -#endif + STATIC_REQUIRE_FALSE(is_getter::value); STATIC_REQUIRE_FALSE(is_getter::value); STATIC_REQUIRE_FALSE(is_getter::value); @@ -145,11 +145,10 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_setter::value); STATIC_REQUIRE(is_setter::value); STATIC_REQUIRE(is_setter::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_setter::value); STATIC_REQUIRE(is_setter::value); STATIC_REQUIRE(is_setter::value); -#endif STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); @@ -170,7 +169,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); @@ -194,7 +193,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); -#endif + STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); @@ -206,7 +205,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); @@ -218,7 +217,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); -#endif + STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); @@ -246,7 +245,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); @@ -270,7 +269,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); -#endif + STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); @@ -282,7 +281,7 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same, User>::value); STATIC_REQUIRE(is_same, int>::value); STATIC_REQUIRE(is_same, int>::value); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); @@ -294,7 +293,6 @@ TEST_CASE("member_traits_tests") { STATIC_REQUIRE(is_same_v, User>); STATIC_REQUIRE(is_same_v, int>); STATIC_REQUIRE(is_same_v, int>); -#endif STATIC_REQUIRE(is_same(&User::id))>, DerivedUser>::value); STATIC_REQUIRE(is_same>, DerivedUser>::value); From 9628b937798852ed3786fd48f166a1471bbcb583 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 16:40:36 +0200 Subject: [PATCH 125/170] Required C++ Initialization of aggregate class with base classes --- dev/constraints.h | 3 -- dev/functional/cxx_check_prerequisites.h | 3 +- dev/functional/cxx_core_features.h | 4 --- dev/schema/column.h | 8 +---- dev/schema/index.h | 5 --- dev/schema/table.h | 10 ------ dev/schema/triggers.h | 5 --- dev/tuple_helper/tuple_iteration.h | 3 -- include/sqlite_orm/sqlite_orm.h | 41 ++---------------------- 9 files changed, 6 insertions(+), 76 deletions(-) diff --git a/dev/constraints.h b/dev/constraints.h index acfe247b2..50c09794a 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -53,9 +53,6 @@ namespace sqlite_orm { const primary_key_type& as_base() const { return *this; } -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - constexpr primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} -#endif }; /** diff --git a/dev/functional/cxx_check_prerequisites.h b/dev/functional/cxx_check_prerequisites.h index 437b04b40..07bce74be 100644 --- a/dev/functional/cxx_check_prerequisites.h +++ b/dev/functional/cxx_check_prerequisites.h @@ -8,6 +8,7 @@ #error A fully C++17-compliant compiler is required. #endif -#if __cpp_noexcept_function_type < 201510L || __cpp_fold_expressions < 201603L || __cpp_if_constexpr < 201606L +#if (__cpp_noexcept_function_type < 201510L) || \ + (__cpp_fold_expressions < 201603L || __cpp_aggregate_bases < 201603L) || (__cpp_if_constexpr < 201606L) #error A fully C++17-compliant compiler is required. #endif diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index 79aba84e6..f83042d91 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -17,10 +17,6 @@ #define SQLITE_ORM_HAS_INCLUDE(file) 0L #endif -#if __cpp_aggregate_bases >= 201603L -#define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED -#endif - #if __cpp_constexpr >= 201603L #define SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED #endif diff --git a/dev/schema/column.h b/dev/schema/column.h index 4a4f8470b..ed567bee0 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -97,13 +97,7 @@ namespace sqlite_orm { * It is a composition of orthogonal information stored in different base classes. */ template - struct column_t : column_identifier, column_field, column_constraints { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - column_t(std::string name, G memberPointer, S setter, std::tuple op) : - column_identifier{std::move(name)}, column_field{memberPointer, setter}, - column_constraints{std::move(op)} {} -#endif - }; + struct column_t : column_identifier, column_field, column_constraints {}; template struct column_field_expression { diff --git a/dev/schema/index.h b/dev/schema/index.h index 770ef402f..b6bc92710 100644 --- a/dev/schema/index.h +++ b/dev/schema/index.h @@ -25,11 +25,6 @@ namespace sqlite_orm { using object_type = void; using table_mapped_type = T; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - index_t(std::string name_, bool unique_, elements_type elements_) : - index_base{std::move(name_), unique_}, elements(std::move(elements_)) {} -#endif - elements_type elements; }; } diff --git a/dev/schema/table.h b/dev/schema/table.h index 7680e029a..35194f8e4 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -87,11 +87,6 @@ namespace sqlite_orm { elements_type elements; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - table_t(std::string name_, elements_type elements_) : - basic_table{std::move(name_)}, elements{std::move(elements_)} {} -#endif - table_t without_rowid() const { return {this->name, this->elements}; } @@ -313,11 +308,6 @@ namespace sqlite_orm { module_details_type module_details; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - virtual_table_t(std::string name, module_details_type module_details) : - basic_table{std::move(name)}, module_details{std::move(module_details)} {} -#endif - /** * Call passed lambda with columns not having the specified constraint trait `OpTrait`. * @param lambda Lambda called for each column. diff --git a/dev/schema/triggers.h b/dev/schema/triggers.h index 2873116cf..2e6ecebd9 100644 --- a/dev/schema/triggers.h +++ b/dev/schema/triggers.h @@ -72,11 +72,6 @@ namespace sqlite_orm { * Statements of the triggers (to be executed when the trigger fires) */ elements_type elements; - -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - trigger_t(std::string name, T trigger_base, elements_type statements) : - base_trigger{std::move(name)}, base(std::move(trigger_base)), elements(std::move(statements)) {} -#endif }; /** diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index 7e9c198a1..f8a72f1ce 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -39,9 +39,6 @@ namespace sqlite_orm { template class Base, class L> struct lambda_as_template_base : L { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} -#endif template decltype(auto) operator()(const Base& object) { return L::operator()(object); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 6f54dcf28..70ba8dda3 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -43,7 +43,8 @@ using std::nullptr_t; #error A fully C++17-compliant compiler is required. #endif -#if __cpp_noexcept_function_type < 201510L || __cpp_fold_expressions < 201603L || __cpp_if_constexpr < 201606L +#if (__cpp_noexcept_function_type < 201510L) || \ + (__cpp_fold_expressions < 201603L || __cpp_aggregate_bases < 201603L) || (__cpp_if_constexpr < 201606L) #error A fully C++17-compliant compiler is required. #endif @@ -66,10 +67,6 @@ using std::nullptr_t; #define SQLITE_ORM_HAS_INCLUDE(file) 0L #endif -#if __cpp_aggregate_bases >= 201603L -#define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED -#endif - #if __cpp_constexpr >= 201603L #define SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED #endif @@ -1639,9 +1636,6 @@ namespace sqlite_orm { template class Base, class L> struct lambda_as_template_base : L { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} -#endif template decltype(auto) operator()(const Base& object) { return L::operator()(object); @@ -3252,9 +3246,6 @@ namespace sqlite_orm { const primary_key_type& as_base() const { return *this; } -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - constexpr primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} -#endif }; /** @@ -8593,13 +8584,7 @@ namespace sqlite_orm { * It is a composition of orthogonal information stored in different base classes. */ template - struct column_t : column_identifier, column_field, column_constraints { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - column_t(std::string name, G memberPointer, S setter, std::tuple op) : - column_identifier{std::move(name)}, column_field{memberPointer, setter}, - column_constraints{std::move(op)} {} -#endif - }; + struct column_t : column_identifier, column_field, column_constraints {}; template struct column_field_expression { @@ -12091,11 +12076,6 @@ namespace sqlite_orm { using object_type = void; using table_mapped_type = T; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - index_t(std::string name_, bool unique_, elements_type elements_) : - index_base{std::move(name_), unique_}, elements(std::move(elements_)) {} -#endif - elements_type elements; }; } @@ -12199,11 +12179,6 @@ namespace sqlite_orm { elements_type elements; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - table_t(std::string name_, elements_type elements_) : - basic_table{std::move(name_)}, elements{std::move(elements_)} {} -#endif - table_t without_rowid() const { return {this->name, this->elements}; } @@ -12425,11 +12400,6 @@ namespace sqlite_orm { module_details_type module_details; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - virtual_table_t(std::string name, module_details_type module_details) : - basic_table{std::move(name)}, module_details{std::move(module_details)} {} -#endif - /** * Call passed lambda with columns not having the specified constraint trait `OpTrait`. * @param lambda Lambda called for each column. @@ -19530,11 +19500,6 @@ namespace sqlite_orm { * Statements of the triggers (to be executed when the trigger fires) */ elements_type elements; - -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - trigger_t(std::string name, T trigger_base, elements_type statements) : - base_trigger{std::move(name)}, base(std::move(trigger_base)), elements(std::move(statements)) {} -#endif }; /** From 0f6148b2f50d00c7c60dbdbe10017e7fd5c35dc4 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 17:26:53 +0200 Subject: [PATCH 126/170] appveyor: Corrected CMake generator for Visual Studio 2019 worker image --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 18e25e96a..87e3b2ce6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -104,7 +104,7 @@ for: if "%platform%"=="x64" (set architecture=-A x64) if "%platform%"=="x86" (set architecture=-A Win32) if "%appveyor_build_worker_image%"=="Visual Studio 2022" (set generator="Visual Studio 17 2022" %architecture%) - if "%appveyor_build_worker_image%"=="Visual Studio 2017" (set generator="Visual Studio 15 2017" %architecture%) + if "%appveyor_build_worker_image%"=="Visual Studio 2019" (set generator="Visual Studio 16 2019" %architecture%) install: - |- cd C:\Tools\vcpkg From d21f8084ef17111528ec9ef85537e5c782e696a9 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 17:31:15 +0200 Subject: [PATCH 127/170] appveyor: Updated Ubuntu build worker image for C++23 builds --- appveyor.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 87e3b2ce6..38097d81c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,14 +26,14 @@ environment: SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" - job_name: clang, C++23 - appveyor_build_worker_image: Ubuntu + appveyor_build_worker_image: Ubuntu2204 CC: clang CXX: clang++ SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" cmake_build_parallel: "" - job_name: gcc, C++23 - appveyor_build_worker_image: Ubuntu + appveyor_build_worker_image: Ubuntu2204 CC: gcc CXX: g++ SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" @@ -132,6 +132,7 @@ for: matrix: only: - appveyor_build_worker_image: Ubuntu + - appveyor_build_worker_image: Ubuntu2204 init: - |- echo $appveyor_build_worker_image From 239412d432b8d1afca96a175b8c14742ce8dd6cf Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 17:23:11 +0200 Subject: [PATCH 128/170] Required C++ Constexpr lambdas --- dev/functional/config.h | 6 ------ dev/functional/cxx_check_prerequisites.h | 3 ++- dev/functional/cxx_core_features.h | 4 ---- dev/functional/cxx_functional_polyfill.h | 2 +- dev/udf_proxy.h | 6 +++--- include/sqlite_orm/sqlite_orm.h | 21 ++++++--------------- 6 files changed, 12 insertions(+), 30 deletions(-) diff --git a/dev/functional/config.h b/dev/functional/config.h index 3dd2e0329..295bb7465 100644 --- a/dev/functional/config.h +++ b/dev/functional/config.h @@ -12,12 +12,6 @@ #include #endif -#ifdef SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED -#define SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 constexpr -#else -#define SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 -#endif - #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED #define SQLITE_ORM_INLINE_VAR inline #else diff --git a/dev/functional/cxx_check_prerequisites.h b/dev/functional/cxx_check_prerequisites.h index 07bce74be..8dd9d0928 100644 --- a/dev/functional/cxx_check_prerequisites.h +++ b/dev/functional/cxx_check_prerequisites.h @@ -9,6 +9,7 @@ #endif #if (__cpp_noexcept_function_type < 201510L) || \ - (__cpp_fold_expressions < 201603L || __cpp_aggregate_bases < 201603L) || (__cpp_if_constexpr < 201606L) + (__cpp_fold_expressions < 201603L || __cpp_constexpr < 201603L || __cpp_aggregate_bases < 201603L) || \ + (__cpp_if_constexpr < 201606L) #error A fully C++17-compliant compiler is required. #endif diff --git a/dev/functional/cxx_core_features.h b/dev/functional/cxx_core_features.h index f83042d91..1afdc17a6 100644 --- a/dev/functional/cxx_core_features.h +++ b/dev/functional/cxx_core_features.h @@ -17,10 +17,6 @@ #define SQLITE_ORM_HAS_INCLUDE(file) 0L #endif -#if __cpp_constexpr >= 201603L -#define SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED -#endif - #if __cpp_range_based_for >= 201603L #define SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED #endif diff --git a/dev/functional/cxx_functional_polyfill.h b/dev/functional/cxx_functional_polyfill.h index f575fa24c..ee4bf09fc 100644 --- a/dev/functional/cxx_functional_polyfill.h +++ b/dev/functional/cxx_functional_polyfill.h @@ -85,7 +85,7 @@ namespace sqlite_orm { template struct is_invocable_impl : std::false_type {}; -#if __cplusplus >= 201703 +#if __cplusplus >= 201703L template struct is_invocable_impl()...))>, Ts...> : std::true_type {}; diff --git a/dev/udf_proxy.h b/dev/udf_proxy.h index 73710ee21..ce82b0b13 100644 --- a/dev/udf_proxy.h +++ b/dev/udf_proxy.h @@ -24,7 +24,7 @@ namespace sqlite_orm { std::allocator allocator; using traits = std::allocator_traits; - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto deallocate = [](void* location) noexcept { + constexpr auto deallocate = [](void* location) noexcept { std::allocator allocator; using traits = std::allocator_traits; traits::deallocate(allocator, (UDF*)location, 1); @@ -38,13 +38,13 @@ namespace sqlite_orm { */ template std::pair obtain_udf_allocator() { - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto allocate = []() { + constexpr auto allocate = []() { std::allocator allocator; using traits = std::allocator_traits; return (void*)traits::allocate(allocator, 1); }; - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto deallocate = [](void* location) noexcept { + constexpr auto deallocate = [](void* location) noexcept { std::allocator allocator; using traits = std::allocator_traits; traits::deallocate(allocator, (UDF*)location, 1); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 70ba8dda3..dcc1b1940 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -44,7 +44,8 @@ using std::nullptr_t; #endif #if (__cpp_noexcept_function_type < 201510L) || \ - (__cpp_fold_expressions < 201603L || __cpp_aggregate_bases < 201603L) || (__cpp_if_constexpr < 201606L) + (__cpp_fold_expressions < 201603L || __cpp_constexpr < 201603L || __cpp_aggregate_bases < 201603L) || \ + (__cpp_if_constexpr < 201606L) #error A fully C++17-compliant compiler is required. #endif @@ -67,10 +68,6 @@ using std::nullptr_t; #define SQLITE_ORM_HAS_INCLUDE(file) 0L #endif -#if __cpp_constexpr >= 201603L -#define SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED -#endif - #if __cpp_range_based_for >= 201603L #define SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED #endif @@ -203,12 +200,6 @@ using std::nullptr_t; #include #endif -#ifdef SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED -#define SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 constexpr -#else -#define SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 -#endif - #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED #define SQLITE_ORM_INLINE_VAR inline #else @@ -690,7 +681,7 @@ namespace sqlite_orm { template struct is_invocable_impl : std::false_type {}; -#if __cplusplus >= 201703 +#if __cplusplus >= 201703L template struct is_invocable_impl()...))>, Ts...> : std::true_type {}; @@ -17482,7 +17473,7 @@ namespace sqlite_orm { std::allocator allocator; using traits = std::allocator_traits; - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto deallocate = [](void* location) noexcept { + constexpr auto deallocate = [](void* location) noexcept { std::allocator allocator; using traits = std::allocator_traits; traits::deallocate(allocator, (UDF*)location, 1); @@ -17496,13 +17487,13 @@ namespace sqlite_orm { */ template std::pair obtain_udf_allocator() { - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto allocate = []() { + constexpr auto allocate = []() { std::allocator allocator; using traits = std::allocator_traits; return (void*)traits::allocate(allocator, 1); }; - SQLITE_ORM_CONSTEXPR_LAMBDA_CPP17 auto deallocate = [](void* location) noexcept { + constexpr auto deallocate = [](void* location) noexcept { std::allocator allocator; using traits = std::allocator_traits; traits::deallocate(allocator, (UDF*)location, 1); From a76b878942db29b81cff11db42d27e661c29d9ff Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 18:20:46 +0200 Subject: [PATCH 129/170] appveyor: Try to use Clang-18 for C++23 builds --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 38097d81c..6dd0e683a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,8 +27,8 @@ environment: - job_name: clang, C++23 appveyor_build_worker_image: Ubuntu2204 - CC: clang - CXX: clang++ + CC: clang-18 + CXX: clang++-18 SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_23=ON" cmake_build_parallel: "" From cc73afca9101f873cfa9d514dea2a7be960a17eb Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 22:28:01 +0200 Subject: [PATCH 130/170] Corrected concepts used in unit tests. * A concept cannot have an associated constraint, i.e. it cannot constrain a parameter using a concept recursively. * dependent template names (e.g. methods with explicit template parameters) must be qualified with the `template` keyword. --- tests/static_tests/alias.cpp | 14 +++++++------- tests/static_tests/function_static_tests.cpp | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/static_tests/alias.cpp b/tests/static_tests/alias.cpp index cfa83bfda..0945d5f08 100644 --- a/tests/static_tests/alias.cpp +++ b/tests/static_tests/alias.cpp @@ -31,17 +31,17 @@ void runTest(ColAlias /*colRef*/) { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES -template> -concept table_alias_callable = requires { +template> +concept table_alias_callable = orm_table_alias && requires { { get_all() } -> same_as>>; { count() } -> same_as>; }; -template> -concept storage_table_alias_callable = requires(S& storage) { - { storage.get_all() } -> same_as>; - { storage.count() } -> same_as; - { storage.iterate() } -> same_as>; +template> +concept storage_table_alias_callable = orm_table_alias && requires(S& storage) { + { storage.template get_all() } -> same_as>; + { storage.template count() } -> same_as; + { storage.template iterate() } -> same_as>; }; #endif diff --git a/tests/static_tests/function_static_tests.cpp b/tests/static_tests/function_static_tests.cpp index cbb827d43..ea27d0ad6 100644 --- a/tests/static_tests/function_static_tests.cpp +++ b/tests/static_tests/function_static_tests.cpp @@ -15,14 +15,14 @@ using internal::quoted_scalar_function; #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template concept storage_scalar_callable = requires(S& storage) { - { storage.create_scalar_function() }; - { storage.delete_scalar_function() }; + { storage.template create_scalar_function() }; + { storage.template delete_scalar_function() }; }; template concept storage_aggregate_callable = requires(S& storage) { - { storage.create_aggregate_function() }; - { storage.delete_aggregate_function() }; + { storage.template create_aggregate_function() }; + { storage.template delete_aggregate_function() }; }; #endif From a46353b7ff0b06b2b623d4fb00ba288169cc1b7a Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 26 Mar 2025 22:59:22 +0200 Subject: [PATCH 131/170] Renamed internal method that sync's regular tables --- dev/implementations/storage_definitions.h | 4 ++-- dev/storage.h | 2 +- include/sqlite_orm/sqlite_orm.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index 7fd6f1d1b..142e24f4d 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -34,13 +34,13 @@ namespace sqlite_orm { std::is_same, sqlite_master>::value) { return sync_schema_result::already_in_sync; } else { - return this->_sync_normal_table(table, db, preserve); + return this->sync_regular_table(table, db, preserve); } } template template> - sync_schema_result storage_t::_sync_normal_table(const Table& table, sqlite3* db, bool preserve) { + sync_schema_result storage_t::sync_regular_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/storage.h b/dev/storage.h index 6cf1a74dd..eaa91c1ab 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1209,7 +1209,7 @@ namespace sqlite_orm { sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); template = true> - sync_schema_result _sync_normal_table(const Table& table, sqlite3* db, bool preserve); + sync_schema_result sync_regular_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/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index d996e1356..0b56373fc 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -23725,7 +23725,7 @@ namespace sqlite_orm { sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); template = true> - sync_schema_result _sync_normal_table(const Table& table, sqlite3* db, bool preserve); + sync_schema_result sync_regular_table(const Table& table, sqlite3* db, bool preserve); template void add_column(sqlite3* db, const std::string& tableName, const C& column) const { @@ -24637,13 +24637,13 @@ namespace sqlite_orm { std::is_same, sqlite_master>::value) { return sync_schema_result::already_in_sync; } else { - return this->_sync_normal_table(table, db, preserve); + return this->sync_regular_table(table, db, preserve); } } template template> - sync_schema_result storage_t::_sync_normal_table(const Table& table, sqlite3* db, bool preserve) { + sync_schema_result storage_t::sync_regular_table(const Table& table, sqlite3* db, bool preserve) { auto res = sync_schema_result::already_in_sync; bool attempt_to_preserve = true; From 7b8fd9b59d7e0a6da8c7105b46f6f4df51d100c1 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 27 Mar 2025 14:30:10 +0200 Subject: [PATCH 132/170] Renamed `empty_callable` -> `always_default` --- dev/functional/static_magic.h | 18 ++++++++++-------- dev/statement_serializer.h | 2 +- include/sqlite_orm/sqlite_orm.h | 6 +++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/dev/functional/static_magic.h b/dev/functional/static_magic.h index 7f2fe0ee9..47f00ea61 100644 --- a/dev/functional/static_magic.h +++ b/dev/functional/static_magic.h @@ -2,21 +2,23 @@ namespace sqlite_orm { - // got from here - // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co namespace internal { - // note: this is a class template accompanied with a variable template because older compilers (e.g. VC 2017) - // cannot handle a static lambda variable inside a template function + /* + * Function object whose variadic call operator always returns the default constructed value of its template parameter type. + */ template - struct empty_callable_t { + struct always_default_of { template - R operator()(Args&&...) const { + constexpr R operator()(Args&&...) const { return R(); } + + using is_transparent = int; }; - template - constexpr empty_callable_t empty_callable{}; + + template + constexpr always_default_of always_default{}; } } diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index a6890635d..01a7f4c63 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1272,7 +1272,7 @@ namespace sqlite_orm { << streaming_non_generated_column_names(table) << ")" << " VALUES (" << streaming_field_values_excluding(check_if{}, - empty_callable, // don't exclude + always_default, // don't exclude context, get_ref(statement.object)) << ")"; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 0b56373fc..386f2b91c 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -18993,14 +18993,14 @@ namespace sqlite_orm { // note: this is a class template accompanied with a variable template because older compilers (e.g. VC 2017) // cannot handle a static lambda variable inside a template function template - struct empty_callable_t { + struct always_default_t { template R operator()(Args&&...) const { return R(); } }; template - constexpr empty_callable_t empty_callable{}; + constexpr always_default_t always_default{}; } } @@ -21033,7 +21033,7 @@ namespace sqlite_orm { << streaming_non_generated_column_names(table) << ")" << " VALUES (" << streaming_field_values_excluding(check_if{}, - empty_callable, // don't exclude + always_default, // don't exclude context, get_ref(statement.object)) << ")"; From e2925428af205590b1804a6c42b9d6c14cbb0a5d Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 27 Mar 2025 20:43:17 +0200 Subject: [PATCH 133/170] Removed superfluous inclusion of _functional/cxx_core_features.h_ --- dev/column_pointer.h | 1 - dev/cte_types.h | 1 - include/sqlite_orm/sqlite_orm.h | 22 ++++++++++------------ 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/dev/column_pointer.h b/dev/column_pointer.h index 720f3f0b6..db6eb6b8e 100644 --- a/dev/column_pointer.h +++ b/dev/column_pointer.h @@ -5,7 +5,6 @@ #include // std::move #endif -#include "functional/cxx_core_features.h" #include "functional/cxx_type_traits_polyfill.h" #include "type_traits.h" #include "table_reference.h" diff --git a/dev/cte_types.h b/dev/cte_types.h index a28557b81..a1b3fe613 100644 --- a/dev/cte_types.h +++ b/dev/cte_types.h @@ -7,7 +7,6 @@ #endif #endif -#include "functional/cxx_core_features.h" #include "functional/cxx_type_traits_polyfill.h" #include "tuple_helper/tuple_fy.h" diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 386f2b91c..da1ebec03 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -2250,8 +2250,6 @@ namespace sqlite_orm { #include // std::move #endif -// #include "functional/cxx_core_features.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "type_traits.h" @@ -10623,8 +10621,6 @@ namespace sqlite_orm { #endif #endif -// #include "functional/cxx_core_features.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "tuple_helper/tuple_fy.h" @@ -18986,21 +18982,23 @@ namespace sqlite_orm { namespace sqlite_orm { - // got from here - // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co namespace internal { - // note: this is a class template accompanied with a variable template because older compilers (e.g. VC 2017) - // cannot handle a static lambda variable inside a template function + /* + * Function object whose variadic call operator always returns the default constructed value of its template parameter type. + */ template - struct always_default_t { + struct always_default_of { template - R operator()(Args&&...) const { + constexpr R operator()(Args&&...) const { return R(); } + + using is_transparent = int; }; - template - constexpr always_default_t always_default{}; + + template + constexpr always_default_of always_default{}; } } From a0eac9b2cb4ac938f1ef92d6a849db27106661a1 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 27 Mar 2025 21:33:35 +0200 Subject: [PATCH 134/170] Renamed _functional/static_magic.h_ -> _functional/always_default.h_ --- dev/functional/{static_magic.h => always_default.h} | 0 dev/statement_serializer.h | 2 +- include/sqlite_orm/sqlite_orm.h | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename dev/functional/{static_magic.h => always_default.h} (100%) diff --git a/dev/functional/static_magic.h b/dev/functional/always_default.h similarity index 100% rename from dev/functional/static_magic.h rename to dev/functional/always_default.h diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 01a7f4c63..ada6b1bb7 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -18,7 +18,7 @@ #include "functional/cxx_type_traits_polyfill.h" // std::remove_cvref, std::disjunction #include "functional/cxx_functional_polyfill.h" // std::identity, std::invoke -#include "functional/static_magic.h" +#include "functional/always_default.h" #include "functional/mpl.h" #include "tuple_helper/tuple_filter.h" #include "ast/upsert_clause.h" diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index da1ebec03..456466578 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -18978,7 +18978,7 @@ namespace sqlite_orm { // std::remove_cvref, std::disjunction // #include "functional/cxx_functional_polyfill.h" // std::identity, std::invoke -// #include "functional/static_magic.h" +// #include "functional/always_default.h" namespace sqlite_orm { From f5f7511ff42fa7baf9629e27cd5965e266a9281f Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 27 Mar 2025 21:36:44 +0200 Subject: [PATCH 135/170] Improved `same_or_void` with concepts --- dev/tuple_helper/same_or_void.h | 16 ++++++++-- include/sqlite_orm/sqlite_orm.h | 16 ++++++++-- .../static_tests/functional/same_or_void.cpp | 32 +++++++++---------- 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/dev/tuple_helper/same_or_void.h b/dev/tuple_helper/same_or_void.h index 002b7f581..b0c651142 100644 --- a/dev/tuple_helper/same_or_void.h +++ b/dev/tuple_helper/same_or_void.h @@ -2,6 +2,9 @@ #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::common_type +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include // std::same_as +#endif #endif namespace sqlite_orm { @@ -15,6 +18,15 @@ namespace sqlite_orm { using type = void; }; + template + using same_or_void_t = typename same_or_void::type; + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template... Rest> + struct same_or_void { + using type = A; + }; +#else template struct same_or_void { using type = A; @@ -25,11 +37,9 @@ namespace sqlite_orm { using type = A; }; - template - using same_or_void_t = typename same_or_void::type; - template struct same_or_void : same_or_void {}; +#endif template struct common_type_of; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 456466578..876cae5d1 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -3199,6 +3199,9 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::common_type +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include // std::same_as +#endif #endif namespace sqlite_orm { @@ -3212,6 +3215,15 @@ namespace sqlite_orm { using type = void; }; + template + using same_or_void_t = typename same_or_void::type; + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template... Rest> + struct same_or_void { + using type = A; + }; +#else template struct same_or_void { using type = A; @@ -3222,11 +3234,9 @@ namespace sqlite_orm { using type = A; }; - template - using same_or_void_t = typename same_or_void::type; - template struct same_or_void : same_or_void {}; +#endif template struct common_type_of; diff --git a/tests/static_tests/functional/same_or_void.cpp b/tests/static_tests/functional/same_or_void.cpp index a83c2dd6a..22bae8edf 100644 --- a/tests/static_tests/functional/same_or_void.cpp +++ b/tests/static_tests/functional/same_or_void.cpp @@ -9,30 +9,30 @@ using namespace sqlite_orm; TEST_CASE("same_or_void") { using internal::common_type_of_t; - using internal::same_or_void; + using internal::same_or_void_t; // one argument - STATIC_REQUIRE(std::is_same::type, int>::value); - STATIC_REQUIRE(std::is_same::type, std::string>::value); - STATIC_REQUIRE(std::is_same::type, long>::value); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, std::string>::value); + STATIC_REQUIRE(std::is_same, long>::value); // two arguments - STATIC_REQUIRE(std::is_same::type, int>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); - STATIC_REQUIRE(std::is_same::type, std::string>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, void>::value); + STATIC_REQUIRE(std::is_same, std::string>::value); + STATIC_REQUIRE(std::is_same, void>::value); // three arguments - STATIC_REQUIRE(std::is_same::type, int>::value); - STATIC_REQUIRE(std::is_same::type, long>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, long>::value); + STATIC_REQUIRE(std::is_same, void>::value); + STATIC_REQUIRE(std::is_same, void>::value); + STATIC_REQUIRE(std::is_same, void>::value); // four arguments - STATIC_REQUIRE(std::is_same::type, int>::value); - STATIC_REQUIRE(std::is_same::type, long>::value); - STATIC_REQUIRE(std::is_same::type, void>::value); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, long>::value); + STATIC_REQUIRE(std::is_same, void>::value); // type pack, e.g. tuple STATIC_REQUIRE(std::is_same>, int>::value); From 5b2d1169a93086e3e88e2773e28ad38616d0f882 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 27 Mar 2025 23:22:19 +0200 Subject: [PATCH 136/170] Used `std::time` in a qualified way --- tests/constraints/foreign_key.cpp | 10 ++++++---- tests/select_constraints_tests.cpp | 4 +++- .../table_constraints/foreign_key.cpp | 3 ++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/constraints/foreign_key.cpp b/tests/constraints/foreign_key.cpp index b4fd703ba..6593d50ec 100644 --- a/tests/constraints/foreign_key.cpp +++ b/tests/constraints/foreign_key.cpp @@ -2,6 +2,8 @@ #include #include // std::is_same +#include // std::uint8_t +#include // std::time_t, std::time #include "../static_tests/static_tests_storage_traits.h" @@ -22,8 +24,8 @@ TEST_CASE("Foreign key") { int id; std::unique_ptr location; std::unique_ptr user; - int visited_at; - uint8_t mark; + std::time_t visited_at; + std::uint8_t mark; }; // this case didn't compile on linux until `typedef constraints_type` was added to `foreign_key_t` @@ -56,8 +58,8 @@ TEST_CASE("Foreign key") { } storage.sync_schema(); - int fromDate = int(std::time(nullptr)); - int toDate = int(std::time(nullptr)); + std::time_t fromDate = std::time(nullptr); + std::time_t toDate = std::time(nullptr); int toDistance = 100; auto id = 10; storage.select(columns(&Visit::mark, &Visit::visited_at, &Location::place), diff --git a/tests/select_constraints_tests.cpp b/tests/select_constraints_tests.cpp index e13910e58..8871e8e10 100644 --- a/tests/select_constraints_tests.cpp +++ b/tests/select_constraints_tests.cpp @@ -1,6 +1,8 @@ #include #include +#include // std::time_t + using namespace sqlite_orm; namespace { @@ -227,7 +229,7 @@ namespace { struct Visit1 { int id = 0; int userId = 0; - time_t time = 0; + std::time_t time = 0; }; } #if SQLITE_VERSION_NUMBER >= 3006019 diff --git a/tests/statement_serializer_tests/table_constraints/foreign_key.cpp b/tests/statement_serializer_tests/table_constraints/foreign_key.cpp index f208cd78b..21c7fcf8e 100644 --- a/tests/statement_serializer_tests/table_constraints/foreign_key.cpp +++ b/tests/statement_serializer_tests/table_constraints/foreign_key.cpp @@ -2,6 +2,7 @@ #include #include // std::is_same +#include // std::time_t #include "../../static_tests/static_tests_storage_traits.h" @@ -338,7 +339,7 @@ TEST_CASE("statement_serializer foreign key") { struct UserVisit { int userId = 0; std::string userFirstName; - time_t time = 0; + std::time_t time = 0; }; auto fk = foreign_key(&UserVisit::userId, &UserVisit::userFirstName).references(&User::id, &User::firstName); From 132221a955e4c4fe250f75a405ddc0b9ba79ade2 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 27 Mar 2025 23:22:39 +0200 Subject: [PATCH 137/170] Added a unit test for `always_default` --- tests/static_tests/functional/mpl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/static_tests/functional/mpl.cpp b/tests/static_tests/functional/mpl.cpp index 719c310df..b86b9bce3 100644 --- a/tests/static_tests/functional/mpl.cpp +++ b/tests/static_tests/functional/mpl.cpp @@ -109,4 +109,6 @@ TEST_CASE("mpl") { 0); STATIC_REQUIRE(mpl::invoke_t, std::tuple>::value == 1); + + STATIC_REQUIRE(internal::always_default() value); } From 90a4fe5df9fd2bd23720de70f939bbef7a7d82c2 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 27 Mar 2025 23:52:23 +0200 Subject: [PATCH 138/170] Addressed a few things found when compiling with VC 15.9 --- appveyor.yml | 8 +-- dev/function.h | 2 +- dev/functional/mpl.h | 2 +- dev/tuple_helper/tuple_iteration.h | 8 +-- include/sqlite_orm/sqlite_orm.h | 12 ++-- tests/constraints/foreign_key.cpp | 4 +- .../table_constraints/foreign_key.cpp | 8 +-- .../static_tests_storage_traits.h | 67 ++++++++----------- 8 files changed, 49 insertions(+), 62 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 6dd0e683a..1f0a07715 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -72,8 +72,8 @@ environment: cmake_build_parallel: "" # Representative for C++17 - - job_name: Visual Studio 2019, x64, C++17 - appveyor_build_worker_image: Visual Studio 2019 + - job_name: Visual Studio 2017, x64, C++17 + appveyor_build_worker_image: Visual Studio 2017 platform: x64 SQLITE_ORM_CXX_STANDARD: "-DSQLITE_ORM_ENABLE_CXX_17=ON" @@ -95,7 +95,7 @@ for: # Windows matrix: only: - - appveyor_build_worker_image: Visual Studio 2019 + - appveyor_build_worker_image: Visual Studio 2017 - appveyor_build_worker_image: Visual Studio 2022 init: - |- @@ -104,7 +104,7 @@ for: if "%platform%"=="x64" (set architecture=-A x64) if "%platform%"=="x86" (set architecture=-A Win32) if "%appveyor_build_worker_image%"=="Visual Studio 2022" (set generator="Visual Studio 17 2022" %architecture%) - if "%appveyor_build_worker_image%"=="Visual Studio 2019" (set generator="Visual Studio 16 2019" %architecture%) + if "%appveyor_build_worker_image%"=="Visual Studio 2017" (set generator="Visual Studio 15 2017" %architecture%) install: - |- cd C:\Tools\vcpkg diff --git a/dev/function.h b/dev/function.h index 6f30ccff9..11d8b3ef4 100644 --- a/dev/function.h +++ b/dev/function.h @@ -257,7 +257,7 @@ namespace sqlite_orm { return valid; } -#if __cplusplus >= 201703L // C++17 or later +#ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR template SQLITE_ORM_CONSTEVAL bool assert_same_pointer_tag() { constexpr bool valid = Binding == PointerArg; diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index 108f3ccd6..ba2bd3bcf 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -46,7 +46,7 @@ namespace sqlite_orm { * Determines whether a class template has a nested metafunction `fn`. * * Implementation note: the technique of specialiazing on the inline variable must come first because - * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR]. */ template SQLITE_ORM_INLINE_VAR constexpr bool is_quoted_metafuntion_v = false; diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index b9280e6bf..c3e4936af 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -13,7 +13,7 @@ namespace sqlite_orm { constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr (reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= - int sink; + [[maybe_unused]] int sink; // note: `(void)` cast silences warning 'expression result unused' (void)((lambda(std::get(tpl)), sink) = ... = 0); } else { @@ -44,18 +44,18 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED template - void iterate_tuple(std::index_sequence, L&& lambda) { + constexpr void iterate_tuple(std::index_sequence, L&& lambda) { (lambda((std::tuple_element_t*)nullptr), ...); } #else template - void iterate_tuple(std::index_sequence, L&& lambda) { + constexpr void iterate_tuple(std::index_sequence, L&& lambda) { using Sink = int[sizeof...(Idx)]; (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; } #endif template - void iterate_tuple(L&& lambda) { + constexpr void iterate_tuple(L&& lambda) { iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 876cae5d1..90f6cab96 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -782,7 +782,7 @@ namespace sqlite_orm { * Determines whether a class template has a nested metafunction `fn`. * * Implementation note: the technique of specialiazing on the inline variable must come first because - * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR]. */ template SQLITE_ORM_INLINE_VAR constexpr bool is_quoted_metafuntion_v = false; @@ -1647,7 +1647,7 @@ namespace sqlite_orm { constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr (reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= - int sink; + [[maybe_unused]] int sink; // note: `(void)` cast silences warning 'expression result unused' (void)((lambda(std::get(tpl)), sink) = ... = 0); } else { @@ -1678,18 +1678,18 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED template - void iterate_tuple(std::index_sequence, L&& lambda) { + constexpr void iterate_tuple(std::index_sequence, L&& lambda) { (lambda((std::tuple_element_t*)nullptr), ...); } #else template - void iterate_tuple(std::index_sequence, L&& lambda) { + constexpr void iterate_tuple(std::index_sequence, L&& lambda) { using Sink = int[sizeof...(Idx)]; (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; } #endif template - void iterate_tuple(L&& lambda) { + constexpr void iterate_tuple(L&& lambda) { iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); } @@ -11253,7 +11253,7 @@ namespace sqlite_orm { return valid; } -#if __cplusplus >= 201703L // C++17 or later +#ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_NTTP_EXPR template SQLITE_ORM_CONSTEVAL bool assert_same_pointer_tag() { constexpr bool valid = Binding == PointerArg; diff --git a/tests/constraints/foreign_key.cpp b/tests/constraints/foreign_key.cpp index 6593d50ec..39e14e795 100644 --- a/tests/constraints/foreign_key.cpp +++ b/tests/constraints/foreign_key.cpp @@ -47,8 +47,8 @@ TEST_CASE("Foreign key") { using namespace sqlite_orm::internal::storage_traits; using Storage = decltype(storage); - STATIC_REQUIRE(storage_foreign_keys_count::value == 1); - STATIC_REQUIRE(storage_foreign_keys_count::value == 0); + STATIC_REQUIRE(storage_foreign_keys_target_count() == 1); + STATIC_REQUIRE(storage_foreign_keys_target_count() == 0); using LocationFks = storage_fk_references::type; STATIC_REQUIRE(std::is_same>::value); diff --git a/tests/statement_serializer_tests/table_constraints/foreign_key.cpp b/tests/statement_serializer_tests/table_constraints/foreign_key.cpp index 21c7fcf8e..7db1273c3 100644 --- a/tests/statement_serializer_tests/table_constraints/foreign_key.cpp +++ b/tests/statement_serializer_tests/table_constraints/foreign_key.cpp @@ -354,16 +354,16 @@ TEST_CASE("statement_serializer foreign key") { make_column("last_name", &User::lastName), primary_key(&User::id, &User::firstName)); - STATIC_REQUIRE(table_foreign_keys_count::value == 0); - STATIC_REQUIRE(table_foreign_keys_count::value == 0); + STATIC_REQUIRE(table_foreign_keys_target_count() == 0); + STATIC_REQUIRE(table_foreign_keys_target_count() == 0); auto visitsTable = make_table("visits", make_column("user_id", &UserVisit::userId), make_column("user_first_name", &UserVisit::userFirstName), make_column("time", &UserVisit::time), fk); - STATIC_REQUIRE(table_foreign_keys_count::value == 1); - STATIC_REQUIRE(table_foreign_keys_count::value == 0); + STATIC_REQUIRE(table_foreign_keys_target_count() == 1); + STATIC_REQUIRE(table_foreign_keys_target_count() == 0); using db_objects_t = internal::db_objects_tuple; diff --git a/tests/static_tests/static_tests_storage_traits.h b/tests/static_tests/static_tests_storage_traits.h index 453cf2955..503dcf8f4 100644 --- a/tests/static_tests/static_tests_storage_traits.h +++ b/tests/static_tests/static_tests_storage_traits.h @@ -33,45 +33,6 @@ namespace sqlite_orm { struct storage_columns_count : storage_columns_count_impl> {}; - /** - * Table A `table_t<>` - * Lookup is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has Lookup = User) - */ - template - struct table_foreign_keys_count - : count_filtered_tuple, - check_if_is_type::template fn, - filter_tuple_sequence_t, is_foreign_key>, - target_type_t> {}; - - /** - * DBOs - db_objects_tuple - * Lookup - type mapped to storage - */ - template - struct storage_foreign_keys_count_impl : std::integral_constant {}; - -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED - template - struct storage_foreign_keys_count_impl, Lookup> { - static constexpr int value = (table_foreign_keys_count::value + ...); - }; -#else - template - struct storage_foreign_keys_count_impl, Lookup> { - static constexpr int value = table_foreign_keys_count::value + - storage_foreign_keys_count_impl, Lookup>::value; - }; -#endif - - /** - * S - storage class - * Lookup - type mapped to storage - * This class tells how many types mapped to DBOs have foreign keys to Lookup - */ - template - struct storage_foreign_keys_count : storage_foreign_keys_count_impl {}; - template using table_foreign_keys_t = filter_tuple_t, @@ -82,7 +43,7 @@ namespace sqlite_orm { /* * Implementation note: must be a struct instead of an alias template because the foreign keys tuple * must be hoisted into a named alias, otherwise type replacement may fail for legacy compilers - * if an alias template has a dependent expression in it [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + * if a template template has a dependent expression in it [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. */ template struct table_fk_references { @@ -119,6 +80,32 @@ namespace sqlite_orm { template struct storage_foreign_keys : storage_foreign_keys_impl {}; + + template + constexpr int table_foreign_keys_target_count() { + using namespace sqlite_orm::internal; + + using elements_type = elements_type_t
; + using fk_index_sequence = filter_tuple_sequence_t; + using filtered_index_sequence = filter_tuple_sequence_t::template fn, + target_type_t, + fk_index_sequence>; + + return int(filtered_index_sequence::size()); + } + + template + constexpr int storage_foreign_keys_target_count() { + using namespace sqlite_orm::internal; + + int res = 0; + iterate_tuple(tables_index_sequence{}, [&res](const auto* dummy) { + using table_type = std::remove_pointer_t; + res += table_foreign_keys_target_count(); + }); + return res; + } } } } From 06ac67718fec13ec5102945f7707606d450aa1da Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 27 Mar 2025 23:59:23 +0200 Subject: [PATCH 139/170] Renamed type trait `is_base_of_template` -> `is_base_template_of` --- dev/conditions.h | 4 +-- dev/core_functions.h | 4 +-- .../is_base_template_of.h} | 12 ++++---- dev/operators.h | 4 +-- dev/select_constraints.h | 4 +-- include/sqlite_orm/sqlite_orm.h | 28 +++++++++---------- 6 files changed, 28 insertions(+), 28 deletions(-) rename dev/{is_base_of_template.h => functional/is_base_template_of.h} (72%) diff --git a/dev/conditions.h b/dev/conditions.h index 1ac5b4584..ee340ea83 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -11,7 +11,7 @@ #endif #include "functional/cxx_type_traits_polyfill.h" -#include "is_base_of_template.h" +#include "functional/is_base_template_of.h" #include "type_traits.h" #include "collate_argument.h" #include "constraints.h" @@ -142,7 +142,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_template_of_v; template struct is_binary_condition : polyfill::bool_constant> {}; diff --git a/dev/core_functions.h b/dev/core_functions.h index 8d66c034c..ac9b38a90 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -10,7 +10,7 @@ #include "functional/cxx_type_traits_polyfill.h" #include "functional/mpl/conditional.h" -#include "is_base_of_template.h" +#include "functional/is_base_template_of.h" #include "tuple_helper/tuple_traits.h" #include "conditions.h" #include "serialize_result_type.h" @@ -46,7 +46,7 @@ namespace sqlite_orm { template SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = - is_base_of_template::value; + is_base_template_of::value; template struct is_built_in_function : polyfill::bool_constant> {}; diff --git a/dev/is_base_of_template.h b/dev/functional/is_base_template_of.h similarity index 72% rename from dev/is_base_of_template.h rename to dev/functional/is_base_template_of.h index 6a77d7e51..9728dbd7e 100644 --- a/dev/is_base_of_template.h +++ b/dev/functional/is_base_template_of.h @@ -14,7 +14,7 @@ namespace sqlite_orm { */ #ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION template class Base> - struct is_base_of_template_impl { + struct is_base_template_of_impl { template static constexpr std::true_type test(const Base&); @@ -22,19 +22,19 @@ namespace sqlite_orm { }; template class C> - using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); + using is_base_template_of = decltype(is_base_template_of_impl::test(std::declval())); #else template class C, typename... Ts> - std::true_type is_base_of_template_impl(const C&); + std::true_type is_base_template_of_impl(const C&); template class C> - std::false_type is_base_of_template_impl(...); + std::false_type is_base_template_of_impl(...); template class C> - using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); + using is_base_template_of = decltype(is_base_template_of_impl(std::declval())); #endif template class C> - SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_base_template_of_v = is_base_template_of::value; } } diff --git a/dev/operators.h b/dev/operators.h index 9244e3153..319228c8a 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -6,7 +6,7 @@ #endif #include "functional/cxx_type_traits_polyfill.h" -#include "is_base_of_template.h" +#include "functional/is_base_template_of.h" #include "tags.h" #include "serialize_result_type.h" @@ -26,7 +26,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_of_template::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_template_of::value; template using is_binary_operator = polyfill::bool_constant>; diff --git a/dev/select_constraints.h b/dev/select_constraints.h index f1048f6db..754384924 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -12,7 +12,7 @@ #include "functional/cxx_optional.h" #include "functional/cxx_type_traits_polyfill.h" -#include "is_base_of_template.h" +#include "functional/is_base_template_of.h" #include "tuple_helper/tuple_traits.h" #include "tuple_helper/tuple_transformer.h" #include "tuple_helper/tuple_iteration.h" @@ -154,7 +154,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_of_template::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_template_of::value; template using is_compound_operator = polyfill::bool_constant>; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 90f6cab96..b8198e690 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -4225,7 +4225,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" -// #include "is_base_of_template.h" +// #include "functional/is_base_template_of.h" #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::declval @@ -4241,7 +4241,7 @@ namespace sqlite_orm { */ #ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION template class Base> - struct is_base_of_template_impl { + struct is_base_template_of_impl { template static constexpr std::true_type test(const Base&); @@ -4249,20 +4249,20 @@ namespace sqlite_orm { }; template class C> - using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); + using is_base_template_of = decltype(is_base_template_of_impl::test(std::declval())); #else template class C, typename... Ts> - std::true_type is_base_of_template_impl(const C&); + std::true_type is_base_template_of_impl(const C&); template class C> - std::false_type is_base_of_template_impl(...); + std::false_type is_base_template_of_impl(...); template class C> - using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); + using is_base_template_of = decltype(is_base_template_of_impl(std::declval())); #endif template class C> - SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_base_template_of_v = is_base_template_of::value; } } @@ -4320,7 +4320,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_of_template::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_template_of::value; template using is_binary_operator = polyfill::bool_constant>; @@ -4610,7 +4610,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" -// #include "is_base_of_template.h" +// #include "functional/is_base_template_of.h" // #include "tuple_helper/tuple_traits.h" @@ -4778,7 +4778,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "functional/mpl/conditional.h" -// #include "is_base_of_template.h" +// #include "functional/is_base_template_of.h" // #include "tuple_helper/tuple_traits.h" @@ -4796,7 +4796,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" -// #include "is_base_of_template.h" +// #include "functional/is_base_template_of.h" // #include "type_traits.h" @@ -5101,7 +5101,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_template_of_v; template struct is_binary_condition : polyfill::bool_constant> {}; @@ -6297,7 +6297,7 @@ namespace sqlite_orm { template SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = - is_base_of_template::value; + is_base_template_of::value; template struct is_built_in_function : polyfill::bool_constant> {}; @@ -8889,7 +8889,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_of_template::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_template_of::value; template using is_compound_operator = polyfill::bool_constant>; From 491ef23e9cdd4df37701d8d9fd113f5a7e846047 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 28 Mar 2025 00:15:36 +0200 Subject: [PATCH 140/170] Coalesced checks for C++14 language features --- dev/functional/cxx_check_prerequisites.h | 8 +++----- include/sqlite_orm/sqlite_orm.h | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/dev/functional/cxx_check_prerequisites.h b/dev/functional/cxx_check_prerequisites.h index c3f21922f..518b84933 100644 --- a/dev/functional/cxx_check_prerequisites.h +++ b/dev/functional/cxx_check_prerequisites.h @@ -4,11 +4,9 @@ * This header detects missing core C++ language features on which sqlite_orm depends, bailing out with a hard error. */ -#if __cpp_aggregate_nsdmi < 201304L -#error A fully C++17-compliant compiler is required. -#endif - -#if __cpp_constexpr < 201304L +// note: Before the C++17 language standard was made the baseline, the library had workarounds for these specific missing C++14 language features, +// so they are kept here as explicit checks for reference. +#if __cpp_aggregate_nsdmi < 201304L || __cpp_constexpr < 201304L #error A fully C++17-compliant compiler is required. #endif diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index b8198e690..60a3a43f2 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -39,11 +39,9 @@ using std::nullptr_t; * This header detects missing core C++ language features on which sqlite_orm depends, bailing out with a hard error. */ -#if __cpp_aggregate_nsdmi < 201304L -#error A fully C++17-compliant compiler is required. -#endif - -#if __cpp_constexpr < 201304L +// note: Before the C++17 language standard was made the baseline, the library had workarounds for these specific missing C++14 language features, +// so they are kept here as explicit checks for reference. +#if __cpp_aggregate_nsdmi < 201304L || __cpp_constexpr < 201304L #error A fully C++17-compliant compiler is required. #endif From cd8737d27eec0139644043db586dcbcef454a713 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 28 Mar 2025 08:07:28 +0200 Subject: [PATCH 141/170] Corrected unit test for `always_default` --- tests/static_tests/functional/mpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/static_tests/functional/mpl.cpp b/tests/static_tests/functional/mpl.cpp index b86b9bce3..462f85aef 100644 --- a/tests/static_tests/functional/mpl.cpp +++ b/tests/static_tests/functional/mpl.cpp @@ -110,5 +110,5 @@ TEST_CASE("mpl") { STATIC_REQUIRE(mpl::invoke_t, std::tuple>::value == 1); - STATIC_REQUIRE(internal::always_default() value); + STATIC_REQUIRE(internal::always_default()()); } From ba448ce2999de16d758496f047ca348794bb99be Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 28 Mar 2025 21:23:33 +0200 Subject: [PATCH 142/170] Removed superfluous newlines introduced in commit #f96cf150bdb4 --- dev/functional/index_sequence_util.h | 13 +++---------- include/sqlite_orm/sqlite_orm.h | 7 ------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/dev/functional/index_sequence_util.h b/dev/functional/index_sequence_util.h index dc2d2da03..bf485dfa9 100644 --- a/dev/functional/index_sequence_util.h +++ b/dev/functional/index_sequence_util.h @@ -7,17 +7,13 @@ namespace sqlite_orm { namespace internal { #if defined(SQLITE_ORM_PACK_INDEXING_SUPPORTED) - /** - * Get the index value of an `index_sequence` at a specific position. - */ + /** * Get the index value of an `index_sequence` at a specific position. */ template SQLITE_ORM_CONSTEVAL auto index_sequence_value_at(std::index_sequence) { return Idx...[Pos]; } #elif defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) - /** - * Get the index value of an `index_sequence` at a specific position. - */ + /** * Get the index value of an `index_sequence` at a specific position. */ template SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { static_assert(Pos < sizeof...(Idx)); @@ -32,10 +28,7 @@ namespace sqlite_orm { return result; } #else - /** - * Get the index value of an `index_sequence` at a specific position. - * `Pos` must always be `0`. - */ + /** * Get the index value of an `index_sequence` at a specific position. * `Pos` must always be `0`. */ template SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { static_assert(Pos == 0, ""); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 60a3a43f2..925f1d0b2 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1345,9 +1345,7 @@ namespace sqlite_orm { namespace internal { #if defined(SQLITE_ORM_PACK_INDEXING_SUPPORTED) /** - * Get the index value of an `index_sequence` at a specific position. - */ template SQLITE_ORM_CONSTEVAL auto index_sequence_value_at(std::index_sequence) { @@ -1355,9 +1353,7 @@ namespace sqlite_orm { } #elif defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) /** - * Get the index value of an `index_sequence` at a specific position. - */ template SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { @@ -1374,11 +1370,8 @@ namespace sqlite_orm { } #else /** - * Get the index value of an `index_sequence` at a specific position. - * `Pos` must always be `0`. - */ template SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { From b18ef87f24401d5d463778404b121d8abde97f1f Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 28 Mar 2025 21:36:31 +0200 Subject: [PATCH 143/170] Follow-up: Corrected concepts used in unit tests. --- dev/cte_storage.h | 1 - include/sqlite_orm/sqlite_orm.h | 1 - tests/static_tests/column_pointer.cpp | 46 +++++++++++++-------------- tests/static_tests/iterator_t.cpp | 4 +-- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/dev/cte_storage.h b/dev/cte_storage.h index 6cdf53d5f..7eeb789d3 100644 --- a/dev/cte_storage.h +++ b/dev/cte_storage.h @@ -72,7 +72,6 @@ namespace sqlite_orm { // F O::* template = true> auto make_cte_column(std::string name, const ColRef& finalColRef) { - using object_type = table_type_of_t; using column_type = column_t; return column_type{std::move(name), finalColRef, empty_setter{}}; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 925f1d0b2..bdd722a70 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -22317,7 +22317,6 @@ namespace sqlite_orm { // F O::* template = true> auto make_cte_column(std::string name, const ColRef& finalColRef) { - using object_type = table_type_of_t; using column_type = column_t; return column_type{std::move(name), finalColRef, empty_setter{}}; diff --git a/tests/static_tests/column_pointer.cpp b/tests/static_tests/column_pointer.cpp index 74cb0ce73..4d4df3ef4 100644 --- a/tests/static_tests/column_pointer.cpp +++ b/tests/static_tests/column_pointer.cpp @@ -62,19 +62,19 @@ concept storage_field_callable = requires(S& storage, C field) { { storage.group_concat(field, 42) } -> same_as; }; -template -concept refers_to_recordset_callable = requires { +template +concept refers_to_recordset_callable = orm_refers_to_recordset && requires { { count() } -> same_as>>; }; -template> -concept refers_to_table_callable = requires { +template> +concept refers_to_table_callable = orm_refers_to_table && requires { { get_all() } -> same_as, std::vector>>; { count() } -> same_as>>; }; -template> -concept table_reference_callable = requires { +template> +concept table_reference_callable = orm_table_reference && requires { { get
(42) } -> same_as>; { get_pointer
(42) } -> same_as>; { get_optional
(42) } -> same_as>; @@ -86,25 +86,25 @@ concept table_reference_callable = requires { { count
() } -> same_as>; }; -template> -concept storage_refers_to_table_callable = requires(S& storage) { - { storage.get_all() } -> same_as>; - { storage.count() } -> same_as; - { storage.iterate() } -> same_as>; +template> +concept storage_refers_to_table_callable = orm_refers_to_table && requires(S& storage) { + { storage.template get_all() } -> same_as>; + { storage.template count() } -> same_as; + { storage.template iterate() } -> same_as>; }; -template> -concept storage_table_reference_callable = requires(S& storage) { - { storage.get
(42) } -> same_as; - { storage.get_pointer
(42) } -> same_as>; - { storage.get_optional
(42) } -> same_as>; - { storage.get_all
() } -> same_as>; - { storage.get_all_pointer
() } -> same_as>>; - { storage.get_all_optional
() } -> same_as>>; - { storage.remove
(42) } -> same_as; - { storage.remove_all
() } -> same_as; - { storage.count
() } -> same_as; - { storage.iterate
() } -> same_as>; +template> +concept storage_table_reference_callable = orm_table_reference && requires(S& storage) { + { storage.template get
(42) } -> same_as; + { storage.template get_pointer
(42) } -> same_as>; + { storage.template get_optional
(42) } -> same_as>; + { storage.template get_all
() } -> same_as>; + { storage.template get_all_pointer
() } -> same_as>>; + { storage.template get_all_optional
() } -> same_as>>; + { storage.template remove
(42) } -> same_as; + { storage.template remove_all
() } -> same_as; + { storage.template count
() } -> same_as; + { storage.template iterate
() } -> same_as>; }; #endif diff --git a/tests/static_tests/iterator_t.cpp b/tests/static_tests/iterator_t.cpp index 554692cf5..efd7dab3b 100644 --- a/tests/static_tests/iterator_t.cpp +++ b/tests/static_tests/iterator_t.cpp @@ -72,8 +72,8 @@ concept can_view_mapped = requires(V view) { template concept storage_iterate_mapped = requires(S& storage_type) { - { storage_type.iterate() } -> std::same_as>; - { storage_type.iterate() } -> can_view_mapped; + { storage_type.template iterate() } -> std::same_as>; + { storage_type.template iterate() } -> can_view_mapped; }; #endif From c3e202716448a444666d3cb0ea4da337db007e42 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 28 Mar 2025 21:38:03 +0200 Subject: [PATCH 144/170] Reordered template parameters of type trait `is_base_template_of` --- dev/conditions.h | 2 +- dev/core_functions.h | 2 +- dev/functional/is_base_template_of.h | 18 +++++----- dev/operators.h | 2 +- dev/select_constraints.h | 2 +- include/sqlite_orm/sqlite_orm.h | 26 +++++++------- tests/static_tests/functional/functional.cpp | 36 ++++++++++++++++++++ tests/static_tests/functional/mpl.cpp | 2 -- 8 files changed, 62 insertions(+), 28 deletions(-) create mode 100644 tests/static_tests/functional/functional.cpp diff --git a/dev/conditions.h b/dev/conditions.h index ee340ea83..da0b77f53 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -142,7 +142,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_template_of_v; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_template_of_v; template struct is_binary_condition : polyfill::bool_constant> {}; diff --git a/dev/core_functions.h b/dev/core_functions.h index ac9b38a90..9c01d65af 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -46,7 +46,7 @@ namespace sqlite_orm { template SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = - is_base_template_of::value; + is_base_template_of::value; template struct is_built_in_function : polyfill::bool_constant> {}; diff --git a/dev/functional/is_base_template_of.h b/dev/functional/is_base_template_of.h index 9728dbd7e..14f350a74 100644 --- a/dev/functional/is_base_template_of.h +++ b/dev/functional/is_base_template_of.h @@ -21,20 +21,20 @@ namespace sqlite_orm { static constexpr std::false_type test(...); }; - template class C> - using is_base_template_of = decltype(is_base_template_of_impl::test(std::declval())); + template class Base, typename T> + using is_base_template_of = decltype(is_base_template_of_impl::test(std::declval())); #else - template class C, typename... Ts> - std::true_type is_base_template_of_impl(const C&); + template class Base, typename... Ts> + std::true_type is_base_template_of_impl(const Base&); - template class C> + template class Base> std::false_type is_base_template_of_impl(...); - template class C> - using is_base_template_of = decltype(is_base_template_of_impl(std::declval())); + template class Base, typename T> + using is_base_template_of = decltype(is_base_template_of_impl(std::declval())); #endif - template class C> - SQLITE_ORM_INLINE_VAR constexpr bool is_base_template_of_v = is_base_template_of::value; + template class Base, typename T> + SQLITE_ORM_INLINE_VAR constexpr bool is_base_template_of_v = is_base_template_of::value; } } diff --git a/dev/operators.h b/dev/operators.h index 319228c8a..eea071679 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -26,7 +26,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_template_of::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_template_of::value; template using is_binary_operator = polyfill::bool_constant>; diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 754384924..8d913cb32 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -154,7 +154,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_template_of::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_template_of::value; template using is_compound_operator = polyfill::bool_constant>; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index bdd722a70..f6f3c19f1 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -4239,21 +4239,21 @@ namespace sqlite_orm { static constexpr std::false_type test(...); }; - template class C> - using is_base_template_of = decltype(is_base_template_of_impl::test(std::declval())); + template class Base, typename T> + using is_base_template_of = decltype(is_base_template_of_impl::test(std::declval())); #else - template class C, typename... Ts> - std::true_type is_base_template_of_impl(const C&); + template class Base, typename... Ts> + std::true_type is_base_template_of_impl(const Base&); - template class C> + template class Base> std::false_type is_base_template_of_impl(...); - template class C> - using is_base_template_of = decltype(is_base_template_of_impl(std::declval())); + template class Base, typename T> + using is_base_template_of = decltype(is_base_template_of_impl(std::declval())); #endif - template class C> - SQLITE_ORM_INLINE_VAR constexpr bool is_base_template_of_v = is_base_template_of::value; + template class Base, typename T> + SQLITE_ORM_INLINE_VAR constexpr bool is_base_template_of_v = is_base_template_of::value; } } @@ -4311,7 +4311,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_template_of::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_template_of::value; template using is_binary_operator = polyfill::bool_constant>; @@ -5092,7 +5092,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_template_of_v; + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_template_of_v; template struct is_binary_condition : polyfill::bool_constant> {}; @@ -6288,7 +6288,7 @@ namespace sqlite_orm { template SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = - is_base_template_of::value; + is_base_template_of::value; template struct is_built_in_function : polyfill::bool_constant> {}; @@ -8880,7 +8880,7 @@ namespace sqlite_orm { }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_template_of::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_template_of::value; template using is_compound_operator = polyfill::bool_constant>; diff --git a/tests/static_tests/functional/functional.cpp b/tests/static_tests/functional/functional.cpp new file mode 100644 index 000000000..372d8cc55 --- /dev/null +++ b/tests/static_tests/functional/functional.cpp @@ -0,0 +1,36 @@ +#include +#include + +#include // std::true_type + +using namespace sqlite_orm; + +TEST_CASE("always_default") { + STATIC_REQUIRE(internal::always_default()()); +} + +// https://rextester.com/WTEWN51158 +// THE BROKEN CASE, Fixed In: Visual Studio 2019 version 16.0 Preview 5 +// It appears that when the base base class in question has more than 1 template parameter +// and the second template parameter is not varadic, the dudction in above in [A] is +// unable to deduce Ts... This works in both gcc and clang +template +struct basea {}; + +// WORKING with 1 template parameter +template +struct baseb {}; + +// WORKING if the second parameter is varadic +template +struct basec {}; + +TEST_CASE("is_base_template_of") { + struct testa : basea {}; + struct testb : baseb {}; + struct testc : basec {}; + + STATIC_REQUIRE(internal::is_base_template_of::value); + STATIC_REQUIRE(internal::is_base_template_of::value); + STATIC_REQUIRE(internal::is_base_template_of::value); +} diff --git a/tests/static_tests/functional/mpl.cpp b/tests/static_tests/functional/mpl.cpp index 462f85aef..719c310df 100644 --- a/tests/static_tests/functional/mpl.cpp +++ b/tests/static_tests/functional/mpl.cpp @@ -109,6 +109,4 @@ TEST_CASE("mpl") { 0); STATIC_REQUIRE(mpl::invoke_t, std::tuple>::value == 1); - - STATIC_REQUIRE(internal::always_default()()); } From 5624e6a66962c2a51c8ecf37995c0f56172376b0 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 28 Mar 2025 22:47:31 +0200 Subject: [PATCH 145/170] Unit test for reverse tuple iteration --- dev/tuple_helper/tuple_iteration.h | 4 ++++ tests/tuple_iteration.cpp | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index be24840d2..4697658ed 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -12,7 +12,11 @@ namespace sqlite_orm { constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr (reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= +#ifdef SQLITE_ORM_CONSTEVAL_SUPPORTED [[maybe_unused]] int sink; +#else + [[maybe_unused]] int sink = 0; +#endif // note: `(void)` cast silences warning 'expression result unused' (void)((lambda(std::get(tpl)), sink) = ... = 0); } else { diff --git a/tests/tuple_iteration.cpp b/tests/tuple_iteration.cpp index a08c92881..1db32fffc 100644 --- a/tests/tuple_iteration.cpp +++ b/tests/tuple_iteration.cpp @@ -12,7 +12,7 @@ TEST_CASE("tuple iteration") { std::vector expected; std::vector types; SECTION("iterate_tuple with tuple instance") { - auto lambda = [&types](const auto& item) { + const auto lambda = [&types](const auto& item) { types.emplace_back(typeid(item)); }; SECTION("empty") { @@ -29,15 +29,25 @@ TEST_CASE("tuple iteration") { iterate_tuple(tuple, lambda); expected = {typeid(std::string), typeid(long)}; } + SECTION("std::string, long, reversed") { + std::tuple tuple; + iterate_tuple(tuple, lambda); + expected = {typeid(long), typeid(std::string)}; + } SECTION("index selection") { constexpr size_t selectedIdx = 1; - std::tuple tuple; + std::tuple tuple; iterate_tuple(tuple, std::index_sequence{}, lambda); expected = {typeid(long)}; } + SECTION("index selection, reversed") { + std::tuple tuple; + iterate_tuple(tuple, std::index_sequence<1, 2>{}, lambda); + expected = {typeid(int), typeid(long)}; + } } SECTION("iterate_tuple with no tuple instance") { - auto lambda = [&types](auto* itemPointer) { + const auto lambda = [&types](auto* itemPointer) { using Item = std::remove_pointer_t; types.emplace_back(typeid(Item)); }; From 29a1aac51f106b94153e6432f4c80ba221818124 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 28 Mar 2025 23:08:49 +0200 Subject: [PATCH 146/170] Derived class for a string literator operator template needs its own ctor --- dev/function.h | 2 +- include/sqlite_orm/sqlite_orm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/function.h b/dev/function.h index 11d8b3ef4..c92563b6b 100644 --- a/dev/function.h +++ b/dev/function.h @@ -425,7 +425,7 @@ namespace sqlite_orm { template struct quoted_function_builder : cstring_literal { - using cstring_literal::cstring_literal; + constexpr quoted_function_builder(const char (&cstr)[N]) : cstring_literal{cstr} {} /* * From a freestanding function, possibly overloaded. diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index f6f3c19f1..020017ede 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -11412,7 +11412,7 @@ namespace sqlite_orm { template struct quoted_function_builder : cstring_literal { - using cstring_literal::cstring_literal; + constexpr quoted_function_builder(const char (&cstr)[N]) : cstring_literal{cstr} {} /* * From a freestanding function, possibly overloaded. From 6c537ac07b05ed7111699622ba883382b4ae0528 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 28 Mar 2025 23:10:44 +0200 Subject: [PATCH 147/170] Unit test for reverse tuple iteration --- dev/tuple_helper/tuple_iteration.h | 4 ++++ include/sqlite_orm/sqlite_orm.h | 4 ++++ tests/tuple_iteration.cpp | 16 +++++++++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index c3e4936af..b0bb8d647 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -13,7 +13,11 @@ namespace sqlite_orm { constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr (reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= +#ifdef SQLITE_ORM_CONSTEVAL_SUPPORTED [[maybe_unused]] int sink; +#else + [[maybe_unused]] int sink = 0; +#endif // note: `(void)` cast silences warning 'expression result unused' (void)((lambda(std::get(tpl)), sink) = ... = 0); } else { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 020017ede..769d39c30 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1638,7 +1638,11 @@ namespace sqlite_orm { constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr (reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= +#ifdef SQLITE_ORM_CONSTEVAL_SUPPORTED [[maybe_unused]] int sink; +#else + [[maybe_unused]] int sink = 0; +#endif // note: `(void)` cast silences warning 'expression result unused' (void)((lambda(std::get(tpl)), sink) = ... = 0); } else { diff --git a/tests/tuple_iteration.cpp b/tests/tuple_iteration.cpp index a08c92881..1db32fffc 100644 --- a/tests/tuple_iteration.cpp +++ b/tests/tuple_iteration.cpp @@ -12,7 +12,7 @@ TEST_CASE("tuple iteration") { std::vector expected; std::vector types; SECTION("iterate_tuple with tuple instance") { - auto lambda = [&types](const auto& item) { + const auto lambda = [&types](const auto& item) { types.emplace_back(typeid(item)); }; SECTION("empty") { @@ -29,15 +29,25 @@ TEST_CASE("tuple iteration") { iterate_tuple(tuple, lambda); expected = {typeid(std::string), typeid(long)}; } + SECTION("std::string, long, reversed") { + std::tuple tuple; + iterate_tuple(tuple, lambda); + expected = {typeid(long), typeid(std::string)}; + } SECTION("index selection") { constexpr size_t selectedIdx = 1; - std::tuple tuple; + std::tuple tuple; iterate_tuple(tuple, std::index_sequence{}, lambda); expected = {typeid(long)}; } + SECTION("index selection, reversed") { + std::tuple tuple; + iterate_tuple(tuple, std::index_sequence<1, 2>{}, lambda); + expected = {typeid(int), typeid(long)}; + } } SECTION("iterate_tuple with no tuple instance") { - auto lambda = [&types](auto* itemPointer) { + const auto lambda = [&types](auto* itemPointer) { using Item = std::remove_pointer_t; types.emplace_back(typeid(Item)); }; From 0a95bc346ffe2276b7a6b0fdc349d240e04685ea Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sat, 29 Mar 2025 13:01:19 +0500 Subject: [PATCH 148/170] added pragma logger tests --- dev/storage.h | 2 + dev/storage_base.h | 8 +- dev/storage_options.h | 20 +++ dev/util.h | 26 +--- include/sqlite_orm/sqlite_orm.h | 56 ++++--- tests/logger_tests.cpp | 255 ++++++++++++++++++++++++++++++++ 6 files changed, 325 insertions(+), 42 deletions(-) create mode 100644 tests/logger_tests.cpp diff --git a/dev/storage.h b/dev/storage.h index 6a3cc5a4b..e3e48e0e3 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -112,6 +112,8 @@ namespace sqlite_orm { storage_base{std::move(filename), storage_opt_or_default(options), storage_opt_or_default(options), + storage_opt_or_default(options), + storage_opt_or_default(options), foreign_keys_count()}, db_objects{std::move(dbObjects)} {} diff --git a/dev/storage_base.h b/dev/storage_base.h index 47ba3405c..3d76ef853 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -683,6 +683,8 @@ namespace sqlite_orm { storage_base(std::string filename, connection_control connectionCtrl, on_open_spec onOpenSpec, + will_run_query_spec willRunQuerySpec, + did_run_query_spec didRunQuerySpec, int foreignKeysCount) : on_open{std::move(onOpenSpec.onOpen)}, pragma(std::bind(&storage_base::get_connection, this), executor), limit(std::bind(&storage_base::get_connection, this)), @@ -690,7 +692,8 @@ namespace sqlite_orm { connection(std::make_unique( std::move(filename), std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), - cachedForeignKeysCount(foreignKeysCount) { + cachedForeignKeysCount(foreignKeysCount), + executor{std::move(willRunQuerySpec.willRunQuery), std::move(didRunQuerySpec.didRunQuery)} { if (this->inMemory) { this->connection->retain(); } @@ -706,7 +709,8 @@ namespace sqlite_orm { connection(std::make_unique( *other.connection, std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), - cachedForeignKeysCount(other.cachedForeignKeysCount) { + cachedForeignKeysCount(other.cachedForeignKeysCount), + executor{std::move(other.executor.will_run_query), std::move(other.executor.did_run_query)} { if (this->inMemory) { this->connection->retain(); } diff --git a/dev/storage_options.h b/dev/storage_options.h index 54f310087..91337c783 100644 --- a/dev/storage_options.h +++ b/dev/storage_options.h @@ -9,6 +9,8 @@ #include // std::function #endif +#include "serialize_result_type.h" + namespace sqlite_orm { namespace internal { template @@ -19,6 +21,16 @@ namespace sqlite_orm { std::function onOpen; }; + struct will_run_query_spec { + using storage_opt_tag = int; + + std::function willRunQuery; + }; + struct did_run_query_spec { + using storage_opt_tag = int; + + std::function didRunQuery; + }; } } @@ -47,4 +59,12 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { return {std::move(onOpen)}; } #endif + inline internal::will_run_query_spec + will_run_query(std::function willRunQuery) { + return {std::move(willRunQuery)}; + } + + inline internal::did_run_query_spec did_run_query(std::function didRunQuery) { + return {std::move(didRunQuery)}; + } } diff --git a/dev/util.h b/dev/util.h index d8ae8fb19..5ff98588b 100644 --- a/dev/util.h +++ b/dev/util.h @@ -60,45 +60,37 @@ namespace sqlite_orm { namespace internal { struct sqlite_executor { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - std::function will_run_query; - std::function did_run_query; -#endif - inline void perform_void_exec(sqlite3* db, const serialize_result_type& sql) const { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::function will_run_query; + std::function did_run_query; + + inline void perform_void_exec(sqlite3* db, serialize_arg_type sql) const { + if (this->will_run_query) { this->will_run_query(sql); } -#endif const int rc = sqlite3_exec(db, sql.data(), nullptr, nullptr, nullptr); if (rc != SQLITE_OK) { throw_translated_sqlite_error(db); } -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->did_run_query) { this->did_run_query(sql); } -#endif } inline void perform_exec(sqlite3* db, const char* sql, int (*callback)(void* data, int argc, char** argv, char**), void* user_data) const { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->will_run_query) { this->will_run_query(sql); } -#endif const int rc = sqlite3_exec(db, sql, callback, user_data, nullptr); if (rc != SQLITE_OK) { throw_translated_sqlite_error(db); } -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->did_run_query) { this->did_run_query(sql); } -#endif } inline void perform_exec(sqlite3* db, @@ -110,12 +102,10 @@ namespace sqlite_orm { template void perform_steps(sqlite3_stmt* stmt, L&& lambda) { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string_view sql = sqlite3_sql(stmt); + const auto sql = sqlite3_sql(stmt); if (this->will_run_query) { this->will_run_query(sql); } -#endif int rc; do { switch (rc = sqlite3_step(stmt)) { @@ -129,11 +119,9 @@ namespace sqlite_orm { } } } while (rc != SQLITE_DONE); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->did_run_query) { this->did_run_query(sql); } -#endif } }; @@ -144,7 +132,7 @@ namespace sqlite_orm { } // note: query is deliberately taken by value, such that it is thrown away early - inline sqlite3_stmt* prepare_stmt(sqlite3* db, const serialize_result_type& query) { + inline sqlite3_stmt* prepare_stmt(sqlite3* db, serialize_arg_type query) { sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db, query.data(), -1, &stmt, nullptr) != SQLITE_OK) { throw_translated_sqlite_error(db); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 97d2e0c38..c34556cff 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13802,45 +13802,37 @@ namespace sqlite_orm { namespace internal { struct sqlite_executor { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - std::function will_run_query; - std::function did_run_query; -#endif - inline void perform_void_exec(sqlite3* db, const serialize_result_type& sql) const { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::function will_run_query; + std::function did_run_query; + + inline void perform_void_exec(sqlite3* db, serialize_arg_type sql) const { + if (this->will_run_query) { this->will_run_query(sql); } -#endif const int rc = sqlite3_exec(db, sql.data(), nullptr, nullptr, nullptr); if (rc != SQLITE_OK) { throw_translated_sqlite_error(db); } -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->did_run_query) { this->did_run_query(sql); } -#endif } inline void perform_exec(sqlite3* db, const char* sql, int (*callback)(void* data, int argc, char** argv, char**), void* user_data) const { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->will_run_query) { this->will_run_query(sql); } -#endif const int rc = sqlite3_exec(db, sql, callback, user_data, nullptr); if (rc != SQLITE_OK) { throw_translated_sqlite_error(db); } -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->did_run_query) { this->did_run_query(sql); } -#endif } inline void perform_exec(sqlite3* db, @@ -13852,12 +13844,10 @@ namespace sqlite_orm { template void perform_steps(sqlite3_stmt* stmt, L&& lambda) { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string_view sql = sqlite3_sql(stmt); + const auto sql = sqlite3_sql(stmt); if (this->will_run_query) { this->will_run_query(sql); } -#endif int rc; do { switch (rc = sqlite3_step(stmt)) { @@ -13871,11 +13861,9 @@ namespace sqlite_orm { } } } while (rc != SQLITE_DONE); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->did_run_query) { this->did_run_query(sql); } -#endif } }; @@ -13886,7 +13874,7 @@ namespace sqlite_orm { } // note: query is deliberately taken by value, such that it is thrown away early - inline sqlite3_stmt* prepare_stmt(sqlite3* db, const serialize_result_type& query) { + inline sqlite3_stmt* prepare_stmt(sqlite3* db, serialize_arg_type query) { sqlite3_stmt* stmt; if (sqlite3_prepare_v2(db, query.data(), -1, &stmt, nullptr) != SQLITE_OK) { throw_translated_sqlite_error(db); @@ -17953,6 +17941,8 @@ namespace sqlite_orm { #include // std::function #endif +// #include "serialize_result_type.h" + namespace sqlite_orm { namespace internal { template @@ -17963,6 +17953,16 @@ namespace sqlite_orm { std::function onOpen; }; + struct will_run_query_spec { + using storage_opt_tag = int; + + std::function willRunQuery; + }; + struct did_run_query_spec { + using storage_opt_tag = int; + + std::function didRunQuery; + }; } } @@ -17991,6 +17991,14 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { return {std::move(onOpen)}; } #endif + inline internal::will_run_query_spec + will_run_query(std::function willRunQuery) { + return {std::move(willRunQuery)}; + } + + inline internal::did_run_query_spec did_run_query(std::function didRunQuery) { + return {std::move(didRunQuery)}; + } } namespace sqlite_orm { @@ -18640,6 +18648,8 @@ namespace sqlite_orm { storage_base(std::string filename, connection_control connectionCtrl, on_open_spec onOpenSpec, + will_run_query_spec willRunQuerySpec, + did_run_query_spec didRunQuerySpec, int foreignKeysCount) : on_open{std::move(onOpenSpec.onOpen)}, pragma(std::bind(&storage_base::get_connection, this), executor), limit(std::bind(&storage_base::get_connection, this)), @@ -18647,7 +18657,8 @@ namespace sqlite_orm { connection(std::make_unique( std::move(filename), std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), - cachedForeignKeysCount(foreignKeysCount) { + cachedForeignKeysCount(foreignKeysCount), + executor{std::move(willRunQuerySpec.willRunQuery), std::move(didRunQuerySpec.didRunQuery)} { if (this->inMemory) { this->connection->retain(); } @@ -18663,7 +18674,8 @@ namespace sqlite_orm { connection(std::make_unique( *other.connection, std::bind(&storage_base::on_open_internal, this, std::placeholders::_1))), - cachedForeignKeysCount(other.cachedForeignKeysCount) { + cachedForeignKeysCount(other.cachedForeignKeysCount), + executor{std::move(other.executor.will_run_query), std::move(other.executor.did_run_query)} { if (this->inMemory) { this->connection->retain(); } @@ -22794,6 +22806,8 @@ namespace sqlite_orm { storage_base{std::move(filename), storage_opt_or_default(options), storage_opt_or_default(options), + storage_opt_or_default(options), + storage_opt_or_default(options), foreign_keys_count()}, db_objects{std::move(dbObjects)} {} diff --git a/tests/logger_tests.cpp b/tests/logger_tests.cpp new file mode 100644 index 000000000..f9ba5002f --- /dev/null +++ b/tests/logger_tests.cpp @@ -0,0 +1,255 @@ +#include +#include + +using namespace sqlite_orm; + +struct WillLogsCollector { + static std::vector logs; + + void operator()(const std::string_view log) { + this->logs.push_back(std::string(log)); + } +}; + +std::vector WillLogsCollector::logs; + +struct DidLogsCollector { + static std::vector logs; + + void operator()(const std::string_view log) { + this->logs.push_back(std::string(log)); + } +}; + +std::vector DidLogsCollector::logs; + +TEST_CASE("logger") { + using Logs = std::vector; + using Callback = std::function; + + Logs expectedWillLogs; + Logs expectedDidLogs; + + auto willRunQuery = GENERATE(Callback(WillLogsCollector()), Callback()); + auto didRunQuery = GENERATE(Callback(DidLogsCollector()), Callback()); + + struct User { + int id = 0; + std::string name; + }; + struct Visit { + int id = 0; + int userId = 0; + std::string date; + }; + + auto pushExpected = + [&willRunQuery, &didRunQuery, &expectedWillLogs, &expectedDidLogs](const std::string& expected) { + if (willRunQuery) { + expectedWillLogs.push_back(expected); + } + if (didRunQuery) { + expectedDidLogs.push_back(expected); + } + }; + + auto storage = + make_storage("", + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), + make_table("visits", + make_column("id", &Visit::id, primary_key()), + make_column("user_id", &Visit::userId), + make_column("date", &Visit::date)), + will_run_query(std::move(willRunQuery)), + did_run_query(std::move(didRunQuery))); + storage.sync_schema(); + + WillLogsCollector::logs.clear(); + DidLogsCollector::logs.clear(); + + SECTION("application_id") { + SECTION("get") { + std::ignore = storage.pragma.application_id(); + pushExpected("PRAGMA application_id"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({{2, "PRAGMA application_id = 2"}, + {3, "PRAGMA application_id = 3"}, + {4, "PRAGMA application_id = 4"}})); + storage.pragma.application_id(value); + pushExpected(expected); + } + } + SECTION("module_list") { + std::ignore = storage.pragma.module_list(); + pushExpected("PRAGMA module_list"); + } + SECTION("recursive_triggers") { + SECTION("get") { + std::ignore = storage.pragma.recursive_triggers(); + pushExpected("PRAGMA recursive_triggers"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {true, "PRAGMA recursive_triggers = 1"}, + {false, "PRAGMA recursive_triggers = 0"}, + })); + storage.pragma.recursive_triggers(value); + pushExpected(expected); + } + } + SECTION("busy_timeout") { + SECTION("get") { + std::ignore = storage.pragma.busy_timeout(); + pushExpected("PRAGMA busy_timeout"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {1, "PRAGMA busy_timeout = 1"}, + {2, "PRAGMA busy_timeout = 2"}, + {3, "PRAGMA busy_timeout = 3"}, + })); + storage.pragma.busy_timeout(value); + pushExpected(expected); + } + } + SECTION("locking_mode") { + SECTION("get") { + std::ignore = storage.pragma.locking_mode(); + pushExpected("PRAGMA locking_mode"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {locking_mode::NORMAL, "PRAGMA locking_mode = NORMAL"}, + {locking_mode::EXCLUSIVE, "PRAGMA locking_mode = EXCLUSIVE"}, + })); + storage.pragma.locking_mode(value); + pushExpected(expected); + } + } + SECTION("journal_mode") { + SECTION("get") { + std::ignore = storage.pragma.journal_mode(); + pushExpected("PRAGMA journal_mode"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {journal_mode::DELETE_, "PRAGMA journal_mode = DELETE"}, + {journal_mode::TRUNCATE, "PRAGMA journal_mode = TRUNCATE"}, + {journal_mode::PERSIST, "PRAGMA journal_mode = PERSIST"}, + {journal_mode::MEMORY, "PRAGMA journal_mode = MEMORY"}, + {journal_mode::WAL, "PRAGMA journal_mode = WAL"}, + {journal_mode::OFF, "PRAGMA journal_mode = OFF"}, + })); + storage.pragma.journal_mode(value); + pushExpected(expected); + } + } + SECTION("synchronous") { + SECTION("get") { + std::ignore = storage.pragma.synchronous(); + pushExpected("PRAGMA synchronous"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {0, "PRAGMA synchronous = 0"}, + {1, "PRAGMA synchronous = 1"}, + {2, "PRAGMA synchronous = 2"}, + {3, "PRAGMA synchronous = 3"}, + })); + storage.pragma.synchronous(value); + pushExpected(expected); + } + } + SECTION("user_version") { + SECTION("get") { + std::ignore = storage.pragma.user_version(); + pushExpected("PRAGMA user_version"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {0, "PRAGMA user_version = 0"}, + {1, "PRAGMA user_version = 1"}, + {2, "PRAGMA user_version = 2"}, + {3, "PRAGMA user_version = 3"}, + {4, "PRAGMA user_version = 4"}, + })); + storage.pragma.user_version(value); + pushExpected(expected); + } + } + SECTION("auto_vacuum") { + SECTION("get") { + std::ignore = storage.pragma.auto_vacuum(); + pushExpected("PRAGMA auto_vacuum"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {0, "PRAGMA auto_vacuum = 0"}, + {1, "PRAGMA auto_vacuum = 1"}, + {2, "PRAGMA auto_vacuum = 2"}, + })); + storage.pragma.auto_vacuum(value); + pushExpected(expected); + } + } + SECTION("max_page_count") { + SECTION("get") { + std::ignore = storage.pragma.max_page_count(); + pushExpected("PRAGMA max_page_count"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {10, "PRAGMA max_page_count = 10"}, + {20, "PRAGMA max_page_count = 20"}, + {30, "PRAGMA max_page_count = 30"}, + })); + storage.pragma.max_page_count(value); + pushExpected(expected); + } + } + SECTION("integrity_check") { + SECTION("get") { + std::ignore = storage.pragma.integrity_check(); + pushExpected("PRAGMA integrity_check"); + } + SECTION("set table-name") { + const auto [value, expected] = GENERATE(table({ + {"users", "PRAGMA integrity_check(users)"}, + {"visits", "PRAGMA integrity_check(visits)"}, + })); + storage.pragma.integrity_check(value); + pushExpected(expected); + } + SECTION("set N") { + const auto [value, expected] = GENERATE(table({ + {1, "PRAGMA integrity_check(1)"}, + {2, "PRAGMA integrity_check(2)"}, + })); + storage.pragma.integrity_check(value); + pushExpected(expected); + } + } + SECTION("quick_check") { + std::ignore = storage.pragma.quick_check(); + pushExpected("PRAGMA quick_check"); + } + SECTION("table_xinfo") { + const auto [value, expected] = GENERATE(table({ + {"users", R"(PRAGMA table_xinfo("users"))"}, + {"visits", R"(PRAGMA table_xinfo("visits"))"}, + })); + std::ignore = storage.pragma.table_xinfo(value); + pushExpected(expected); + } + SECTION("table_info") { + const auto [value, expected] = GENERATE(table({ + {"users", R"(PRAGMA table_info("users"))"}, + {"visits", R"(PRAGMA table_info("visits"))"}, + })); + std::ignore = storage.pragma.table_info(value); + pushExpected(expected); + } + REQUIRE(WillLogsCollector::logs == expectedWillLogs); + REQUIRE(DidLogsCollector::logs == expectedDidLogs); +} From 7029ef26bc8152da1a0745770f90bbb1485fca77 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sat, 29 Mar 2025 14:14:55 +0500 Subject: [PATCH 149/170] code format --- dev/storage.h | 4 +- include/sqlite_orm/sqlite_orm.h | 231 ++------------------------------ 2 files changed, 11 insertions(+), 224 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index 6f7bf3715..56469927a 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -111,8 +111,8 @@ namespace sqlite_orm { void operator()(polyfill::bool_constant, const function_call& udfCall) const { auto&& name = udfCall.name(); SQLITE_ORM_CPP_UNLIKELY { - if (!_contains(_scalarFunctions, name) && !_contains(_aggregateFunctions, name)) - throw std::system_error{orm_error_code::function_not_found, std::string(name)}; + if (!_contains(_scalarFunctions, name) && !_contains(_aggregateFunctions, name)) + throw std::system_error{orm_error_code::function_not_found, std::string(name)}; } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 1b8cd90f0..dc2b49c02 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13,7 +13,6 @@ __pragma(push_macro("max")) // #include "cxx_universal.h" - /* * This header makes central C++ functionality on which sqlite_orm depends universally available: * - alternative operator representations @@ -36,7 +35,6 @@ using std::nullptr_t; // #include "cxx_check_prerequisites.h" - /* * This header detects missing core C++ language features on which sqlite_orm depends, bailing out with a hard error. */ @@ -51,7 +49,6 @@ using std::nullptr_t; // #include "cxx_core_features.h" - /* * This header detects core C++ language features. * May be updated/overwritten by cxx_compiler_quirks.h @@ -148,7 +145,6 @@ using std::nullptr_t; // #include "cxx_compiler_quirks.h" - /* * This header defines macros for circumventing compiler quirks on which sqlite_orm depends. * May amend cxx_core_features.h @@ -212,8 +208,6 @@ using std::nullptr_t; #define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS #endif - - #ifdef BUILD_SQLITE_ORM_MODULE #define SQLITE_ORM_EXPORT export #else @@ -321,10 +315,8 @@ namespace sqlite_orm { #endif // #include "functional/cxx_optional.h" - // #include "cxx_core_features.h" - #ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else @@ -337,10 +329,8 @@ namespace sqlite_orm { #define SQLITE_ORM_OPTIONAL_SUPPORTED #endif - // #include "functional/cxx_type_traits_polyfill.h" - #ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else @@ -349,7 +339,6 @@ namespace sqlite_orm { // #include "mpl/conditional.h" - namespace sqlite_orm { namespace internal { namespace mpl { @@ -383,7 +372,6 @@ namespace sqlite_orm { namespace mpl = internal::mpl; } - namespace sqlite_orm { namespace internal { namespace polyfill { @@ -534,7 +522,6 @@ namespace sqlite_orm { // #include "functional/cxx_functional_polyfill.h" - #ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else @@ -549,14 +536,12 @@ namespace sqlite_orm { // #include "../member_traits/member_traits.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_function, std::true_type, std::false_type #endif // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { // SFINAE friendly trait to get a member object pointer's field type @@ -660,7 +645,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { namespace polyfill { @@ -757,7 +741,6 @@ namespace sqlite_orm { // #include "functional/static_magic.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED #include // std::false_type, std::true_type, std::integral_constant @@ -840,7 +823,6 @@ namespace sqlite_orm { // #include "functional/mpl.h" - /* * Symbols for 'template metaprogramming' (compile-time template programming), * inspired by the MPL of Aleksey Gurtovoy and David Abrahams, and the Mp11 of Peter Dimov and Bjorn Reese. @@ -878,7 +860,6 @@ namespace sqlite_orm { // #include "mpl/conditional.h" - namespace sqlite_orm { namespace internal { namespace mpl { @@ -1373,12 +1354,10 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_traits.h" - // #include "../functional/cxx_type_traits_polyfill.h" // #include "../functional/mpl.h" - namespace sqlite_orm { // convenience metafunction algorithms namespace internal { @@ -1439,7 +1418,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_filter.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant, std::index_sequence, std::conditional, std::declval #include // std::tuple, std::tuple_cat, std::tuple_element @@ -1449,7 +1427,6 @@ namespace sqlite_orm { // #include "../functional/index_sequence_util.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence #endif @@ -1519,7 +1496,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -1609,7 +1585,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_transformer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval #include // std::tuple_size, std::get @@ -1621,7 +1596,6 @@ namespace sqlite_orm { // #include "../functional/mpl.h" - namespace sqlite_orm { namespace internal { @@ -1748,7 +1722,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_iteration.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::get, std::tuple_element, std::tuple_size #include // std::index_sequence, std::make_index_sequence @@ -1839,7 +1812,6 @@ namespace sqlite_orm { // #include "type_traits.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::is_empty, std::is_aggregate #if __cpp_lib_unwrap_ref >= 201811L @@ -1853,7 +1825,6 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { // C++ generic traits used throughout the library namespace internal { @@ -1991,7 +1962,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "alias.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same #include // std::make_index_sequence, std::move @@ -2008,7 +1978,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "functional/cstring_literal.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include // std::index_sequence @@ -2046,7 +2015,6 @@ namespace sqlite_orm::internal { // #include "alias_traits.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::is_same #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -2060,14 +2028,12 @@ namespace sqlite_orm::internal { // #include "table_reference.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const, std::type_identity #endif // #include "functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { /* @@ -2117,7 +2083,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #endif } - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Base class for a custom table alias, column alias or expression alias. @@ -2238,14 +2203,12 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "field_of.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_convertible, std::bool_constant #endif // #include "member_traits/member_traits.h" - namespace sqlite_orm { namespace internal { template @@ -2298,7 +2261,6 @@ namespace sqlite_orm { // #include "table_type_of.h" - namespace sqlite_orm { namespace internal { @@ -2342,10 +2304,8 @@ namespace sqlite_orm { // #include "tags.h" - // #include "functional/cxx_functional_polyfill.h" - namespace sqlite_orm { namespace internal { struct negatable_t {}; @@ -2373,7 +2333,6 @@ namespace sqlite_orm { // #include "column_pointer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_convertible #include // std::move @@ -2391,7 +2350,6 @@ namespace sqlite_orm { // #include "tags.h" - namespace sqlite_orm { namespace internal { /** @@ -3023,7 +2981,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "error_code.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::error_code, std::system_error @@ -3197,7 +3154,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "type_printer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::shared_ptr, std::unique_ptr @@ -3205,14 +3161,12 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #endif // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "type_traits.h" // #include "is_std_ptr.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include @@ -3245,7 +3199,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { }; } - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -3320,7 +3273,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "constraints.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::false_type, std::true_type #include // std::system_error @@ -3335,7 +3287,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "tuple_helper/same_or_void.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::common_type #endif @@ -3391,7 +3342,6 @@ namespace sqlite_orm { // #include "collate_argument.h" - namespace sqlite_orm { namespace internal { @@ -3414,7 +3364,6 @@ namespace sqlite_orm { // #include "table_reference.h" - namespace sqlite_orm { namespace internal { @@ -4100,7 +4049,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "field_printer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::stringstream @@ -4120,7 +4068,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "type_traits.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -4277,7 +4224,6 @@ namespace sqlite_orm { // #include "rowid.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -4352,7 +4298,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "operators.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move @@ -4362,7 +4307,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "is_base_of_template.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::declval #endif @@ -4406,13 +4350,10 @@ namespace sqlite_orm { // #include "serialize_result_type.h" - // #include "functional/cxx_string_view.h" - // #include "cxx_core_features.h" - #ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else @@ -4443,7 +4384,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -4737,7 +4677,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "select_constraints.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include @@ -4749,7 +4688,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #endif // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "is_base_of_template.h" @@ -4762,7 +4700,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "optional_container.h" - namespace sqlite_orm { namespace internal { @@ -4797,7 +4734,6 @@ namespace sqlite_orm { // #include "ast/where.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move @@ -4807,7 +4743,6 @@ namespace sqlite_orm { // #include "../serialize_result_type.h" - namespace sqlite_orm { namespace internal { @@ -4857,7 +4792,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/group_by.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple #include // std::true_type, std::false_type @@ -4866,7 +4800,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { @@ -4913,7 +4846,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "core_functions.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::make_tuple, std::tuple_size @@ -4932,7 +4864,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "conditions.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::enable_if, std::is_same, std::remove_const @@ -4957,7 +4888,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "serializer_context.h" - namespace sqlite_orm { namespace internal { @@ -5005,7 +4935,6 @@ namespace sqlite_orm { // #include "expression.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::enable_if @@ -5013,14 +4942,12 @@ namespace sqlite_orm { #endif // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "tags.h" // #include "operators.h" - namespace sqlite_orm { namespace internal { @@ -5123,7 +5050,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "literal.h" - namespace sqlite_orm { namespace internal { @@ -5140,7 +5066,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -6403,10 +6328,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/into.h" - // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { @@ -6427,7 +6350,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -8576,7 +8498,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "cte_moniker.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -8593,7 +8514,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "alias.h" - #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) namespace sqlite_orm { @@ -8673,7 +8593,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "schema/column.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple #include // std::string @@ -8694,17 +8613,14 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../type_is_nullable.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type, std::enable_if #include // std::shared_ptr, std::unique_ptr #endif // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -8740,7 +8656,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../constraints.h" - namespace sqlite_orm { namespace internal { @@ -8924,7 +8839,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -9693,7 +9607,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "statement_binder.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence @@ -9726,14 +9639,12 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "arithmetic_tag.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_integral #endif // #include "functional/mpl/conditional.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -9754,7 +9665,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "xdestroy_handling.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED @@ -9764,7 +9674,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { using xdestroy_fn_t = void (*)(void*); @@ -10008,7 +9917,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "pointer_value.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3020000 #include @@ -10024,7 +9932,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "xdestroy_handling.h" - #if SQLITE_VERSION_NUMBER >= 3020000 namespace sqlite_orm { namespace internal { @@ -10314,7 +10221,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } #endif - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -10662,7 +10568,6 @@ namespace sqlite_orm { // #include "column_result.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of #include // std::reference_wrapper @@ -10676,7 +10581,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_fy.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif @@ -10711,7 +10615,6 @@ namespace sqlite_orm { // #include "mapped_type_proxy.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const #endif @@ -10722,7 +10625,6 @@ namespace sqlite_orm { // #include "alias_traits.h" - namespace sqlite_orm { namespace internal { @@ -10757,12 +10659,10 @@ namespace sqlite_orm { // #include "column_result_proxy.h" - // #include "type_traits.h" // #include "table_reference.h" - namespace sqlite_orm { namespace internal { @@ -10804,7 +10704,6 @@ namespace sqlite_orm { // #include "cte_types.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include @@ -10818,7 +10717,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_fy.h" - #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) namespace sqlite_orm { @@ -10872,7 +10770,6 @@ namespace sqlite_orm { // #include "storage_traits.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple #endif @@ -10887,7 +10784,6 @@ namespace sqlite_orm { // #include "storage_lookup.h" - #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 @@ -10898,7 +10794,6 @@ namespace sqlite_orm { // #include "type_traits.h" - namespace sqlite_orm { namespace internal { @@ -11046,7 +10941,6 @@ namespace sqlite_orm { // #include "schema/column.h" - namespace sqlite_orm { namespace internal { @@ -11096,7 +10990,6 @@ namespace sqlite_orm { // #include "function.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_member_function_pointer, std::is_function, std::remove_const, std::decay, std::is_convertible, std::is_same, std::false_type, std::true_type #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED @@ -11113,12 +11006,10 @@ namespace sqlite_orm { // #include "functional/function_traits.h" - // #include "cxx_type_traits_polyfill.h" // #include "mpl.h" - namespace sqlite_orm { namespace internal { /* @@ -11203,7 +11094,6 @@ namespace sqlite_orm { // #include "tags.h" - namespace sqlite_orm { struct arg_values; @@ -11736,7 +11626,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/special_keywords.h" - namespace sqlite_orm { namespace internal { struct current_time_t {}; @@ -12087,7 +11976,6 @@ namespace sqlite_orm { // #include "sync_schema_result.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif @@ -12152,7 +12040,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "table_info.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move @@ -12205,7 +12092,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "storage_impl.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -12230,7 +12116,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "schema/table.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::remove_const, std::is_member_pointer, std::true_type, std::false_type @@ -12271,7 +12156,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "index.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t #include // std::string @@ -12282,7 +12166,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../indexed_column.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move @@ -12290,7 +12173,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/where.h" - namespace sqlite_orm { namespace internal { @@ -12352,7 +12234,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../table_type_of.h" - namespace sqlite_orm { namespace internal { @@ -12414,7 +12295,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "column.h" - namespace sqlite_orm { namespace internal { @@ -12898,7 +12778,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "storage_lookup.h" - // interface functions namespace sqlite_orm { namespace internal { @@ -13008,7 +12887,6 @@ namespace sqlite_orm { // #include "journal_mode.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string @@ -13019,7 +12897,6 @@ namespace sqlite_orm { // #include "serialize_result_type.h" - #if defined(_WINNT_) // DELETE is a macro defined in the Windows SDK (winnt.h) #pragma push_macro("DELETE") @@ -13102,7 +12979,6 @@ namespace sqlite_orm { // #include "mapped_view.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::forward, std::move @@ -13110,7 +12986,6 @@ namespace sqlite_orm { // #include "row_extractor.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if @@ -13147,7 +13022,6 @@ namespace sqlite_orm { // #include "locking_mode.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string @@ -13158,7 +13032,6 @@ namespace sqlite_orm { // #include "serialize_result_type.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { enum class locking_mode : signed char { NORMAL = 0, @@ -13214,7 +13087,6 @@ namespace sqlite_orm { // #include "type_traits.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -13772,7 +13644,6 @@ namespace sqlite_orm { // #include "mapped_iterator.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::shared_ptr, std::make_shared @@ -13784,7 +13655,6 @@ namespace sqlite_orm { // #include "statement_finalizer.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr @@ -13804,7 +13674,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "object_from_column_builder.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_member_object_pointer @@ -13823,7 +13692,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "storage_lookup.h" - namespace sqlite_orm { namespace internal { @@ -13894,7 +13762,6 @@ namespace sqlite_orm { // #include "util.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string @@ -13909,7 +13776,6 @@ namespace sqlite_orm { // #include "serialize_result_type.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -14061,7 +13927,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -14168,7 +14033,6 @@ namespace sqlite_orm { // #include "ast_iterator.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_invocable #include // std::vector @@ -14176,7 +14040,7 @@ namespace sqlite_orm { #endif // #include "functional/cxx_functional_polyfill.h" - // std::is_invocable +// std::is_invocable // #include "tuple_helper/tuple_iteration.h" // #include "functional/static_magic.h" @@ -14195,7 +14059,6 @@ namespace sqlite_orm { // #include "prepared_statement.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr @@ -14214,7 +14077,6 @@ namespace sqlite_orm { // #include "connection_holder.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include @@ -14224,7 +14086,6 @@ namespace sqlite_orm { // #include "error_code.h" - namespace sqlite_orm { namespace internal { @@ -14337,7 +14198,6 @@ namespace sqlite_orm { // #include "values.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::vector #include // std::tuple @@ -14346,7 +14206,6 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { @@ -14390,7 +14249,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/upsert_clause.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3024000 #include // std::tuple @@ -14400,7 +14258,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { #if SQLITE_VERSION_NUMBER >= 3024000 @@ -14470,7 +14327,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/set.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::tuple_size #include // std::string @@ -14483,7 +14339,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../table_name_collector.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::set #include // std::string @@ -14504,7 +14359,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "storage_lookup.h" - namespace sqlite_orm { namespace internal { @@ -14593,7 +14447,6 @@ namespace sqlite_orm { } - namespace sqlite_orm { namespace internal { @@ -14696,7 +14549,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -15504,7 +15356,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/excluded.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #endif @@ -15538,14 +15389,12 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/exists.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #endif // #include "../tags.h" - namespace sqlite_orm { namespace internal { @@ -15580,7 +15429,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/match.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif @@ -15605,7 +15453,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -16327,7 +16174,6 @@ namespace sqlite_orm { // #include "util.h" - namespace sqlite_orm { namespace internal { @@ -16393,7 +16239,6 @@ inline constexpr bool std::ranges::enable_borrowed_range #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move, std::remove_cvref @@ -16410,7 +16255,6 @@ inline constexpr bool std::ranges::enable_borrowed_range #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move @@ -16426,7 +16270,6 @@ inline constexpr bool std::ranges::enable_borrowed_range #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // atoi @@ -16604,7 +16445,6 @@ inline constexpr bool std::ranges::enable_borrowed_range #else @@ -16615,7 +16455,7 @@ inline constexpr bool std::ranges::enable_borrowed_range #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string @@ -16671,7 +16510,6 @@ namespace sqlite_orm { // #include "serializing_util.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence, std::remove_cvref #include @@ -16682,7 +16520,7 @@ namespace sqlite_orm { #endif // #include "functional/cxx_type_traits_polyfill.h" - // std::remove_cvref, polyfill::is_detected +// std::remove_cvref, polyfill::is_detected // #include "functional/cxx_functional_polyfill.h" // #include "tuple_helper/tuple_iteration.h" @@ -16699,7 +16537,6 @@ namespace sqlite_orm { // #include "schema/column.h" - namespace sqlite_orm { namespace internal { template @@ -17120,7 +16957,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -17386,7 +17222,6 @@ namespace sqlite_orm { // #include "limit_accessor.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::map @@ -17396,7 +17231,6 @@ namespace sqlite_orm { // #include "connection_holder.h" - namespace sqlite_orm { namespace internal { @@ -17530,7 +17364,6 @@ namespace sqlite_orm { // #include "transaction_guard.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::function #include // std::move @@ -17538,7 +17371,6 @@ namespace sqlite_orm { // #include "connection_holder.h" - namespace sqlite_orm { namespace internal { @@ -17619,7 +17451,6 @@ namespace sqlite_orm { // #include "backup.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::system_error @@ -17632,7 +17463,6 @@ namespace sqlite_orm { // #include "connection_holder.h" - namespace sqlite_orm { namespace internal { @@ -17700,7 +17530,6 @@ namespace sqlite_orm { // #include "values_to_tuple.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::index_sequence, std::make_index_sequence @@ -17715,12 +17544,10 @@ namespace sqlite_orm { // #include "arg_values.h" - #include // #include "row_extractor.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Wrapper around a dynamically typed value object. @@ -17866,7 +17693,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { }; } - namespace sqlite_orm { namespace internal { @@ -17906,7 +17732,6 @@ namespace sqlite_orm { // #include "udf_proxy.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // assert macro @@ -17920,7 +17745,6 @@ namespace sqlite_orm { // #include "error_code.h" - namespace sqlite_orm { namespace internal { /* @@ -18146,7 +17970,6 @@ namespace sqlite_orm { // #include "storage_options.h" - #include #ifdef SQLITE_ORM_IMPORT_STD_MODULE #include @@ -18158,7 +17981,6 @@ namespace sqlite_orm { // #include "serialize_result_type.h" - namespace sqlite_orm { namespace internal { template @@ -18217,7 +18039,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -19259,7 +19080,6 @@ namespace sqlite_orm { // #include "expression_object_type.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::decay, std::remove_reference #include // std::reference_wrapper @@ -19269,7 +19089,6 @@ namespace sqlite_orm { // #include "prepared_statement.h" - namespace sqlite_orm { namespace internal { @@ -19380,7 +19199,6 @@ namespace sqlite_orm { // #include "statement_serializer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::remove_pointer, std::remove_reference, std::remove_cvref, std::disjunction #include // std::stringstream @@ -19398,11 +19216,10 @@ namespace sqlite_orm { // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" - // std::remove_cvref, std::disjunction +// std::remove_cvref, std::disjunction // #include "functional/cxx_functional_polyfill.h" - // std::identity, std::invoke +// std::identity, std::invoke // #include "functional/mpl.h" // #include "tuple_helper/tuple_filter.h" @@ -19419,7 +19236,6 @@ namespace sqlite_orm { // #include "ast/rank.h" - namespace sqlite_orm { namespace internal { struct rank_t {}; @@ -19462,7 +19278,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "column_names_getter.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of #include // std::string @@ -19485,12 +19300,11 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "select_constraints.h" // #include "storage_lookup.h" - // pick_table +// pick_table // #include "serializer_context.h" // #include "util.h" - namespace sqlite_orm { namespace internal { @@ -19606,7 +19420,6 @@ namespace sqlite_orm { // #include "cte_column_names_collector.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include @@ -19630,7 +19443,6 @@ namespace sqlite_orm { // #include "serializer_context.h" - #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) namespace sqlite_orm { namespace internal { @@ -19835,7 +19647,6 @@ namespace sqlite_orm { // #include "order_by_serializer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::string @@ -19929,7 +19740,6 @@ namespace sqlite_orm { // #include "schema/triggers.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include @@ -19939,7 +19749,6 @@ namespace sqlite_orm { // #include "../optional_container.h" - // NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? // (Could be implemented with a normal trigger that insert or update an internal table and then retreive // the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) @@ -20224,7 +20033,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "schema/table.h" - namespace sqlite_orm { namespace internal { @@ -22532,7 +22340,6 @@ namespace sqlite_orm { // #include "cte_storage.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include @@ -22560,7 +22367,6 @@ namespace sqlite_orm { // #include "column_expression.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic #include // std::tuple @@ -22579,7 +22385,6 @@ namespace sqlite_orm { // #include "storage_traits.h" - namespace sqlite_orm { namespace internal { @@ -22678,7 +22483,6 @@ namespace sqlite_orm { // #include "storage_lookup.h" - namespace sqlite_orm { namespace internal { @@ -22989,7 +22793,6 @@ namespace sqlite_orm { // #include "serializing_util.h" - namespace sqlite_orm { namespace internal { @@ -23040,8 +22843,8 @@ namespace sqlite_orm { void operator()(polyfill::bool_constant, const function_call& udfCall) const { auto&& name = udfCall.name(); SQLITE_ORM_CPP_UNLIKELY { - if (!_contains(_scalarFunctions, name) && !_contains(_aggregateFunctions, name)) - throw std::system_error{orm_error_code::function_not_found, std::string(name)}; + if (!_contains(_scalarFunctions, name) && !_contains(_aggregateFunctions, name)) + throw std::system_error{orm_error_code::function_not_found, std::string(name)}; } } @@ -24946,7 +24749,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { * this file is also used to provide definitions of interface methods 'hitting the database'. */ - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::make_unique #endif @@ -24957,7 +24759,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../default_value_extractor.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -24968,7 +24769,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "storage_lookup.h" - namespace sqlite_orm { namespace internal { @@ -24992,7 +24792,6 @@ namespace sqlite_orm { // #include "../schema/column.h" - namespace sqlite_orm { namespace internal { @@ -25019,7 +24818,6 @@ namespace sqlite_orm { * this file is also used to provide definitions of interface methods 'hitting the database'. */ - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference #include // std::move @@ -25036,7 +24834,6 @@ namespace sqlite_orm { // #include "../schema/table.h" - namespace sqlite_orm { namespace internal { @@ -25096,7 +24893,6 @@ namespace sqlite_orm { * e.g. usage of the dbstat table. */ - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_same #include // std::stringstream @@ -25109,7 +24905,6 @@ namespace sqlite_orm { // #include "../sqlite_schema_table.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -25122,7 +24917,6 @@ namespace sqlite_orm { // #include "alias.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** * SQLite's "schema table" that stores the schema for a database. @@ -25162,7 +24956,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../eponymous_vtabs/dbstat.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ENABLE_DBSTAT_VTAB #include // std::string @@ -25175,7 +24968,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../column_pointer.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB struct dbstat { @@ -25219,7 +25011,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../storage.h" - namespace sqlite_orm { namespace internal { @@ -25375,7 +25166,6 @@ namespace sqlite_orm { // #include "node_tuple.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if #include // std::tuple @@ -25417,7 +25207,6 @@ namespace sqlite_orm { // #include "ast/match.h" - namespace sqlite_orm { namespace internal { @@ -25657,7 +25446,6 @@ namespace sqlite_orm { // #include "expression_object_type.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { template @@ -25842,7 +25630,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "pointer_value.h" - #if SQLITE_VERSION_NUMBER >= 3020000 #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED SQLITE_ORM_EXPORT namespace sqlite_orm { From 631171416dfe180203350ccf79d033d53db4916d Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sat, 29 Mar 2025 15:58:44 +0500 Subject: [PATCH 150/170] fixed connection name --- dev/storage.h | 2 +- include/sqlite_orm/sqlite_orm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index 56469927a..4b2be5f17 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1285,7 +1285,7 @@ namespace sqlite_orm { auto conection = this->get_connection(); const std::string sql = serialize(statement, context); sqlite3_stmt* stmt = prepare_stmt(conection.get(), sql); - return prepared_statement_t{std::forward(statement), stmt, std::move(con)}; + return prepared_statement_t{std::forward(statement), stmt, std::move(conection)}; } public: diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index dc2b49c02..f594a4f70 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -24017,7 +24017,7 @@ namespace sqlite_orm { auto conection = this->get_connection(); const std::string sql = serialize(statement, context); sqlite3_stmt* stmt = prepare_stmt(conection.get(), sql); - return prepared_statement_t{std::forward(statement), stmt, std::move(con)}; + return prepared_statement_t{std::forward(statement), stmt, std::move(conection)}; } public: From a96f29b1539487dfbdcfd1189b90d32666b5bbd7 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 29 Mar 2025 13:03:16 +0200 Subject: [PATCH 151/170] Unlike msvc, gcc+clang require NTTPs to be copyable --- tests/user_defined_functions.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index 320b43d82..0124c7d62 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -615,14 +615,17 @@ TEST_CASE("generalized scalar udf") { storage.delete_scalar_function(); } SECTION("non-copyable function object") { - constexpr auto idfunc_f = "idfunc"_scalar.quote(); - storage.create_scalar_function(); + // note: unlike msvc, gcc+clang require the constant template parameter to be copyable (and probably rightly so); + // so we have to go through the builder to create/delete the quoted function. + constexpr auto idfunc_builder = "idfunc"_scalar; + constexpr auto idfunc_f = idfunc_builder.quote(); + storage.create_scalar_function()>(); { auto rows = storage.select(idfunc_f(1)); decltype(rows) expected{1}; REQUIRE(rows == expected); } - storage.delete_scalar_function(); + storage.delete_scalar_function()>(); } SECTION("stateful function object") { constexpr auto offset0_f = "offset0"_scalar.quote(offset0); From 1df8021086ae4b34cca1ceb907c0ec6b5141aae9 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sat, 29 Mar 2025 16:28:06 +0500 Subject: [PATCH 152/170] fixed sqlite version --- dev/connection_holder.h | 2 +- include/sqlite_orm/sqlite_orm.h | 2 +- tests/constraints/unique.cpp | 2 +- tests/prepared_statement_tests/insert_explicit.cpp | 2 +- tests/schema/index_tests.cpp | 2 +- tests/storage_non_crud_tests.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index bf076c9ef..850978c0e 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -29,7 +29,7 @@ namespace sqlite_orm { int rc = sqlite3_open_v2(this->filename.c_str(), &this->db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE -#if SQLITE_VERSION_NUMBER >= 3008008 +#if SQLITE_VERSION_NUMBER >= 3037002 | SQLITE_OPEN_EXRESCODE, #else | 0, diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index f594a4f70..d9cb9d972 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -14106,7 +14106,7 @@ namespace sqlite_orm { int rc = sqlite3_open_v2(this->filename.c_str(), &this->db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE -#if SQLITE_VERSION_NUMBER >= 3008008 +#if SQLITE_VERSION_NUMBER >= 3037002 | SQLITE_OPEN_EXRESCODE, #else | 0, diff --git a/tests/constraints/unique.cpp b/tests/constraints/unique.cpp index 4d9859bd3..dd78c499e 100644 --- a/tests/constraints/unique.cpp +++ b/tests/constraints/unique.cpp @@ -5,7 +5,7 @@ using namespace sqlite_orm; TEST_CASE("Unique") { -#if SQLITE_VERSION_NUMBER >= 3008008 +#if SQLITE_VERSION_NUMBER >= 3037002 const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_UNIQUE)); #else const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); diff --git a/tests/prepared_statement_tests/insert_explicit.cpp b/tests/prepared_statement_tests/insert_explicit.cpp index 7b32cbbeb..17af43db9 100644 --- a/tests/prepared_statement_tests/insert_explicit.cpp +++ b/tests/prepared_statement_tests/insert_explicit.cpp @@ -10,7 +10,7 @@ using namespace sqlite_orm; TEST_CASE("Prepared insert explicit") { using namespace PreparedStatementTests; using Catch::Matchers::UnorderedEquals; -#if SQLITE_VERSION_NUMBER >= 3008008 +#if SQLITE_VERSION_NUMBER >= 3037002 const ErrorCodeExceptionMatcher primaryKeyExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_PRIMARYKEY)); #else const ErrorCodeExceptionMatcher primaryKeyExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); diff --git a/tests/schema/index_tests.cpp b/tests/schema/index_tests.cpp index b672596d5..7f1158488 100644 --- a/tests/schema/index_tests.cpp +++ b/tests/schema/index_tests.cpp @@ -67,7 +67,7 @@ TEST_CASE("index") { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED TEST_CASE("filtered index") { -#if SQLITE_VERSION_NUMBER >= 3008008 +#if SQLITE_VERSION_NUMBER >= 3037002 const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_UNIQUE)); #else const ErrorCodeExceptionMatcher uniqueExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); diff --git a/tests/storage_non_crud_tests.cpp b/tests/storage_non_crud_tests.cpp index f0fc5f0f9..215be01ed 100644 --- a/tests/storage_non_crud_tests.cpp +++ b/tests/storage_non_crud_tests.cpp @@ -505,7 +505,7 @@ TEST_CASE("Remove all") { } TEST_CASE("Explicit insert") { -#if SQLITE_VERSION_NUMBER >= 3008008 +#if SQLITE_VERSION_NUMBER >= 3037002 const ErrorCodeExceptionMatcher notNullExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT_NOTNULL)); #else const ErrorCodeExceptionMatcher notNullExceptionMatcher(sqlite_errc(SQLITE_CONSTRAINT)); From ed8394dd3a1f337fb5e8e28465c9a40b9c196bda Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 29 Mar 2025 15:19:13 +0200 Subject: [PATCH 153/170] Ensure that a CTE's moniker type is non-const Compiling unit tests with gcc C++23 revealed that a const moniker type was somehow part of a common table expression, which caused picking of a table from the tuple of DBOs to fail. --- dev/cte_storage.h | 11 ++++++---- dev/cte_types.h | 2 -- dev/select_constraints.h | 23 ++++++++++++-------- include/sqlite_orm/sqlite_orm.h | 37 +++++++++++++++++++-------------- 4 files changed, 42 insertions(+), 31 deletions(-) diff --git a/dev/cte_storage.h b/dev/cte_storage.h index 7eeb789d3..adbf02355 100644 --- a/dev/cte_storage.h +++ b/dev/cte_storage.h @@ -2,7 +2,7 @@ #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include +#include // std::remove_const #include #include #include @@ -57,9 +57,12 @@ namespace sqlite_orm { typename SubselectColRefs, typename FinalColRefs, typename Result> - using create_cte_mapper_t = - typename create_cte_mapper:: - type; + using create_cte_mapper_t = typename create_cte_mapper, + ExplicitColRefs, + Expression, + SubselectColRefs, + FinalColRefs, + Result>::type; // aliased column expressions, explicit or implicitly numbered template = true> diff --git a/dev/cte_types.h b/dev/cte_types.h index a1b3fe613..25efbe693 100644 --- a/dev/cte_types.h +++ b/dev/cte_types.h @@ -2,12 +2,10 @@ #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include #include #endif #endif -#include "functional/cxx_type_traits_polyfill.h" #include "tuple_helper/tuple_fy.h" #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 8d913cb32..98f108c79 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -270,30 +270,35 @@ namespace sqlite_orm { template struct cte_builder { - ExplicitCols explicitColumns; + using moniker_type = std::remove_const_t; + + ExplicitCols _explicitColumns; #if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) template = true> - constexpr common_table_expression, Select> + constexpr common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; + return {std::move(_explicitColumns), std::move(sel)}; } template = true> - constexpr common_table_expression, select_t> + constexpr common_table_expression, + select_t> as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; + return {std::move(_explicitColumns), {std::move(sel)}}; } #else template = true> - constexpr common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; + constexpr common_table_expression, Select> as(Select sel) && { + return {std::move(_explicitColumns), std::move(sel)}; } template = true> - constexpr common_table_expression, select_t> + constexpr common_table_expression, select_t> as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; + return {std::move(_explicitColumns), {std::move(sel)}}; } #endif }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 769d39c30..3988faa64 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -9000,30 +9000,35 @@ namespace sqlite_orm { template struct cte_builder { - ExplicitCols explicitColumns; + using moniker_type = std::remove_const_t; + + ExplicitCols _explicitColumns; #if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) template = true> - constexpr common_table_expression, Select> + constexpr common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; + return {std::move(_explicitColumns), std::move(sel)}; } template = true> - constexpr common_table_expression, select_t> + constexpr common_table_expression, + select_t> as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; + return {std::move(_explicitColumns), {std::move(sel)}}; } #else template = true> - constexpr common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; + constexpr common_table_expression, Select> as(Select sel) && { + return {std::move(_explicitColumns), std::move(sel)}; } template = true> - constexpr common_table_expression, select_t> + constexpr common_table_expression, select_t> as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; + return {std::move(_explicitColumns), {std::move(sel)}}; } #endif }; @@ -10621,13 +10626,10 @@ namespace sqlite_orm { #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include #include #endif #endif -// #include "functional/cxx_type_traits_polyfill.h" - // #include "tuple_helper/tuple_fy.h" #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -22128,7 +22130,7 @@ namespace sqlite_orm { #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include +#include // std::remove_const #include #include #include @@ -22306,9 +22308,12 @@ namespace sqlite_orm { typename SubselectColRefs, typename FinalColRefs, typename Result> - using create_cte_mapper_t = - typename create_cte_mapper:: - type; + using create_cte_mapper_t = typename create_cte_mapper, + ExplicitColRefs, + Expression, + SubselectColRefs, + FinalColRefs, + Result>::type; // aliased column expressions, explicit or implicitly numbered template = true> From 00ee4a4d018266083d2682fc90a9009000d785bb Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 29 Mar 2025 16:34:12 +0200 Subject: [PATCH 154/170] Corrected unit test for gcc until v13.3 gcc until v13.3 had the bad habit to deduce a constant template parameter as const; remove it --- dev/table_reference.h | 4 +--- include/sqlite_orm/sqlite_orm.h | 2 -- tests/static_tests/alias.cpp | 5 ++++- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dev/table_reference.h b/dev/table_reference.h index aa968f4f5..30a984bf7 100644 --- a/dev/table_reference.h +++ b/dev/table_reference.h @@ -8,9 +8,7 @@ namespace sqlite_orm { namespace internal { - /* - * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. - */ + /* * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. */ template struct table_reference : polyfill::type_identity {}; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 3988faa64..c5ac349f9 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1944,9 +1944,7 @@ namespace sqlite_orm::internal { namespace sqlite_orm { namespace internal { /* - * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. - */ template struct table_reference : polyfill::type_identity {}; diff --git a/tests/static_tests/alias.cpp b/tests/static_tests/alias.cpp index 0945d5f08..11b611292 100644 --- a/tests/static_tests/alias.cpp +++ b/tests/static_tests/alias.cpp @@ -31,7 +31,10 @@ void runTest(ColAlias /*colRef*/) { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES -template> +template, + typename O = internal::auto_type_t> concept table_alias_callable = orm_table_alias && requires { { get_all() } -> same_as>>; { count() } -> same_as>; From 2f42c7c22a758375ae1ee9be3aa6b66959ef4321 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 29 Mar 2025 18:31:26 +0200 Subject: [PATCH 155/170] Further fallback correction for gcc until v13.3 --- dev/alias.h | 6 +++--- dev/select_constraints.h | 17 ++++++----------- include/sqlite_orm/sqlite_orm.h | 23 +++++++++-------------- 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/dev/alias.h b/dev/alias.h index fbc174126..902e36ec4 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -252,7 +252,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { requires (!orm_cte_moniker>) constexpr auto alias_column(C field) { using namespace ::sqlite_orm::internal; - using A = decltype(als); + using A = std::remove_const_t; using aliased_type = type_t; static_assert(is_field_of_v, "Column must be from aliased table"); @@ -348,7 +348,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { */ template auto as(E expression) { - return internal::as_t{std::move(expression)}; + return internal::as_t, E>{std::move(expression)}; } /** @@ -380,7 +380,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template auto get() { - return internal::alias_holder{}; + return internal::alias_holder>{}; } #endif diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 98f108c79..2a77bf774 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -270,33 +270,28 @@ namespace sqlite_orm { template struct cte_builder { - using moniker_type = std::remove_const_t; - ExplicitCols _explicitColumns; #if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) template = true> - constexpr common_table_expression, Select> + constexpr common_table_expression, Select> as(Select sel) && { return {std::move(_explicitColumns), std::move(sel)}; } template = true> - constexpr common_table_expression, - select_t> + constexpr common_table_expression, select_t> as(Compound sel) && { return {std::move(_explicitColumns), {std::move(sel)}}; } #else template = true> - constexpr common_table_expression, Select> as(Select sel) && { + constexpr common_table_expression, Select> as(Select sel) && { return {std::move(_explicitColumns), std::move(sel)}; } template = true> - constexpr common_table_expression, select_t> + constexpr common_table_expression, select_t> as(Compound sel) && { return {std::move(_explicitColumns), {std::move(sel)}}; } @@ -587,8 +582,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { static_assert((!is_builtin_numeric_column_alias_v && ...), "Numeric column aliases are reserved for referencing columns locally within a single CTE."); - using builder_type = - cte_builder, decay_explicit_column_t>>; + using builder_type = cte_builder, + transform_tuple_t, decay_explicit_column_t>>; return builder_type{{std::move(explicitColumns)...}}; } #endif diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index c5ac349f9..3e877d99e 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -2638,7 +2638,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { requires (!orm_cte_moniker>) constexpr auto alias_column(C field) { using namespace ::sqlite_orm::internal; - using A = decltype(als); + using A = std::remove_const_t; using aliased_type = type_t; static_assert(is_field_of_v, "Column must be from aliased table"); @@ -2734,7 +2734,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { */ template auto as(E expression) { - return internal::as_t{std::move(expression)}; + return internal::as_t, E>{std::move(expression)}; } /** @@ -2766,7 +2766,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template auto get() { - return internal::alias_holder{}; + return internal::alias_holder>{}; } #endif @@ -8998,33 +8998,28 @@ namespace sqlite_orm { template struct cte_builder { - using moniker_type = std::remove_const_t; - ExplicitCols _explicitColumns; #if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) template = true> - constexpr common_table_expression, Select> + constexpr common_table_expression, Select> as(Select sel) && { return {std::move(_explicitColumns), std::move(sel)}; } template = true> - constexpr common_table_expression, - select_t> + constexpr common_table_expression, select_t> as(Compound sel) && { return {std::move(_explicitColumns), {std::move(sel)}}; } #else template = true> - constexpr common_table_expression, Select> as(Select sel) && { + constexpr common_table_expression, Select> as(Select sel) && { return {std::move(_explicitColumns), std::move(sel)}; } template = true> - constexpr common_table_expression, select_t> + constexpr common_table_expression, select_t> as(Compound sel) && { return {std::move(_explicitColumns), {std::move(sel)}}; } @@ -9315,8 +9310,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { static_assert((!is_builtin_numeric_column_alias_v && ...), "Numeric column aliases are reserved for referencing columns locally within a single CTE."); - using builder_type = - cte_builder, decay_explicit_column_t>>; + using builder_type = cte_builder, + transform_tuple_t, decay_explicit_column_t>>; return builder_type{{std::move(explicitColumns)...}}; } #endif From d9bb705f129f392c3620a54c5d066f975b70b0f5 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 29 Mar 2025 20:09:36 +0200 Subject: [PATCH 156/170] Handled non-copyable quoted scalar functions --- dev/storage_base.h | 8 +++++--- dev/type_traits.h | 4 ++-- include/sqlite_orm/sqlite_orm.h | 12 +++++++----- tests/user_defined_functions.cpp | 7 +++---- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/dev/storage_base.h b/dev/storage_base.h index e1d4b0b1e..fd771d589 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -365,9 +365,10 @@ namespace sqlite_orm { * `quoted_scalar_function::callable()` uses the original function object, assuming it is free of side effects; * otherwise, it repeatedly uses a copy of the contained function object, assuming possible side effects. */ - template + template + requires (orm_quoted_scalar_function) void create_scalar_function() { - using Sig = auto_udf_type_t; + using Sig = auto_udf_type_t<(quotedF)>; using args_tuple = typename callable_arguments::args_tuple; using return_type = typename callable_arguments::return_type; constexpr auto argsCount = std::is_same>::value @@ -487,7 +488,8 @@ namespace sqlite_orm { * Delete a quoted scalar function you created before. * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ - template + template + requires (orm_quoted_scalar_function) void delete_scalar_function() { this->delete_function_impl(quotedF.name(), this->scalarFunctions); } diff --git a/dev/type_traits.h b/dev/type_traits.h index 345f445d6..f4e9a3103 100644 --- a/dev/type_traits.h +++ b/dev/type_traits.h @@ -114,8 +114,8 @@ namespace sqlite_orm { template using udf_type_t = typename T::udf_type; - template - using auto_udf_type_t = typename decltype(a)::udf_type; + template + using auto_udf_type_t = typename std::remove_reference_t::udf_type; #endif #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 3e877d99e..8c59dc5a9 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1834,8 +1834,8 @@ namespace sqlite_orm { template using udf_type_t = typename T::udf_type; - template - using auto_udf_type_t = typename decltype(a)::udf_type; + template + using auto_udf_type_t = typename std::remove_reference_t::udf_type; #endif #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -18166,9 +18166,10 @@ namespace sqlite_orm { * `quoted_scalar_function::callable()` uses the original function object, assuming it is free of side effects; * otherwise, it repeatedly uses a copy of the contained function object, assuming possible side effects. */ - template + template + requires (orm_quoted_scalar_function) void create_scalar_function() { - using Sig = auto_udf_type_t; + using Sig = auto_udf_type_t<(quotedF)>; using args_tuple = typename callable_arguments::args_tuple; using return_type = typename callable_arguments::return_type; constexpr auto argsCount = std::is_same>::value @@ -18288,7 +18289,8 @@ namespace sqlite_orm { * Delete a quoted scalar function you created before. * Can be called at any time (in a single-threaded context) no matter whether the database connection is open or not. */ - template + template + requires (orm_quoted_scalar_function) void delete_scalar_function() { this->delete_function_impl(quotedF.name(), this->scalarFunctions); } diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index 0124c7d62..37b442a67 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -617,15 +617,14 @@ TEST_CASE("generalized scalar udf") { SECTION("non-copyable function object") { // note: unlike msvc, gcc+clang require the constant template parameter to be copyable (and probably rightly so); // so we have to go through the builder to create/delete the quoted function. - constexpr auto idfunc_builder = "idfunc"_scalar; - constexpr auto idfunc_f = idfunc_builder.quote(); - storage.create_scalar_function()>(); + static constexpr auto idfunc_f = "idfunc"_scalar.quote(); + storage.create_scalar_function<(idfunc_f)>(); { auto rows = storage.select(idfunc_f(1)); decltype(rows) expected{1}; REQUIRE(rows == expected); } - storage.delete_scalar_function()>(); + storage.delete_scalar_function<(idfunc_f)>(); } SECTION("stateful function object") { constexpr auto offset0_f = "offset0"_scalar.quote(offset0); From d59eb5d4d1447f6b16a53404111b26da0f3e7aec Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 29 Mar 2025 20:44:34 +0200 Subject: [PATCH 157/170] Replaced `(void)` casts by `maybe_unused` annotations where possible --- dev/column_pointer.h | 3 +-- dev/schema/table.h | 4 +--- dev/udf_proxy.h | 7 ++----- include/sqlite_orm/sqlite_orm.h | 14 ++++---------- 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/dev/column_pointer.h b/dev/column_pointer.h index db6eb6b8e..393fed7d6 100644 --- a/dev/column_pointer.h +++ b/dev/column_pointer.h @@ -113,7 +113,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { * storage.with(cte()(select(as(&Object::id))), select(column(get()))); */ template = true> - constexpr auto column(F field) { + constexpr auto column([[maybe_unused]] F field) { using namespace ::sqlite_orm::internal; static_assert(is_cte_moniker_v, "`Moniker' must be a CTE moniker"); @@ -126,7 +126,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } else { return column_pointer{std::move(field)}; } - (void)field; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES diff --git a/dev/schema/table.h b/dev/schema/table.h index 7680e029a..0fe359088 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -150,7 +150,7 @@ namespace sqlite_orm { } const basic_generated_always::storage_type* - find_column_generated_storage_type(const std::string& name) const { + find_column_generated_storage_type([[maybe_unused]] const std::string& name) const { const basic_generated_always::storage_type* result = nullptr; #if SQLITE_VERSION_NUMBER >= 3031000 iterate_tuple(this->elements, @@ -165,8 +165,6 @@ namespace sqlite_orm { constexpr size_t opIndex = index_sequence_value_at<0>(generated_op_index_sequence{}); result = &std::get(column.constraints).storage; }); -#else - (void)name; #endif return result; } diff --git a/dev/udf_proxy.h b/dev/udf_proxy.h index 73710ee21..df9adb7f2 100644 --- a/dev/udf_proxy.h +++ b/dev/udf_proxy.h @@ -142,19 +142,16 @@ namespace sqlite_orm { }; // safety net of doing a triple check at runtime - inline void assert_args_count(const udf_proxy* proxy, int argsCount) { + inline void assert_args_count([[maybe_unused]] const udf_proxy* proxy, [[maybe_unused]] int argsCount) { assert((proxy->argumentsCount == -1) || (proxy->argumentsCount == argsCount || /*check fin call*/ argsCount == -1)); - (void)proxy; - (void)argsCount; } // safety net of doing a triple check at runtime - inline void proxy_assert_args_count(sqlite3_context* context, int argsCount) { + inline void proxy_assert_args_count([[maybe_unused]] sqlite3_context* context, int argsCount) { udf_proxy* proxy; assert((proxy = static_cast(sqlite3_user_data(context))) != nullptr); assert_args_count(proxy, argsCount); - (void)context; } // note: may throw `std::bad_alloc` in case memory space for the aggregate function object cannot be allocated diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 8c59dc5a9..b8a947e9c 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -2355,7 +2355,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { * storage.with(cte()(select(as(&Object::id))), select(column(get()))); */ template = true> - constexpr auto column(F field) { + constexpr auto column([[maybe_unused]] F field) { using namespace ::sqlite_orm::internal; static_assert(is_cte_moniker_v, "`Moniker' must be a CTE moniker"); @@ -2368,7 +2368,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } else { return column_pointer{std::move(field)}; } - (void)field; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -12324,7 +12323,7 @@ namespace sqlite_orm { } const basic_generated_always::storage_type* - find_column_generated_storage_type(const std::string& name) const { + find_column_generated_storage_type([[maybe_unused]] const std::string& name) const { const basic_generated_always::storage_type* result = nullptr; #if SQLITE_VERSION_NUMBER >= 3031000 iterate_tuple(this->elements, @@ -12339,8 +12338,6 @@ namespace sqlite_orm { constexpr size_t opIndex = index_sequence_value_at<0>(generated_op_index_sequence{}); result = &std::get(column.constraints).storage; }); -#else - (void)name; #endif return result; } @@ -17692,19 +17689,16 @@ namespace sqlite_orm { }; // safety net of doing a triple check at runtime - inline void assert_args_count(const udf_proxy* proxy, int argsCount) { + inline void assert_args_count([[maybe_unused]] const udf_proxy* proxy, [[maybe_unused]] int argsCount) { assert((proxy->argumentsCount == -1) || (proxy->argumentsCount == argsCount || /*check fin call*/ argsCount == -1)); - (void)proxy; - (void)argsCount; } // safety net of doing a triple check at runtime - inline void proxy_assert_args_count(sqlite3_context* context, int argsCount) { + inline void proxy_assert_args_count([[maybe_unused]] sqlite3_context* context, int argsCount) { udf_proxy* proxy; assert((proxy = static_cast(sqlite3_user_data(context))) != nullptr); assert_args_count(proxy, argsCount); - (void)context; } // note: may throw `std::bad_alloc` in case memory space for the aggregate function object cannot be allocated From 095c1a008f2496d1e76916b030db2d73e75d1ee3 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 30 Mar 2025 09:33:33 +0300 Subject: [PATCH 158/170] Corrected "Macintosh" newlines --- dev/functional/index_sequence_util.h | 13 ++++++++++--- dev/table_reference.h | 12 +++++++----- include/sqlite_orm/sqlite_orm.h | 4 ---- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/dev/functional/index_sequence_util.h b/dev/functional/index_sequence_util.h index bf485dfa9..7950aea40 100644 --- a/dev/functional/index_sequence_util.h +++ b/dev/functional/index_sequence_util.h @@ -7,13 +7,17 @@ namespace sqlite_orm { namespace internal { #if defined(SQLITE_ORM_PACK_INDEXING_SUPPORTED) - /** * Get the index value of an `index_sequence` at a specific position. */ + /** + * Get the index value of an `index_sequence` at a specific position. + */ template SQLITE_ORM_CONSTEVAL auto index_sequence_value_at(std::index_sequence) { return Idx...[Pos]; } #elif defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) - /** * Get the index value of an `index_sequence` at a specific position. */ + /** + * Get the index value of an `index_sequence` at a specific position. + */ template SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { static_assert(Pos < sizeof...(Idx)); @@ -28,7 +32,10 @@ namespace sqlite_orm { return result; } #else - /** * Get the index value of an `index_sequence` at a specific position. * `Pos` must always be `0`. */ + /** + * Get the index value of an `index_sequence` at a specific position. + * `Pos` must always be `0`. + */ template SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { static_assert(Pos == 0, ""); diff --git a/dev/table_reference.h b/dev/table_reference.h index 30a984bf7..ac792d7e2 100644 --- a/dev/table_reference.h +++ b/dev/table_reference.h @@ -8,7 +8,9 @@ namespace sqlite_orm { namespace internal { - /* * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. */ + /* + * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. + */ template struct table_reference : polyfill::type_identity {}; @@ -37,10 +39,10 @@ namespace sqlite_orm { SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. - * - * A concrete table reference has the following traits: - * - specialization of `table_reference`, whose `type` typename references a mapped object. + /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. + * + * A concrete table reference has the following traits: + * - specialization of `table_reference`, whose `type` typename references a mapped object. */ template concept orm_table_reference = polyfill::is_specialization_of_v, internal::table_reference>; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index b8a947e9c..d6060b502 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1975,13 +1975,9 @@ namespace sqlite_orm { SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. - * - * A concrete table reference has the following traits: - * - specialization of `table_reference`, whose `type` typename references a mapped object. - */ template concept orm_table_reference = polyfill::is_specialization_of_v, internal::table_reference>; From 2bd2b11fd70a29b13c4aa1dfee54c4310d63c562 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 30 Mar 2025 10:03:45 +0300 Subject: [PATCH 159/170] Updated a comment on non-copyable quoted functions --- tests/user_defined_functions.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/user_defined_functions.cpp b/tests/user_defined_functions.cpp index 37b442a67..8f7361ffa 100644 --- a/tests/user_defined_functions.cpp +++ b/tests/user_defined_functions.cpp @@ -615,8 +615,9 @@ TEST_CASE("generalized scalar udf") { storage.delete_scalar_function(); } SECTION("non-copyable function object") { - // note: unlike msvc, gcc+clang require the constant template parameter to be copyable (and probably rightly so); - // so we have to go through the builder to create/delete the quoted function. + // note: unlike msvc, gcc+clang require a constant template parameter to be copyable (and probably rightly so); + // so we must explicitly use an ordinary l-value expression from a global quoted function. + static constexpr auto idfunc_f = "idfunc"_scalar.quote(); storage.create_scalar_function<(idfunc_f)>(); { From 0c13c71173a01a4b5cb1272d258b8bd6b403c762 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 30 Mar 2025 10:07:06 +0300 Subject: [PATCH 160/170] C++17 mentioned as a basic requirement in the readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 850aeb62a..bd21f2398 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ SQLite ORM light header only library for modern C++. Please read the license pre * **No raw string queries** * **Intuitive syntax** * **Comfortable interface - one code line per single query** -* **Built with modern C++14/C++17/C++20 features (no macros and external scripts)** +* **Built with modern C++17 functionality (or higher) - no macros and external scripts** * **CRUD support** * **Pure select query support** * **Prepared statements support** @@ -801,7 +801,7 @@ If you want to use the lib directly with Make or something else, just set the in # Requirements -* C++14 compatible compiler (not C++11 cause of templated lambdas in the lib). +* C++17 compatible compiler. * Sqlite3 installed on your system and in the path, so cmake can find it (or linked to you project if you don't use cmake) # Video from conference From c72ba1997949be9a99184b9acb9030669bf5b3df Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sun, 30 Mar 2025 22:06:29 +0500 Subject: [PATCH 161/170] added tests for almost all public storage funcs --- dev/storage.h | 118 ++- include/sqlite_orm/sqlite_orm.h | 118 ++- tests/logger_tests.cpp | 1481 +++++++++++++++++++++++++++---- 3 files changed, 1440 insertions(+), 277 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index 4b2be5f17..ce91809a5 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1480,18 +1480,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -1504,18 +1503,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } #endif @@ -1523,18 +1521,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } /** @@ -1552,18 +1549,17 @@ namespace sqlite_orm { [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { return table.object_field_value(object, memberPointer); }); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -1603,18 +1599,17 @@ namespace sqlite_orm { const object_type& o = get_object(expression); processObject(o); })(statement.expression); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } /** @@ -1663,18 +1658,17 @@ namespace sqlite_orm { const object_type& o = get_object(expression); processObject(o); })(statement.expression); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -1682,18 +1676,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.ids, conditional_binder{stmt}); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } template @@ -1716,18 +1709,17 @@ namespace sqlite_orm { bindValue(polyfill::invoke(column.member_pointer, object)); } }); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } template @@ -1737,22 +1729,21 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::unique_ptr res; -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { res = std::make_unique(); object_from_column_builder builder{*res, stmt}; table.for_each_column(builder); }); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif return res; } @@ -1764,21 +1755,20 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::optional res; -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { object_from_column_builder builder{res.emplace(), stmt}; table.for_each_column(builder); }); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -1791,7 +1781,10 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED std::optional res; - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } @@ -1808,7 +1801,10 @@ namespace sqlite_orm { return std::move(res).value(); #else auto& table = this->get_table(); - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } @@ -1837,18 +1833,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.conditions, conditional_binder{stmt}); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } template @@ -1857,18 +1852,17 @@ namespace sqlite_orm { conditional_binder bindNode{stmt}; iterate_ast(statement.expression.set, bindNode); iterate_ast(statement.expression.conditions, bindNode); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index d9cb9d972..b7bd2477e 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -24212,18 +24212,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) @@ -24236,18 +24235,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } #endif @@ -24255,18 +24253,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } /** @@ -24284,18 +24281,17 @@ namespace sqlite_orm { [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { return table.object_field_value(object, memberPointer); }); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -24335,18 +24331,17 @@ namespace sqlite_orm { const object_type& o = get_object(expression); processObject(o); })(statement.expression); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } /** @@ -24395,18 +24390,17 @@ namespace sqlite_orm { const object_type& o = get_object(expression); processObject(o); })(statement.expression); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -24414,18 +24408,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.ids, conditional_binder{stmt}); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } template @@ -24448,18 +24441,17 @@ namespace sqlite_orm { bindValue(polyfill::invoke(column.member_pointer, object)); } }); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } template @@ -24469,22 +24461,21 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::unique_ptr res; -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { res = std::make_unique(); object_from_column_builder builder{*res, stmt}; table.for_each_column(builder); }); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif return res; } @@ -24496,21 +24487,20 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::optional res; -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { object_from_column_builder builder{res.emplace(), stmt}; table.for_each_column(builder); }); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -24523,7 +24513,10 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED std::optional res; - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } @@ -24540,7 +24533,10 @@ namespace sqlite_orm { return std::move(res).value(); #else auto& table = this->get_table(); - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } @@ -24569,18 +24565,17 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.conditions, conditional_binder{stmt}); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } template @@ -24589,18 +24584,17 @@ namespace sqlite_orm { conditional_binder bindNode{stmt}; iterate_ast(statement.expression.set, bindNode); iterate_ast(statement.expression.conditions, bindNode); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - const std::string sql = statement.sql(); + std::string sql; + if (this->executor.will_run_query || this->executor.will_run_query) { + sql = statement.sql(); + } if (this->executor.will_run_query) { this->executor.will_run_query(sql); } -#endif perform_step(stmt); -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED if (this->executor.did_run_query) { this->executor.did_run_query(sql); } -#endif } #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) diff --git a/tests/logger_tests.cpp b/tests/logger_tests.cpp index f9ba5002f..006e1d67a 100644 --- a/tests/logger_tests.cpp +++ b/tests/logger_tests.cpp @@ -30,6 +30,11 @@ TEST_CASE("logger") { Logs expectedWillLogs; Logs expectedDidLogs; + auto runRequire = [&expectedWillLogs, &expectedDidLogs] { + REQUIRE(WillLogsCollector::logs == expectedWillLogs); + REQUIRE(DidLogsCollector::logs == expectedDidLogs); + }; + auto willRunQuery = GENERATE(Callback(WillLogsCollector()), Callback()); auto didRunQuery = GENERATE(Callback(DidLogsCollector()), Callback()); @@ -42,6 +47,10 @@ TEST_CASE("logger") { int userId = 0; std::string date; }; + struct VisitLog { + int id = 0; + std::string message; + }; auto pushExpected = [&willRunQuery, &didRunQuery, &expectedWillLogs, &expectedDidLogs](const std::string& expected) { @@ -53,13 +62,33 @@ TEST_CASE("logger") { } }; + auto requireLogsAreEmpty = [] { + REQUIRE(WillLogsCollector::logs.empty()); + REQUIRE(DidLogsCollector::logs.empty()); + }; + auto storage = make_storage("", + make_trigger("visits_log_insert", + after() + .insert() + .on() + .for_each_row() + .when(gt(new_(&Visit::userId), 0)) + .begin(insert(into(), + columns(&VisitLog::message), + values(std::make_tuple("User " || c(new_(&Visit::userId)) || + " visited on " || new_(&Visit::date))))) + .end()), + make_index("user_id_index", &User::id), make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), make_table("visits", make_column("id", &Visit::id, primary_key()), make_column("user_id", &Visit::userId), make_column("date", &Visit::date)), + make_table("visits_log", + make_column("id", &VisitLog::id, primary_key()), + make_column("message", &VisitLog::message)), will_run_query(std::move(willRunQuery)), did_run_query(std::move(didRunQuery))); storage.sync_schema(); @@ -67,189 +96,1335 @@ TEST_CASE("logger") { WillLogsCollector::logs.clear(); DidLogsCollector::logs.clear(); - SECTION("application_id") { - SECTION("get") { - std::ignore = storage.pragma.application_id(); - pushExpected("PRAGMA application_id"); + SECTION("misc") { + SECTION("transaction_guard") { + enum class ActionAfter { + rollback, + commit, + nothing, + }; + + auto guard = storage.transaction_guard(); + pushExpected("BEGIN TRANSACTION"); + runRequire(); + + SECTION("nothing") { + SECTION("nothing") { + pushExpected("ROLLBACK"); + } + SECTION("commit_on_destroy = true") { + guard.commit_on_destroy = true; + pushExpected("COMMIT"); + } + SECTION("commit_on_destroy = false") { + guard.commit_on_destroy = false; + pushExpected("ROLLBACK"); + } + } + SECTION("commit") { + guard.commit(); + pushExpected("COMMIT"); + } + SECTION("rollback") { + guard.rollback(); + pushExpected("ROLLBACK"); + } } - SECTION("set") { - const auto [value, expected] = GENERATE(table({{2, "PRAGMA application_id = 2"}, - {3, "PRAGMA application_id = 3"}, - {4, "PRAGMA application_id = 4"}})); - storage.pragma.application_id(value); - pushExpected(expected); + SECTION("drop_index") { + storage.drop_index("user_id_index"); + pushExpected(R"(DROP INDEX "user_id_index")"); } - } - SECTION("module_list") { - std::ignore = storage.pragma.module_list(); - pushExpected("PRAGMA module_list"); - } - SECTION("recursive_triggers") { - SECTION("get") { - std::ignore = storage.pragma.recursive_triggers(); - pushExpected("PRAGMA recursive_triggers"); - } - SECTION("set") { - const auto [value, expected] = GENERATE(table({ - {true, "PRAGMA recursive_triggers = 1"}, - {false, "PRAGMA recursive_triggers = 0"}, + SECTION("drop_index_if_exists") { + storage.drop_index_if_exists("user_id_index"); + pushExpected(R"(DROP INDEX IF EXISTS "user_id_index")"); + } + SECTION("drop_trigger") { + storage.drop_trigger("visits_log_insert"); + pushExpected(R"(DROP TRIGGER "visits_log_insert")"); + } + SECTION("drop_trigger_if_exists") { + const auto [value, expected] = GENERATE(table({ + {"one", R"(DROP TRIGGER IF EXISTS "one")"}, + {"two", R"(DROP TRIGGER IF EXISTS "two")"}, })); - storage.pragma.recursive_triggers(value); + storage.drop_trigger_if_exists(value); pushExpected(expected); } - } - SECTION("busy_timeout") { - SECTION("get") { - std::ignore = storage.pragma.busy_timeout(); - pushExpected("PRAGMA busy_timeout"); - } - SECTION("set") { - const auto [value, expected] = GENERATE(table({ - {1, "PRAGMA busy_timeout = 1"}, - {2, "PRAGMA busy_timeout = 2"}, - {3, "PRAGMA busy_timeout = 3"}, + SECTION("vacuum") { + storage.vacuum(); + pushExpected("VACUUM"); + } + SECTION("drop_table") { + const auto [value, expected] = GENERATE(table({ + {"users", R"(DROP TABLE "users")"}, + {"visits", R"(DROP TABLE "visits")"}, })); - storage.pragma.busy_timeout(value); + storage.drop_table(value); pushExpected(expected); } - } - SECTION("locking_mode") { - SECTION("get") { - std::ignore = storage.pragma.locking_mode(); - pushExpected("PRAGMA locking_mode"); - } - SECTION("set") { - const auto [value, expected] = GENERATE(table({ - {locking_mode::NORMAL, "PRAGMA locking_mode = NORMAL"}, - {locking_mode::EXCLUSIVE, "PRAGMA locking_mode = EXCLUSIVE"}, + SECTION("drop_table_if_exists") { + const auto [value, expected] = GENERATE(table({ + {"users", R"(DROP TABLE IF EXISTS "users")"}, + {"visits", R"(DROP TABLE IF EXISTS "visits")"}, })); - storage.pragma.locking_mode(value); + storage.drop_table_if_exists(value); pushExpected(expected); } - } - SECTION("journal_mode") { - SECTION("get") { - std::ignore = storage.pragma.journal_mode(); - pushExpected("PRAGMA journal_mode"); - } - SECTION("set") { - const auto [value, expected] = GENERATE(table({ - {journal_mode::DELETE_, "PRAGMA journal_mode = DELETE"}, - {journal_mode::TRUNCATE, "PRAGMA journal_mode = TRUNCATE"}, - {journal_mode::PERSIST, "PRAGMA journal_mode = PERSIST"}, - {journal_mode::MEMORY, "PRAGMA journal_mode = MEMORY"}, - {journal_mode::WAL, "PRAGMA journal_mode = WAL"}, - {journal_mode::OFF, "PRAGMA journal_mode = OFF"}, + SECTION("changes") { + std::ignore = storage.changes(); + } + SECTION("total_changes") { + std::ignore = storage.total_changes(); + } + SECTION("last_insert_rowid") { + std::ignore = storage.last_insert_rowid(); + } + SECTION("busy_timeout") { + std::ignore = storage.busy_timeout(100); + } + SECTION("libversion") { + std::ignore = storage.libversion(); + } + SECTION("transaction") { + using Transaction = std::function; + + const auto [transactionLambda, expectedVector] = GENERATE(table>({ + {Transaction(), {}}, + {Transaction([] { + return false; + }), + {"BEGIN TRANSACTION", "ROLLBACK"}}, + {Transaction([] { + return true; + }), + {"BEGIN TRANSACTION", "COMMIT"}}, })); - storage.pragma.journal_mode(value); - pushExpected(expected); + storage.transaction(transactionLambda); + for (auto& expected: expectedVector) { + pushExpected(expected); + } } - } - SECTION("synchronous") { - SECTION("get") { - std::ignore = storage.pragma.synchronous(); - pushExpected("PRAGMA synchronous"); - } - SECTION("set") { - const auto [value, expected] = GENERATE(table({ - {0, "PRAGMA synchronous = 0"}, - {1, "PRAGMA synchronous = 1"}, - {2, "PRAGMA synchronous = 2"}, - {3, "PRAGMA synchronous = 3"}, + SECTION("current_time") { + std::ignore = storage.current_time(); + pushExpected("SELECT CURRENT_TIME"); + } + SECTION("current_date") { + std::ignore = storage.current_date(); + pushExpected("SELECT CURRENT_DATE"); + } + SECTION("current_timestamp") { + std::ignore = storage.current_timestamp(); + pushExpected("SELECT CURRENT_TIMESTAMP"); + } + SECTION("db_release_memory") { + std::ignore = storage.db_release_memory(); + } + SECTION("table_names") { + std::ignore = storage.table_names(); + pushExpected("SELECT name FROM sqlite_master WHERE type='table'"); + } + SECTION("open_forever") { + storage.open_forever(); + } + SECTION("begin_transaction") { + using storage_base = internal::storage_base; + + enum class Action { + commit, + rollback, + nothing, + }; + + const auto [transactionFunction, expected] = GENERATE(table({ + {&storage_base::begin_transaction, "BEGIN TRANSACTION"}, + {&storage_base::begin_deferred_transaction, "BEGIN DEFERRED TRANSACTION"}, + {&storage_base::begin_immediate_transaction, "BEGIN IMMEDIATE TRANSACTION"}, + {&storage_base::begin_exclusive_transaction, "BEGIN EXCLUSIVE TRANSACTION"}, })); - storage.pragma.synchronous(value); + (storage.*transactionFunction)(); pushExpected(expected); + const auto postAction = GENERATE(Action::commit, Action::rollback, Action::nothing); + switch (postAction) { + case Action::commit: + storage.commit(); + pushExpected("COMMIT"); + break; + case Action::rollback: + storage.rollback(); + pushExpected("ROLLBACK"); + break; + case Action::nothing: + break; + } + } + SECTION("filename") { + std::ignore = storage.filename(); + } + SECTION("is_opened") { + std::ignore = storage.is_opened(); + } + SECTION("get_autocommit") { + std::ignore = storage.get_autocommit(); + } + SECTION("busy_handler") { + storage.busy_handler([](int argument) -> int { + std::ignore = argument; + return 0; + }); + } + SECTION("rename_table") { + SECTION("in database") { + storage.rename_table("users", "winners"); + pushExpected(R"(ALTER TABLE "users" RENAME TO "winners")"); + } + SECTION("in storage schema") { + storage.rename_table("winners"); + } + } + SECTION("dump") { + SECTION("users") { + std::ignore = storage.dump(User{1, "Tate McRae"}); + } + SECTION("visits") { + std::ignore = storage.dump(Visit{1, 1, "today"}); + } + } + SECTION("tablename") { + SECTION("users") { + std::ignore = storage.tablename(); + } + SECTION("visits") { + std::ignore = storage.tablename(); + } + } + SECTION("find_column_name") { + SECTION("users.id") { + std::ignore = storage.find_column_name(&User::id); + } + SECTION("users.name") { + std::ignore = storage.find_column_name(&User::name); + } + SECTION("visits.id") { + std::ignore = storage.find_column_name(&Visit::id); + } + SECTION("visits.user_id") { + std::ignore = storage.find_column_name(&Visit::userId); + } + SECTION("visits.date") { + std::ignore = storage.find_column_name(&Visit::date); + } + } + SECTION("table_exists") { + SECTION("users") { + std::ignore = storage.table_exists("users"); + pushExpected(R"(SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = 'users')"); + } + SECTION("visits") { + std::ignore = storage.table_exists("visits"); + pushExpected(R"(SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = 'visits')"); + } + SECTION("non_existing") { + std::ignore = storage.table_exists("non_existing"); + pushExpected(R"(SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = 'non_existing')"); + } } } - SECTION("user_version") { - SECTION("get") { - std::ignore = storage.pragma.user_version(); - pushExpected("PRAGMA user_version"); - } - SECTION("set") { - const auto [value, expected] = GENERATE(table({ - {0, "PRAGMA user_version = 0"}, - {1, "PRAGMA user_version = 1"}, - {2, "PRAGMA user_version = 2"}, - {3, "PRAGMA user_version = 3"}, - {4, "PRAGMA user_version = 4"}, - })); - storage.pragma.user_version(value); - pushExpected(expected); + SECTION("functions") { + SECTION("total") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.total(&User::id); + pushExpected(R"(SELECT TOTAL("users"."id") FROM "users")"); + } + SECTION("where length(name) < 10") { + std::ignore = storage.total(&User::id, where(length(&User::name) < 10)); + pushExpected(R"(SELECT TOTAL("users"."id") FROM "users" WHERE (LENGTH("users"."name") < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.total(&Visit::id); + pushExpected(R"(SELECT TOTAL("visits"."id") FROM "visits")"); + } + SECTION("where user_id < 10") { + std::ignore = storage.total(&Visit::id, where(c(&Visit::userId) < 10)); + pushExpected(R"(SELECT TOTAL("visits"."id") FROM "visits" WHERE ("visits"."user_id" < ?))"); + } + } + } + SECTION("sum") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.sum(&User::id); + pushExpected(R"(SELECT SUM("users"."id") FROM "users")"); + } + SECTION("where length(name) < 10") { + std::ignore = storage.sum(&User::id, where(length(&User::name) < 10)); + pushExpected(R"(SELECT SUM("users"."id") FROM "users" WHERE (LENGTH("users"."name") < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.sum(&Visit::id); + pushExpected(R"(SELECT SUM("visits"."id") FROM "visits")"); + } + SECTION("where user_id < 10") { + std::ignore = storage.sum(&Visit::id, where(c(&Visit::userId) < 10)); + pushExpected(R"(SELECT SUM("visits"."id") FROM "visits" WHERE ("visits"."user_id" < ?))"); + } + } + } + SECTION("min") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.min(&User::id); + pushExpected(R"(SELECT MIN("users"."id") FROM "users")"); + } + SECTION("where length(name) < 10") { + std::ignore = storage.min(&User::id, where(length(&User::name) < 10)); + pushExpected(R"(SELECT MIN("users"."id") FROM "users" WHERE (LENGTH("users"."name") < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.min(&Visit::id); + pushExpected(R"(SELECT MIN("visits"."id") FROM "visits")"); + } + SECTION("where user_id < 10") { + std::ignore = storage.min(&Visit::id, where(c(&Visit::userId) < 10)); + pushExpected(R"(SELECT MIN("visits"."id") FROM "visits" WHERE ("visits"."user_id" < ?))"); + } + } + } + SECTION("max") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.max(&User::id); + pushExpected(R"(SELECT MAX("users"."id") FROM "users")"); + } + SECTION("where length(name) < 10") { + std::ignore = storage.max(&User::id, where(length(&User::name) < 10)); + pushExpected(R"(SELECT MAX("users"."id") FROM "users" WHERE (LENGTH("users"."name") < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.max(&Visit::id); + pushExpected(R"(SELECT MAX("visits"."id") FROM "visits")"); + } + SECTION("where user_id < 10") { + std::ignore = storage.max(&Visit::id, where(c(&Visit::userId) < 10)); + pushExpected(R"(SELECT MAX("visits"."id") FROM "visits" WHERE ("visits"."user_id" < ?))"); + } + } + } + SECTION("group_concat") { + SECTION("users") { + SECTION("2 arguments") { + SECTION("no conditions") { + std::ignore = storage.group_concat(&User::name); + pushExpected(R"(SELECT GROUP_CONCAT("users"."name") FROM "users")"); + } + SECTION("where id > 20") { + std::ignore = storage.group_concat(&User::name, where(c(&User::id) > 20)); + pushExpected(R"(SELECT GROUP_CONCAT("users"."name") FROM "users" WHERE ("users"."id" > ?))"); + } + } + SECTION("3 arguments") { + SECTION("no conditions") { + std::ignore = storage.group_concat(&User::name, "-"); + pushExpected(R"(SELECT GROUP_CONCAT("users"."name", ?) FROM "users")"); + } + SECTION("where id > 20") { + std::ignore = storage.group_concat(&User::name, "-", where(c(&User::id) > 20)); + pushExpected(R"(SELECT GROUP_CONCAT("users"."name", ?) FROM "users" WHERE ("users"."id" > ?))"); + } + } + } + SECTION("visits") { + SECTION("2 arguments") { + SECTION("no conditions") { + std::ignore = storage.group_concat(&Visit::date); + pushExpected(R"(SELECT GROUP_CONCAT("visits"."date") FROM "visits")"); + } + SECTION("where id > 20") { + std::ignore = storage.group_concat(&Visit::date, where(c(&Visit::id) > 20)); + pushExpected(R"(SELECT GROUP_CONCAT("visits"."date") FROM "visits" WHERE ("visits"."id" > ?))"); + } + } + SECTION("3 arguments") { + SECTION("no conditions") { + std::ignore = storage.group_concat(&Visit::date, "-"); + pushExpected(R"(SELECT GROUP_CONCAT("visits"."date", ?) FROM "visits")"); + } + SECTION("where id > 20") { + std::ignore = storage.group_concat(&Visit::date, "-", where(c(&Visit::id) > 20)); + pushExpected( + R"(SELECT GROUP_CONCAT("visits"."date", ?) FROM "visits" WHERE ("visits"."id" > ?))"); + } + } + } + } + SECTION("avg") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.avg(&User::id); + pushExpected(R"(SELECT AVG("users"."id") FROM "users")"); + } + SECTION("where id > 10") { + std::ignore = storage.avg(&User::id, where(c(&User::id) > 10)); + pushExpected(R"(SELECT AVG("users"."id") FROM "users" WHERE ("users"."id" > ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.avg(&Visit::id); + pushExpected(R"(SELECT AVG("visits"."id") FROM "visits")"); + } + SECTION("where id > 10") { + std::ignore = storage.avg(&Visit::id, where(c(&Visit::id) > 10)); + pushExpected(R"(SELECT AVG("visits"."id") FROM "visits" WHERE ("visits"."id" > ?))"); + } + } + } + SECTION("count") { + SECTION("count(*)") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.count(); + pushExpected(R"(SELECT COUNT(*) FROM "users")"); + } + SECTION("where id < 10") { + std::ignore = storage.count(where(c(&User::id) < 10)); + pushExpected(R"(SELECT COUNT(*) FROM "users" WHERE ("users"."id" < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.count(); + pushExpected(R"(SELECT COUNT(*) FROM "visits")"); + } + SECTION("where id < 10") { + std::ignore = storage.count(where(c(&Visit::id) < 10)); + pushExpected(R"(SELECT COUNT(*) FROM "visits" WHERE ("visits"."id" < ?))"); + } + } + } + SECTION("count(X)") { + SECTION("users") { + SECTION("no conditions") { + std::ignore = storage.count(&User::id); + pushExpected(R"(SELECT COUNT("users"."id") FROM "users")"); + } + SECTION("where id < 20") { + std::ignore = storage.count(&User::id, where(c(&User::id) < 20)); + pushExpected(R"(SELECT COUNT("users"."id") FROM "users" WHERE ("users"."id" < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + std::ignore = storage.count(&Visit::id); + pushExpected(R"(SELECT COUNT("visits"."id") FROM "visits")"); + } + SECTION("where id < 20") { + std::ignore = storage.count(&Visit::id, where(c(&Visit::id) < 20)); + pushExpected(R"(SELECT COUNT("visits"."id") FROM "visits" WHERE ("visits"."id" < ?))"); + } + } + } } } - SECTION("auto_vacuum") { - SECTION("get") { - std::ignore = storage.pragma.auto_vacuum(); - pushExpected("PRAGMA auto_vacuum"); - } - SECTION("set") { - const auto [value, expected] = GENERATE(table({ - {0, "PRAGMA auto_vacuum = 0"}, - {1, "PRAGMA auto_vacuum = 1"}, - {2, "PRAGMA auto_vacuum = 2"}, - })); - storage.pragma.auto_vacuum(value); - pushExpected(expected); + SECTION("non crud") { + SECTION("insert_range") { + SECTION("users") { + const std::vector usersToInsert = { + {1, "Tate McRae"}, + {2, "Machine Gun Kelly"}, + }; + SECTION("simple") { + storage.insert_range(usersToInsert.begin(), usersToInsert.end()); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert_range(usersToInsert.begin(), usersToInsert.end())); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(INSERT INTO "users" ("name") VALUES (?), (?))"); + } + SECTION("visits") { + const std::vector visitsToInsert = { + {1, 1, "today"}, + {2, 10, "yesterday"}, + }; + SECTION("simple") { + storage.insert_range(visitsToInsert.begin(), visitsToInsert.end()); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert_range(visitsToInsert.begin(), visitsToInsert.end())); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(INSERT INTO "visits" ("user_id", "date") VALUES (?, ?), (?, ?))"); + } + } + SECTION("raw replace") { + SECTION("users") { + SECTION("name") { + SECTION("simple") { + storage.replace(into(), columns(&User::name), values(std::make_tuple("TateMcRae"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare( + replace(into(), columns(&User::name), values(std::make_tuple("TateMcRae")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "users" ("name") VALUES (?))"); + } + SECTION("id, name") { + SECTION("simple") { + storage.replace(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "TateMcRae"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "TateMcRae")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "users" ("id", "name") VALUES (?, ?))"); + } + } + SECTION("visits") { + SECTION("user_id, date") { + SECTION("simple") { + storage.replace(into(), + columns(&Visit::userId, &Visit::date), + values(std::make_tuple(1, "today"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace(into(), + columns(&Visit::userId, &Visit::date), + values(std::make_tuple(1, "today")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "visits" ("user_id", "date") VALUES (?, ?))"); + } + SECTION("id, user_id, date") { + SECTION("simple") { + storage.replace(into(), + columns(&Visit::id, &Visit::userId, &Visit::date), + values(std::make_tuple(1, 1, "today"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace(into(), + columns(&Visit::id, &Visit::userId, &Visit::date), + values(std::make_tuple(1, 1, "today")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "visits" ("id", "user_id", "date") VALUES (?, ?, ?))"); + } + } + } + SECTION("insert") { + SECTION("implicit") { + SECTION("users") { + SECTION("simple") { + storage.insert(User{1, "Tate McRae"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert(User{1, "Tate McRae"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(INSERT INTO "users" ("name") VALUES (?))"); + } + SECTION("visits") { + SECTION("simple") { + storage.insert(Visit{1, 1, "today"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert(Visit{1, 1, "today"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(INSERT INTO "visits" ("user_id", "date") VALUES (?, ?))"); + } + } + SECTION("explicit + raw") { + SECTION("users") { + SECTION("name") { + SECTION("explicit") { + SECTION("simple") { + storage.insert(User{1, "TateMcRae"}, columns(&User::name)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert(User{1, "TateMcRae"}, columns(&User::name))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("raw") { + SECTION("simple") { + storage.insert(into(), + columns(&User::name), + values(std::make_tuple("TateMcRae"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare( + insert(into(), columns(&User::name), values(std::make_tuple("TateMcRae")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + pushExpected(R"(INSERT INTO "users" ("name") VALUES (?))"); + } + SECTION("id, name") { + SECTION("explicit") { + SECTION("simple") { + storage.insert(User{1, "TateMcRae"}, columns(&User::id, &User::name)); + } + SECTION("prepared statement") { + auto statement = + storage.prepare(insert(User{1, "TateMcRae"}, columns(&User::id, &User::name))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("raw") { + SECTION("simple") { + storage.insert(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "TateMcRae"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert(into(), + columns(&User::id, &User::name), + values(std::make_tuple(1, "TateMcRae")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + pushExpected(R"(INSERT INTO "users" ("id", "name") VALUES (?, ?))"); + } + } + SECTION("visits") { + SECTION("user_id, date") { + SECTION("explicit") { + SECTION("simple") { + storage.insert(Visit{1, 1, "today"}, columns(&Visit::userId, &Visit::date)); + } + SECTION("prepared statement") { + auto statement = storage.prepare( + insert(Visit{1, 1, "today"}, columns(&Visit::userId, &Visit::date))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("raw") { + SECTION("simple") { + storage.insert(into(), + columns(&Visit::userId, &Visit::date), + values(std::make_tuple(1, "today"))); + } + SECTION("prepared statement") { + auto statement = storage.prepare(insert(into(), + columns(&Visit::userId, &Visit::date), + values(std::make_tuple(1, "today")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + pushExpected(R"(INSERT INTO "visits" ("user_id", "date") VALUES (?, ?))"); + } + SECTION("id, user_id, date") { + SECTION("explicit") { + SECTION("simple") { + storage.insert(Visit{1, 1, "today"}, columns(&Visit::id, &Visit::userId, &Visit::date)); + } + SECTION("prepared statement") { + auto statement = storage.prepare( + insert(Visit{1, 1, "today"}, columns(&Visit::id, &Visit::userId, &Visit::date))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("raw") { + SECTION("simple") { + storage.insert(into(), + columns(&Visit::id, &Visit::userId, &Visit::date), + values(std::make_tuple(1, 1, "today"))); + } + SECTION("prepared statement") { + auto statement = + storage.prepare(insert(into(), + columns(&Visit::id, &Visit::userId, &Visit::date), + values(std::make_tuple(1, 1, "today")))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + pushExpected(R"(INSERT INTO "visits" ("id", "user_id", "date") VALUES (?, ?, ?))"); + } + } + } + } + SECTION("select") { + SECTION("users") { + SECTION("simple") { + std::ignore = storage.select(&User::id); + } + SECTION("prepared statement") { + auto statement = storage.prepare(select(&User::id)); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(SELECT "users"."id" FROM "users")"); + } + SECTION("visits") { + SECTION("simple") { + std::ignore = storage.select(&Visit::id); + } + SECTION("prepared statement") { + auto statement = storage.prepare(select(&Visit::id)); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(SELECT "visits"."id" FROM "visits")"); + } + } + SECTION("get_all + get_all_pointer + get_all_optional") { + SECTION("users") { + SECTION("no conditions") { + SECTION("get_all") { + SECTION("simple") { + std::ignore = storage.get_all(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("get_all_pointer") { + SECTION("simple") { + std::ignore = storage.get_all_pointer(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_pointer()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_all_optional") { + SECTION("simple") { + std::ignore = storage.get_all_optional(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_optional()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected(R"(SELECT "users"."id", "users"."name" FROM "users")"); + } + SECTION("where id < 10") { + SECTION("get_all") { + SECTION("simple") { + std::ignore = storage.get_all(where(c(&User::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all(where(c(&User::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("get_all_pointer") { + SECTION("simple") { + std::ignore = storage.get_all_pointer(where(c(&User::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_pointer(where(c(&User::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_all_optional") { + SECTION("simple") { + std::ignore = storage.get_all_optional(where(c(&User::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_optional(where(c(&User::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected(R"(SELECT "users"."id", "users"."name" FROM "users" WHERE ("users"."id" < ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + SECTION("get_all") { + SECTION("simple") { + std::ignore = storage.get_all(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("get_all_pointer") { + SECTION("simple") { + std::ignore = storage.get_all_pointer(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_pointer()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_all_optional") { + SECTION("simple") { + std::ignore = storage.get_all_optional(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_optional()); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected(R"(SELECT "visits"."id", "visits"."user_id", "visits"."date" FROM "visits")"); + } + SECTION("where id < 10") { + SECTION("get_all") { + SECTION("simple") { + std::ignore = storage.get_all(where(c(&Visit::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all(where(c(&Visit::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } + SECTION("get_all_pointer") { + SECTION("simple") { + std::ignore = storage.get_all_pointer(where(c(&Visit::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_pointer(where(c(&Visit::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_all_optional") { + SECTION("simple") { + std::ignore = storage.get_all_optional(where(c(&Visit::id) < 10)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_all_optional(where(c(&Visit::id) < 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected( + R"(SELECT "visits"."id", "visits"."user_id", "visits"."date" FROM "visits" WHERE ("visits"."id" < ?))"); + } + } + } + SECTION("remove_all") { + SECTION("users") { + SECTION("no conditions") { + SECTION("simple") { + storage.remove_all(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove_all()); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "users")"); + } + SECTION("where id = 1") { + SECTION("simple") { + storage.remove_all(where(c(&User::id) == 1)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove_all(where(c(&User::id) == 1))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "users" WHERE ("users"."id" = ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + SECTION("simple") { + storage.remove_all(); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove_all()); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "visits")"); + } + SECTION("where id = 1") { + SECTION("simple") { + storage.remove_all(where(c(&Visit::id) == 1)); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove_all(where(c(&Visit::id) == 1))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "visits" WHERE ("visits"."id" = ?))"); + } + } + } + SECTION("update_all") { + SECTION("users") { + SECTION("no conditions") { + SECTION("simple") { + storage.update_all(set(c(&User::name) = "Jade")); + } + SECTION("prepared statement") { + auto statement = storage.prepare(update_all(set(c(&User::name) = "Jade"))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "users" SET "name" = ?)"); + } + SECTION("where id = 10") { + SECTION("simple") { + storage.update_all(set(c(&User::name) = "Jade"), where(c(&User::id) == 10)); + } + SECTION("prepared statement") { + auto statement = + storage.prepare(update_all(set(c(&User::name) = "Jade"), where(c(&User::id) == 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "users" SET "name" = ? WHERE ("users"."id" = ?))"); + } + } + SECTION("visits") { + SECTION("no conditions") { + SECTION("simple") { + storage.update_all(set(c(&Visit::date) = "Jade")); + } + SECTION("prepared statement") { + auto statement = storage.prepare(update_all(set(c(&Visit::date) = "Jade"))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "visits" SET "date" = ?)"); + } + SECTION("where id = 10") { + SECTION("simple") { + storage.update_all(set(c(&Visit::date) = "Jade"), where(c(&Visit::id) == 10)); + } + SECTION("prepared statement") { + auto statement = + storage.prepare(update_all(set(c(&Visit::date) = "Jade"), where(c(&Visit::id) == 10))); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "visits" SET "date" = ? WHERE ("visits"."id" = ?))"); + } + } } } - SECTION("max_page_count") { - SECTION("get") { - std::ignore = storage.pragma.max_page_count(); - pushExpected("PRAGMA max_page_count"); - } - SECTION("set") { - const auto [value, expected] = GENERATE(table({ - {10, "PRAGMA max_page_count = 10"}, - {20, "PRAGMA max_page_count = 20"}, - {30, "PRAGMA max_page_count = 30"}, - })); - storage.pragma.max_page_count(value); - pushExpected(expected); + SECTION("crud") { + SECTION("replace_range") { + SECTION("users") { + const std::vector usersToReplace = { + {1, "Tate McRae"}, + {2, "Machine Gun Kelly"}, + }; + SECTION("simple") { + storage.replace_range(usersToReplace.begin(), usersToReplace.end()); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace_range(usersToReplace.begin(), usersToReplace.end())); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "users" ("id", "name") VALUES (?, ?), (?, ?))"); + } + SECTION("visits") { + const std::vector visitsToReplace = { + {1, 1, "today"}, + {2, 10, "yesterday"}, + }; + SECTION("simple") { + storage.replace_range(visitsToReplace.begin(), visitsToReplace.end()); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace_range(visitsToReplace.begin(), visitsToReplace.end())); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "visits" ("id", "user_id", "date") VALUES (?, ?, ?), (?, ?, ?))"); + } + } + SECTION("replace") { + SECTION("users") { + SECTION("simple") { + storage.replace(User{1, "Tate McRae"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace(User{1, "Tate McRae"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "users" ("id", "name") VALUES (?, ?))"); + } + SECTION("visits") { + SECTION("simple") { + storage.replace(Visit{1, 5, "tomorrow"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(replace(Visit{1, 5, "tomorrow"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(REPLACE INTO "visits" ("id", "user_id", "date") VALUES (?, ?, ?))"); + } + } + SECTION("get + get_pointer + get_no_throw + get_optional") { + SECTION("users") { + storage.insert(User{1, "Tate McRae"}); + + WillLogsCollector::logs.clear(); + DidLogsCollector::logs.clear(); + + SECTION("get") { + SECTION("simple") { + std::ignore = storage.get(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } + SECTION("get_pointer") { + SECTION("simple") { + std::ignore = storage.get_pointer(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_pointer(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } + SECTION("get_no_throw") { + std::ignore = storage.get_no_throw(1); + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_optional") { + SECTION("simple") { + std::ignore = storage.get_optional(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_optional(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected(R"(SELECT "id", "name" FROM "users" WHERE "id" = ?)"); + } + SECTION("visits") { + storage.insert(Visit{1, 1, "today"}); + + WillLogsCollector::logs.clear(); + DidLogsCollector::logs.clear(); + + SECTION("get") { + SECTION("simple") { + std::ignore = storage.get(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } + SECTION("get_pointer") { + SECTION("simple") { + std::ignore = storage.get_pointer(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_pointer(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } + SECTION("get_no_throw") { + std::ignore = storage.get_no_throw(1); + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + SECTION("get_optional") { + SECTION("simple") { + std::ignore = storage.get_optional(1); + } + SECTION("prepared statement") { + auto statement = storage.prepare(get_optional(1)); + requireLogsAreEmpty(); + std::ignore = storage.execute(statement); + } + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + pushExpected(R"(SELECT "id", "user_id", "date" FROM "visits" WHERE "id" = ?)"); + } + } + SECTION("remove") { + SECTION("users") { + SECTION("simple") { + storage.remove(2); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove(2)); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "users" WHERE "id" = ?)"); + } + SECTION("visits") { + SECTION("simple") { + storage.remove(2); + } + SECTION("prepared statement") { + auto statement = storage.prepare(remove(2)); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(DELETE FROM "visits" WHERE "id" = ?)"); + } + } + SECTION("update") { + SECTION("users") { + SECTION("simple") { + storage.update(User{1, "Tate McRae"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(update(User{1, "Tate McRae"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "users" SET "name" = ? WHERE "id" = ?)"); + } + SECTION("visits") { + SECTION("simple") { + storage.update(Visit{1, 2, "yesterday"}); + } + SECTION("prepared statement") { + auto statement = storage.prepare(update(Visit{1, 2, "yesterday"})); + requireLogsAreEmpty(); + storage.execute(statement); + } + pushExpected(R"(UPDATE "visits" SET "user_id" = ?, "date" = ? WHERE "id" = ?)"); + } } } - SECTION("integrity_check") { - SECTION("get") { - std::ignore = storage.pragma.integrity_check(); - pushExpected("PRAGMA integrity_check"); + SECTION("pragma") { + SECTION("application_id") { + SECTION("get") { + std::ignore = storage.pragma.application_id(); + pushExpected("PRAGMA application_id"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({{2, "PRAGMA application_id = 2"}, + {3, "PRAGMA application_id = 3"}, + {4, "PRAGMA application_id = 4"}})); + storage.pragma.application_id(value); + pushExpected(expected); + } + } + SECTION("module_list") { + std::ignore = storage.pragma.module_list(); + pushExpected("PRAGMA module_list"); + } + SECTION("recursive_triggers") { + SECTION("get") { + std::ignore = storage.pragma.recursive_triggers(); + pushExpected("PRAGMA recursive_triggers"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {true, "PRAGMA recursive_triggers = 1"}, + {false, "PRAGMA recursive_triggers = 0"}, + })); + storage.pragma.recursive_triggers(value); + pushExpected(expected); + } } - SECTION("set table-name") { + SECTION("busy_timeout") { + SECTION("get") { + std::ignore = storage.pragma.busy_timeout(); + pushExpected("PRAGMA busy_timeout"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {1, "PRAGMA busy_timeout = 1"}, + {2, "PRAGMA busy_timeout = 2"}, + {3, "PRAGMA busy_timeout = 3"}, + })); + storage.pragma.busy_timeout(value); + pushExpected(expected); + } + } + SECTION("locking_mode") { + SECTION("get") { + std::ignore = storage.pragma.locking_mode(); + pushExpected("PRAGMA locking_mode"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {locking_mode::NORMAL, "PRAGMA locking_mode = NORMAL"}, + {locking_mode::EXCLUSIVE, "PRAGMA locking_mode = EXCLUSIVE"}, + })); + storage.pragma.locking_mode(value); + pushExpected(expected); + } + } + SECTION("journal_mode") { + SECTION("get") { + std::ignore = storage.pragma.journal_mode(); + pushExpected("PRAGMA journal_mode"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {journal_mode::DELETE_, "PRAGMA journal_mode = DELETE"}, + {journal_mode::TRUNCATE, "PRAGMA journal_mode = TRUNCATE"}, + {journal_mode::PERSIST, "PRAGMA journal_mode = PERSIST"}, + {journal_mode::MEMORY, "PRAGMA journal_mode = MEMORY"}, + {journal_mode::WAL, "PRAGMA journal_mode = WAL"}, + {journal_mode::OFF, "PRAGMA journal_mode = OFF"}, + })); + storage.pragma.journal_mode(value); + pushExpected(expected); + } + } + SECTION("synchronous") { + SECTION("get") { + std::ignore = storage.pragma.synchronous(); + pushExpected("PRAGMA synchronous"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {0, "PRAGMA synchronous = 0"}, + {1, "PRAGMA synchronous = 1"}, + {2, "PRAGMA synchronous = 2"}, + {3, "PRAGMA synchronous = 3"}, + })); + storage.pragma.synchronous(value); + pushExpected(expected); + } + } + SECTION("user_version") { + SECTION("get") { + std::ignore = storage.pragma.user_version(); + pushExpected("PRAGMA user_version"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {0, "PRAGMA user_version = 0"}, + {1, "PRAGMA user_version = 1"}, + {2, "PRAGMA user_version = 2"}, + {3, "PRAGMA user_version = 3"}, + {4, "PRAGMA user_version = 4"}, + })); + storage.pragma.user_version(value); + pushExpected(expected); + } + } + SECTION("auto_vacuum") { + SECTION("get") { + std::ignore = storage.pragma.auto_vacuum(); + pushExpected("PRAGMA auto_vacuum"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {0, "PRAGMA auto_vacuum = 0"}, + {1, "PRAGMA auto_vacuum = 1"}, + {2, "PRAGMA auto_vacuum = 2"}, + })); + storage.pragma.auto_vacuum(value); + pushExpected(expected); + } + } + SECTION("max_page_count") { + SECTION("get") { + std::ignore = storage.pragma.max_page_count(); + pushExpected("PRAGMA max_page_count"); + } + SECTION("set") { + const auto [value, expected] = GENERATE(table({ + {10, "PRAGMA max_page_count = 10"}, + {20, "PRAGMA max_page_count = 20"}, + {30, "PRAGMA max_page_count = 30"}, + })); + storage.pragma.max_page_count(value); + pushExpected(expected); + } + } + SECTION("integrity_check") { + SECTION("get") { + std::ignore = storage.pragma.integrity_check(); + pushExpected("PRAGMA integrity_check"); + } + SECTION("set table-name") { + const auto [value, expected] = GENERATE(table({ + {"users", "PRAGMA integrity_check(users)"}, + {"visits", "PRAGMA integrity_check(visits)"}, + })); + storage.pragma.integrity_check(value); + pushExpected(expected); + } + SECTION("set N") { + const auto [value, expected] = GENERATE(table({ + {1, "PRAGMA integrity_check(1)"}, + {2, "PRAGMA integrity_check(2)"}, + })); + storage.pragma.integrity_check(value); + pushExpected(expected); + } + } + SECTION("quick_check") { + std::ignore = storage.pragma.quick_check(); + pushExpected("PRAGMA quick_check"); + } + SECTION("table_xinfo") { const auto [value, expected] = GENERATE(table({ - {"users", "PRAGMA integrity_check(users)"}, - {"visits", "PRAGMA integrity_check(visits)"}, + {"users", R"(PRAGMA table_xinfo("users"))"}, + {"visits", R"(PRAGMA table_xinfo("visits"))"}, })); - storage.pragma.integrity_check(value); + std::ignore = storage.pragma.table_xinfo(value); pushExpected(expected); } - SECTION("set N") { - const auto [value, expected] = GENERATE(table({ - {1, "PRAGMA integrity_check(1)"}, - {2, "PRAGMA integrity_check(2)"}, + SECTION("table_info") { + const auto [value, expected] = GENERATE(table({ + {"users", R"(PRAGMA table_info("users"))"}, + {"visits", R"(PRAGMA table_info("visits"))"}, })); - storage.pragma.integrity_check(value); + std::ignore = storage.pragma.table_info(value); pushExpected(expected); } } - SECTION("quick_check") { - std::ignore = storage.pragma.quick_check(); - pushExpected("PRAGMA quick_check"); - } - SECTION("table_xinfo") { - const auto [value, expected] = GENERATE(table({ - {"users", R"(PRAGMA table_xinfo("users"))"}, - {"visits", R"(PRAGMA table_xinfo("visits"))"}, - })); - std::ignore = storage.pragma.table_xinfo(value); - pushExpected(expected); - } - SECTION("table_info") { - const auto [value, expected] = GENERATE(table({ - {"users", R"(PRAGMA table_info("users"))"}, - {"visits", R"(PRAGMA table_info("visits"))"}, - })); - std::ignore = storage.pragma.table_info(value); - pushExpected(expected); - } - REQUIRE(WillLogsCollector::logs == expectedWillLogs); - REQUIRE(DidLogsCollector::logs == expectedDidLogs); + runRequire(); } From b0e2ff8afd0060ece1470fd3c9c38301625cf319 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sun, 30 Mar 2025 22:40:13 +0500 Subject: [PATCH 162/170] bug fix after merge --- dev/storage.h | 28 ++-- include/sqlite_orm/sqlite_orm.h | 255 +++----------------------------- 2 files changed, 35 insertions(+), 248 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index 78c9c22be..33077fac4 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1479,7 +1479,7 @@ namespace sqlite_orm { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1502,7 +1502,7 @@ namespace sqlite_orm { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1520,7 +1520,7 @@ namespace sqlite_orm { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1548,7 +1548,7 @@ namespace sqlite_orm { return table.object_field_value(object, memberPointer); }); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1596,7 +1596,7 @@ namespace sqlite_orm { processObject(object); }; std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1653,7 +1653,7 @@ namespace sqlite_orm { processObject(object); } std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1671,7 +1671,7 @@ namespace sqlite_orm { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1704,7 +1704,7 @@ namespace sqlite_orm { } }); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1724,7 +1724,7 @@ namespace sqlite_orm { std::unique_ptr res; std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1750,7 +1750,7 @@ namespace sqlite_orm { std::optional res; std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1776,7 +1776,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED std::optional res; std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1796,7 +1796,7 @@ namespace sqlite_orm { #else auto& table = this->get_table(); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1828,7 +1828,7 @@ namespace sqlite_orm { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.conditions, conditional_binder{stmt}); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -1847,7 +1847,7 @@ namespace sqlite_orm { iterate_ast(statement.expression.set, bindNode); iterate_ast(statement.expression.conditions, bindNode); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 19a021d56..53e5cb617 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13,7 +13,6 @@ __pragma(push_macro("max")) // #include "cxx_universal.h" - /* * This header makes central C++ functionality on which sqlite_orm depends universally available: * - alternative operator representations @@ -36,7 +35,6 @@ using std::nullptr_t; // #include "cxx_check_prerequisites.h" - /* * This header detects missing core C++ language features on which sqlite_orm depends, bailing out with a hard error. */ @@ -55,7 +53,6 @@ using std::nullptr_t; // #include "cxx_core_features.h" - /* * This header detects core C++ language features. * May be updated/overwritten by cxx_compiler_quirks.h @@ -132,7 +129,6 @@ using std::nullptr_t; // #include "cxx_compiler_quirks.h" - /* * This header defines macros for circumventing compiler quirks on which sqlite_orm depends. * May amend cxx_core_features.h @@ -196,8 +192,6 @@ using std::nullptr_t; #define SQLITE_ORM_BROKEN_NONTEMPLATE_CONCEPTS #endif - - #ifdef BUILD_SQLITE_ORM_MODULE #define SQLITE_ORM_EXPORT export #else @@ -291,10 +285,8 @@ namespace sqlite_orm { #endif // #include "functional/cxx_optional.h" - // #include "cxx_core_features.h" - #ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else @@ -307,10 +299,8 @@ namespace sqlite_orm { #define SQLITE_ORM_OPTIONAL_SUPPORTED #endif - // #include "functional/cxx_type_traits_polyfill.h" - #ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else @@ -319,7 +309,6 @@ namespace sqlite_orm { // #include "mpl/conditional.h" - namespace sqlite_orm { namespace internal { namespace mpl { @@ -353,7 +342,6 @@ namespace sqlite_orm { namespace mpl = internal::mpl; } - namespace sqlite_orm { namespace internal { namespace polyfill { @@ -504,7 +492,6 @@ namespace sqlite_orm { // #include "functional/cxx_functional_polyfill.h" - #ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else @@ -519,14 +506,12 @@ namespace sqlite_orm { // #include "../member_traits/member_traits.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_function, std::true_type, std::false_type #endif // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { // SFINAE friendly trait to get a member object pointer's field type @@ -626,7 +611,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { namespace polyfill { @@ -723,7 +707,6 @@ namespace sqlite_orm { // #include "functional/mpl.h" - /* * Symbols for 'template metaprogramming' (compile-time template programming), * inspired by the MPL of Aleksey Gurtovoy and David Abrahams, and the Mp11 of Peter Dimov and Bjorn Reese. @@ -761,7 +744,6 @@ namespace sqlite_orm { // #include "mpl/conditional.h" - namespace sqlite_orm { namespace internal { namespace mpl { @@ -1256,12 +1238,10 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_traits.h" - // #include "../functional/cxx_type_traits_polyfill.h" // #include "../functional/mpl.h" - namespace sqlite_orm { // convenience metafunction algorithms namespace internal { @@ -1322,7 +1302,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_filter.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant, std::index_sequence, std::conditional, std::declval #include // std::tuple, std::tuple_cat, std::tuple_element @@ -1332,7 +1311,6 @@ namespace sqlite_orm { // #include "../functional/index_sequence_util.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence #endif @@ -1385,7 +1363,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -1475,7 +1452,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_transformer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval #include // std::tuple_size, std::get @@ -1487,7 +1463,6 @@ namespace sqlite_orm { // #include "../functional/mpl.h" - namespace sqlite_orm { namespace internal { @@ -1611,7 +1586,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_iteration.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::get, std::tuple_element, std::tuple_size #include // std::index_sequence, std::make_index_sequence @@ -1681,7 +1655,6 @@ namespace sqlite_orm { // #include "type_traits.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::is_empty, std::is_aggregate #if __cpp_lib_unwrap_ref >= 201811L @@ -1695,7 +1668,6 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { // C++ generic traits used throughout the library namespace internal { @@ -1833,7 +1805,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "alias.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same #include // std::make_index_sequence, std::move @@ -1850,7 +1821,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "functional/cstring_literal.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include // std::index_sequence @@ -1888,7 +1858,6 @@ namespace sqlite_orm::internal { // #include "alias_traits.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::is_same #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -1902,14 +1871,12 @@ namespace sqlite_orm::internal { // #include "table_reference.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const, std::type_identity #endif // #include "functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { /* @@ -1953,7 +1920,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #endif } - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Base class for a custom table alias, column alias or expression alias. @@ -2074,14 +2040,12 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "field_of.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_convertible, std::bool_constant #endif // #include "member_traits/member_traits.h" - namespace sqlite_orm { namespace internal { template @@ -2134,7 +2098,6 @@ namespace sqlite_orm { // #include "table_type_of.h" - namespace sqlite_orm { namespace internal { @@ -2178,10 +2141,8 @@ namespace sqlite_orm { // #include "tags.h" - // #include "functional/cxx_functional_polyfill.h" - namespace sqlite_orm { namespace internal { struct negatable_t {}; @@ -2209,7 +2170,6 @@ namespace sqlite_orm { // #include "column_pointer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_convertible #include // std::move @@ -2225,7 +2185,6 @@ namespace sqlite_orm { // #include "tags.h" - namespace sqlite_orm { namespace internal { /** @@ -2856,7 +2815,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "error_code.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::error_code, std::system_error @@ -3030,7 +2988,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "type_printer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::shared_ptr, std::unique_ptr @@ -3038,14 +2995,12 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #endif // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "type_traits.h" // #include "is_std_ptr.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include @@ -3078,7 +3033,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { }; } - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -3153,7 +3107,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "constraints.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of, std::false_type, std::true_type #include // std::system_error @@ -3168,7 +3121,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "tuple_helper/same_or_void.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::common_type #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED @@ -3234,7 +3186,6 @@ namespace sqlite_orm { // #include "collate_argument.h" - namespace sqlite_orm { namespace internal { @@ -3257,7 +3208,6 @@ namespace sqlite_orm { // #include "table_reference.h" - namespace sqlite_orm { namespace internal { @@ -3940,7 +3890,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "field_printer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::stringstream @@ -3960,7 +3909,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "type_traits.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -4117,7 +4065,6 @@ namespace sqlite_orm { // #include "rowid.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -4192,7 +4139,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "operators.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move @@ -4202,7 +4148,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "functional/is_base_template_of.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::true_type, std::false_type, std::declval #endif @@ -4246,13 +4191,10 @@ namespace sqlite_orm { // #include "serialize_result_type.h" - // #include "functional/cxx_string_view.h" - // #include "cxx_core_features.h" - #ifdef SQLITE_ORM_IMPORT_STD_MODULE #include #else @@ -4283,7 +4225,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -4577,7 +4518,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "select_constraints.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include @@ -4589,7 +4529,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { #endif // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "functional/is_base_template_of.h" @@ -4602,7 +4541,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "optional_container.h" - namespace sqlite_orm { namespace internal { @@ -4637,7 +4575,6 @@ namespace sqlite_orm { // #include "ast/where.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type #include // std::move @@ -4647,7 +4584,6 @@ namespace sqlite_orm { // #include "../serialize_result_type.h" - namespace sqlite_orm { namespace internal { @@ -4697,7 +4633,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/group_by.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple #include // std::true_type, std::false_type @@ -4706,7 +4641,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { @@ -4753,7 +4687,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "core_functions.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::make_tuple, std::tuple_size @@ -4772,7 +4705,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "conditions.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::enable_if, std::is_same, std::remove_const @@ -4797,7 +4729,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "serializer_context.h" - namespace sqlite_orm { namespace internal { @@ -4845,7 +4776,6 @@ namespace sqlite_orm { // #include "expression.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::enable_if @@ -4853,14 +4783,12 @@ namespace sqlite_orm { #endif // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "tags.h" // #include "operators.h" - namespace sqlite_orm { namespace internal { @@ -4963,7 +4891,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "literal.h" - namespace sqlite_orm { namespace internal { @@ -4980,7 +4907,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -6243,10 +6169,8 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/into.h" - // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { @@ -6267,7 +6191,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -8416,7 +8339,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "cte_moniker.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -8433,7 +8355,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "alias.h" - #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) namespace sqlite_orm { @@ -8513,7 +8434,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "schema/column.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple #include // std::string @@ -8534,17 +8454,14 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../type_is_nullable.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::false_type, std::true_type, std::enable_if #include // std::shared_ptr, std::unique_ptr #endif // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -8580,7 +8497,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../constraints.h" - namespace sqlite_orm { namespace internal { @@ -8758,7 +8674,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -9527,7 +9442,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "statement_binder.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence @@ -9560,14 +9474,12 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "arithmetic_tag.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_integral #endif // #include "functional/mpl/conditional.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -9588,7 +9500,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "xdestroy_handling.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::integral_constant #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED @@ -9598,7 +9509,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { using xdestroy_fn_t = void (*)(void*); @@ -9842,7 +9752,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "pointer_value.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3020000 #include @@ -9858,7 +9767,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "xdestroy_handling.h" - #if SQLITE_VERSION_NUMBER >= 3020000 namespace sqlite_orm { namespace internal { @@ -10148,7 +10056,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } #endif - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -10488,7 +10395,6 @@ namespace sqlite_orm { // #include "column_result.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of #include // std::reference_wrapper @@ -10502,7 +10408,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_fy.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif @@ -10537,7 +10442,6 @@ namespace sqlite_orm { // #include "mapped_type_proxy.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_const #endif @@ -10548,7 +10452,6 @@ namespace sqlite_orm { // #include "alias_traits.h" - namespace sqlite_orm { namespace internal { @@ -10583,12 +10486,10 @@ namespace sqlite_orm { // #include "column_result_proxy.h" - // #include "type_traits.h" // #include "table_reference.h" - namespace sqlite_orm { namespace internal { @@ -10630,7 +10531,6 @@ namespace sqlite_orm { // #include "cte_types.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include @@ -10639,7 +10539,6 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_fy.h" - #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) namespace sqlite_orm { @@ -10693,7 +10592,6 @@ namespace sqlite_orm { // #include "storage_traits.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple #endif @@ -10708,7 +10606,6 @@ namespace sqlite_orm { // #include "storage_lookup.h" - #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 @@ -10719,7 +10616,6 @@ namespace sqlite_orm { // #include "type_traits.h" - namespace sqlite_orm { namespace internal { @@ -10867,7 +10763,6 @@ namespace sqlite_orm { // #include "schema/column.h" - namespace sqlite_orm { namespace internal { @@ -10917,7 +10812,6 @@ namespace sqlite_orm { // #include "function.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_member_function_pointer, std::is_function, std::remove_const, std::decay, std::is_convertible, std::is_same, std::false_type, std::true_type #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED @@ -10934,12 +10828,10 @@ namespace sqlite_orm { // #include "functional/function_traits.h" - // #include "cxx_type_traits_polyfill.h" // #include "mpl.h" - namespace sqlite_orm { namespace internal { /* @@ -11022,7 +10914,6 @@ namespace sqlite_orm { // #include "tags.h" - namespace sqlite_orm { struct arg_values; @@ -11555,7 +11446,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/special_keywords.h" - namespace sqlite_orm { namespace internal { struct current_time_t {}; @@ -11906,7 +11796,6 @@ namespace sqlite_orm { // #include "sync_schema_result.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif @@ -11971,7 +11860,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "table_info.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move @@ -12024,7 +11912,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "storage_impl.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -12047,7 +11934,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "schema/table.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::remove_const, std::is_member_pointer, std::true_type, std::false_type @@ -12086,7 +11972,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "index.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t #include // std::string @@ -12097,7 +11982,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../indexed_column.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #include // std::move @@ -12105,7 +11989,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/where.h" - namespace sqlite_orm { namespace internal { @@ -12167,7 +12050,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../table_type_of.h" - namespace sqlite_orm { namespace internal { @@ -12224,7 +12106,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "column.h" - namespace sqlite_orm { namespace internal { @@ -12696,7 +12577,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "storage_lookup.h" - // interface functions namespace sqlite_orm { namespace internal { @@ -12806,7 +12686,6 @@ namespace sqlite_orm { // #include "journal_mode.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string @@ -12817,7 +12696,6 @@ namespace sqlite_orm { // #include "serialize_result_type.h" - #if defined(_WINNT_) // DELETE is a macro defined in the Windows SDK (winnt.h) #pragma push_macro("DELETE") @@ -12900,7 +12778,6 @@ namespace sqlite_orm { // #include "mapped_view.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::forward, std::move @@ -12908,7 +12785,6 @@ namespace sqlite_orm { // #include "row_extractor.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if @@ -12943,7 +12819,6 @@ namespace sqlite_orm { // #include "locking_mode.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::array #include // std::string @@ -12954,7 +12829,6 @@ namespace sqlite_orm { // #include "serialize_result_type.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { enum class locking_mode : signed char { NORMAL = 0, @@ -13010,7 +12884,6 @@ namespace sqlite_orm { // #include "type_traits.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -13539,7 +13412,6 @@ namespace sqlite_orm { // #include "mapped_iterator.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::shared_ptr, std::make_shared @@ -13551,7 +13423,6 @@ namespace sqlite_orm { // #include "statement_finalizer.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr @@ -13571,7 +13442,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "object_from_column_builder.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_member_object_pointer @@ -13588,7 +13458,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "storage_lookup.h" - namespace sqlite_orm { namespace internal { @@ -13657,7 +13526,6 @@ namespace sqlite_orm { // #include "util.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string @@ -13672,7 +13540,6 @@ namespace sqlite_orm { // #include "serialize_result_type.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @@ -13824,7 +13691,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -13931,7 +13797,6 @@ namespace sqlite_orm { // #include "ast_iterator.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_invocable #include // std::vector @@ -13939,7 +13804,7 @@ namespace sqlite_orm { #endif // #include "functional/cxx_functional_polyfill.h" - // std::is_invocable +// std::is_invocable // #include "tuple_helper/tuple_iteration.h" // #include "type_traits.h" @@ -13956,7 +13821,6 @@ namespace sqlite_orm { // #include "prepared_statement.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::unique_ptr @@ -13975,7 +13839,6 @@ namespace sqlite_orm { // #include "connection_holder.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include @@ -13985,7 +13848,6 @@ namespace sqlite_orm { // #include "error_code.h" - namespace sqlite_orm { namespace internal { @@ -14098,7 +13960,6 @@ namespace sqlite_orm { // #include "values.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::vector #include // std::tuple @@ -14107,7 +13968,6 @@ namespace sqlite_orm { // #include "functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { @@ -14151,7 +14011,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/upsert_clause.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if SQLITE_VERSION_NUMBER >= 3024000 #include // std::tuple @@ -14161,7 +14020,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../functional/cxx_type_traits_polyfill.h" - namespace sqlite_orm { namespace internal { #if SQLITE_VERSION_NUMBER >= 3024000 @@ -14231,7 +14089,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/set.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::tuple, std::tuple_size #include // std::string @@ -14244,7 +14101,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../table_name_collector.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::set #include // std::string @@ -14265,7 +14121,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "storage_lookup.h" - namespace sqlite_orm { namespace internal { @@ -14354,7 +14209,6 @@ namespace sqlite_orm { } - namespace sqlite_orm { namespace internal { @@ -14457,7 +14311,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -15265,7 +15118,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/excluded.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #endif @@ -15299,14 +15151,12 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/exists.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move #endif // #include "../tags.h" - namespace sqlite_orm { namespace internal { @@ -15341,7 +15191,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "ast/match.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #endif @@ -15366,7 +15215,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -16087,7 +15935,6 @@ namespace sqlite_orm { // #include "util.h" - namespace sqlite_orm { namespace internal { @@ -16153,7 +16000,6 @@ inline constexpr bool std::ranges::enable_borrowed_range #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move, std::remove_cvref @@ -16170,7 +16016,6 @@ inline constexpr bool std::ranges::enable_borrowed_range #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::move @@ -16186,7 +16031,6 @@ inline constexpr bool std::ranges::enable_borrowed_range #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // atoi @@ -16364,7 +16206,6 @@ inline constexpr bool std::ranges::enable_borrowed_range #else @@ -16375,7 +16216,7 @@ inline constexpr bool std::ranges::enable_borrowed_range #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string @@ -16431,7 +16271,6 @@ namespace sqlite_orm { // #include "serializing_util.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::index_sequence, std::remove_cvref #include @@ -16442,7 +16281,7 @@ namespace sqlite_orm { #endif // #include "functional/cxx_type_traits_polyfill.h" - // std::remove_cvref, polyfill::is_detected +// std::remove_cvref, polyfill::is_detected // #include "functional/cxx_functional_polyfill.h" // #include "tuple_helper/tuple_iteration.h" @@ -16459,7 +16298,6 @@ namespace sqlite_orm { // #include "schema/column.h" - namespace sqlite_orm { namespace internal { template @@ -16880,7 +16718,6 @@ namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -17146,7 +16983,6 @@ namespace sqlite_orm { // #include "limit_accessor.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::map @@ -17156,7 +16992,6 @@ namespace sqlite_orm { // #include "connection_holder.h" - namespace sqlite_orm { namespace internal { @@ -17290,7 +17125,6 @@ namespace sqlite_orm { // #include "transaction_guard.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::function #include // std::move @@ -17298,7 +17132,6 @@ namespace sqlite_orm { // #include "connection_holder.h" - namespace sqlite_orm { namespace internal { @@ -17379,7 +17212,6 @@ namespace sqlite_orm { // #include "backup.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::system_error @@ -17392,7 +17224,6 @@ namespace sqlite_orm { // #include "connection_holder.h" - namespace sqlite_orm { namespace internal { @@ -17460,7 +17291,6 @@ namespace sqlite_orm { // #include "values_to_tuple.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::index_sequence, std::make_index_sequence @@ -17475,12 +17305,10 @@ namespace sqlite_orm { // #include "arg_values.h" - #include // #include "row_extractor.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** @short Wrapper around a dynamically typed value object. @@ -17626,7 +17454,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { }; } - namespace sqlite_orm { namespace internal { @@ -17666,7 +17493,6 @@ namespace sqlite_orm { // #include "udf_proxy.h" - #include #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // assert macro @@ -17680,7 +17506,6 @@ namespace sqlite_orm { // #include "error_code.h" - namespace sqlite_orm { namespace internal { /* @@ -17903,7 +17728,6 @@ namespace sqlite_orm { // #include "storage_options.h" - #include #ifdef SQLITE_ORM_IMPORT_STD_MODULE #include @@ -17915,7 +17739,6 @@ namespace sqlite_orm { // #include "serialize_result_type.h" - namespace sqlite_orm { namespace internal { template @@ -17974,7 +17797,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { } } - namespace sqlite_orm { namespace internal { @@ -19018,7 +18840,6 @@ namespace sqlite_orm { // #include "expression_object_type.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::decay, std::remove_reference #include // std::reference_wrapper @@ -19028,7 +18849,6 @@ namespace sqlite_orm { // #include "prepared_statement.h" - namespace sqlite_orm { namespace internal { @@ -19139,7 +18959,6 @@ namespace sqlite_orm { // #include "statement_serializer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::remove_pointer, std::remove_reference, std::remove_cvref, std::disjunction #include // std::stringstream @@ -19157,14 +18976,12 @@ namespace sqlite_orm { // #include "functional/cxx_optional.h" - // #include "functional/cxx_type_traits_polyfill.h" - // std::remove_cvref, std::disjunction +// std::remove_cvref, std::disjunction // #include "functional/cxx_functional_polyfill.h" - // std::identity, std::invoke +// std::identity, std::invoke // #include "functional/always_default.h" - namespace sqlite_orm { namespace internal { @@ -19204,7 +19021,6 @@ namespace sqlite_orm { // #include "ast/rank.h" - namespace sqlite_orm { namespace internal { struct rank_t {}; @@ -19247,7 +19063,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "column_names_getter.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_base_of #include // std::string @@ -19270,12 +19085,11 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "select_constraints.h" // #include "storage_lookup.h" - // pick_table +// pick_table // #include "serializer_context.h" // #include "util.h" - namespace sqlite_orm { namespace internal { @@ -19389,7 +19203,6 @@ namespace sqlite_orm { // #include "cte_column_names_collector.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include @@ -19413,7 +19226,6 @@ namespace sqlite_orm { // #include "serializer_context.h" - #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) namespace sqlite_orm { namespace internal { @@ -19619,7 +19431,6 @@ namespace sqlite_orm { // #include "order_by_serializer.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include // std::string @@ -19713,7 +19524,6 @@ namespace sqlite_orm { // #include "schema/triggers.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include #include @@ -19723,7 +19533,6 @@ namespace sqlite_orm { // #include "../optional_container.h" - // NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? // (Could be implemented with a normal trigger that insert or update an internal table and then retreive // the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) @@ -20003,7 +19812,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "schema/table.h" - namespace sqlite_orm { namespace internal { @@ -22310,7 +22118,6 @@ namespace sqlite_orm { // #include "cte_storage.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #include // std::remove_const @@ -22338,7 +22145,6 @@ namespace sqlite_orm { // #include "column_expression.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic #include // std::tuple @@ -22357,7 +22163,6 @@ namespace sqlite_orm { // #include "storage_traits.h" - namespace sqlite_orm { namespace internal { @@ -22456,7 +22261,6 @@ namespace sqlite_orm { // #include "storage_lookup.h" - namespace sqlite_orm { namespace internal { @@ -22769,7 +22573,6 @@ namespace sqlite_orm { // #include "serializing_util.h" - namespace sqlite_orm { namespace internal { @@ -24189,7 +23992,7 @@ namespace sqlite_orm { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24212,7 +24015,7 @@ namespace sqlite_orm { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24230,7 +24033,7 @@ namespace sqlite_orm { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24258,7 +24061,7 @@ namespace sqlite_orm { return table.object_field_value(object, memberPointer); }); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24306,7 +24109,7 @@ namespace sqlite_orm { processObject(object); }; std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24363,7 +24166,7 @@ namespace sqlite_orm { processObject(object); } std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24381,7 +24184,7 @@ namespace sqlite_orm { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24414,7 +24217,7 @@ namespace sqlite_orm { } }); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24434,7 +24237,7 @@ namespace sqlite_orm { std::unique_ptr res; std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24460,7 +24263,7 @@ namespace sqlite_orm { std::optional res; std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24486,7 +24289,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED std::optional res; std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24506,7 +24309,7 @@ namespace sqlite_orm { #else auto& table = this->get_table(); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24538,7 +24341,7 @@ namespace sqlite_orm { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.conditions, conditional_binder{stmt}); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24557,7 +24360,7 @@ namespace sqlite_orm { iterate_ast(statement.expression.set, bindNode); iterate_ast(statement.expression.conditions, bindNode); std::string sql; - if (this->executor.will_run_query || this->executor.will_run_query) { + if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); } if (this->executor.will_run_query) { @@ -24715,7 +24518,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { * this file is also used to provide definitions of interface methods 'hitting the database'. */ - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::make_unique #endif @@ -24724,7 +24526,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../default_value_extractor.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -24735,7 +24536,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "storage_lookup.h" - namespace sqlite_orm { namespace internal { @@ -24759,7 +24559,6 @@ namespace sqlite_orm { // #include "../schema/column.h" - namespace sqlite_orm { namespace internal { @@ -24784,7 +24583,6 @@ namespace sqlite_orm { * this file is also used to provide definitions of interface methods 'hitting the database'. */ - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::remove_reference #include // std::move @@ -24801,7 +24599,6 @@ namespace sqlite_orm { // #include "../schema/table.h" - namespace sqlite_orm { namespace internal { @@ -24861,7 +24658,6 @@ namespace sqlite_orm { * e.g. usage of the dbstat table. */ - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::is_same #include // std::stringstream @@ -24874,7 +24670,6 @@ namespace sqlite_orm { // #include "../sqlite_schema_table.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::string #endif @@ -24887,7 +24682,6 @@ namespace sqlite_orm { // #include "alias.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { /** * SQLite's "schema table" that stores the schema for a database. @@ -24927,7 +24721,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../eponymous_vtabs/dbstat.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #ifdef SQLITE_ENABLE_DBSTAT_VTAB #include // std::string @@ -24940,7 +24733,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../column_pointer.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB struct dbstat { @@ -24984,7 +24776,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "../storage.h" - namespace sqlite_orm { namespace internal { template @@ -25145,7 +24936,6 @@ namespace sqlite_orm { // #include "node_tuple.h" - #ifndef SQLITE_ORM_IMPORT_STD_MODULE #include // std::enable_if #include // std::tuple @@ -25187,7 +24977,6 @@ namespace sqlite_orm { // #include "ast/match.h" - namespace sqlite_orm { namespace internal { @@ -25427,7 +25216,6 @@ namespace sqlite_orm { // #include "expression_object_type.h" - SQLITE_ORM_EXPORT namespace sqlite_orm { template @@ -25606,7 +25394,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { // #include "pointer_value.h" - #if SQLITE_VERSION_NUMBER >= 3020000 #ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED SQLITE_ORM_EXPORT namespace sqlite_orm { From fd41602d130a4cfe2740a3b7950cd4c883d2e667 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sun, 30 Mar 2025 22:48:17 +0500 Subject: [PATCH 163/170] removed semicolon --- dev/storage_base.h | 1 - include/sqlite_orm/sqlite_orm.h | 1 - 2 files changed, 2 deletions(-) diff --git a/dev/storage_base.h b/dev/storage_base.h index 277cd0bb6..6153f8654 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -577,7 +577,6 @@ namespace sqlite_orm { } void begin_deferred_transaction() { - ; this->begin_transaction_internal("BEGIN DEFERRED TRANSACTION"); } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 53e5cb617..db36334e6 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -18338,7 +18338,6 @@ namespace sqlite_orm { } void begin_deferred_transaction() { - ; this->begin_transaction_internal("BEGIN DEFERRED TRANSACTION"); } From bbcf8f1f57c6a24c1d7a3154783eeea7d6320870 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Mon, 31 Mar 2025 15:44:16 +0500 Subject: [PATCH 164/170] removed std::move to fix windows compilation --- tests/logger_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/logger_tests.cpp b/tests/logger_tests.cpp index 006e1d67a..a6c1724e7 100644 --- a/tests/logger_tests.cpp +++ b/tests/logger_tests.cpp @@ -89,8 +89,8 @@ TEST_CASE("logger") { make_table("visits_log", make_column("id", &VisitLog::id, primary_key()), make_column("message", &VisitLog::message)), - will_run_query(std::move(willRunQuery)), - did_run_query(std::move(didRunQuery))); + will_run_query(willRunQuery), + did_run_query(didRunQuery)); storage.sync_schema(); WillLogsCollector::logs.clear(); From e3383f067f467d07b6fbf90d9cd16477ce236fa6 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Mon, 31 Mar 2025 11:00:27 -0700 Subject: [PATCH 165/170] implement recommended edits --- tests/vfs_open_mode_tests.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/vfs_open_mode_tests.cpp b/tests/vfs_open_mode_tests.cpp index f47246686..53a189659 100644 --- a/tests/vfs_open_mode_tests.cpp +++ b/tests/vfs_open_mode_tests.cpp @@ -37,15 +37,21 @@ TEST_CASE("vfs modes open successfully") { SECTION("Storage copy operator carries over vfs option") { auto storage_copy = storage; - CHECK(storage_copy.is_opened()); - CHECK(storage_copy.vfs_name() == vfs); - CHECK(storage_copy.open_mode() == db_open_mode::default_); + REQUIRE(storage_copy.is_opened()); + REQUIRE(storage_copy.vfs_name() == vfs); + REQUIRE(storage_copy.open_mode() == db_open_mode::default_); } } TEST_CASE("readwrite/readonly open modes behaves as expected") { const bool in_memory = GENERATE(true, false); - const char* tmp_filename = in_memory ? ":memory:" : "open_mode.sqlite"; + + const char* tmp_filename; + if (in_memory) { + tmp_filename = ":memory:"; + } else { + tmp_filename = "open_mode.sqlite"; + } connection_control options{true}, readonly_options{true}; options.open_mode = db_open_mode::create_readwrite; From 53bad11f98beb2d045c9f0d431647ebf2c13b9b5 Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Wed, 2 Apr 2025 11:25:40 -0700 Subject: [PATCH 166/170] fix visual studio error --- dev/connection_holder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/connection_holder.h b/dev/connection_holder.h index 49a6a84b3..e952d8940 100644 --- a/dev/connection_holder.h +++ b/dev/connection_holder.h @@ -85,9 +85,9 @@ namespace sqlite_orm { const std::function _didOpenDb; public: + const std::string filename; const std::string vfs_name; const db_open_mode open_mode; - const std::string filename; }; struct connection_ref { From e94d56c488e9b5b325e75bb584c6fc07280b73c4 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Thu, 3 Apr 2025 00:11:30 +0500 Subject: [PATCH 167/170] type fixes --- dev/implementations/storage_definitions.h | 2 +- dev/pragma.h | 6 +- dev/storage.h | 12 +-- dev/storage_base.h | 14 ++-- dev/util.h | 59 +++++++------- include/sqlite_orm/sqlite_orm.h | 93 +++++++++++------------ 6 files changed, 88 insertions(+), 98 deletions(-) diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index 4050b7e09..2f76658ea 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -158,7 +158,7 @@ namespace sqlite_orm { << streaming_identifier(sourceTableName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } } } diff --git a/dev/pragma.h b/dev/pragma.h index 20332a1a3..947537eeb 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -271,10 +271,10 @@ namespace sqlite_orm { void set_pragma_impl(const std::string& sql, sqlite3* db = nullptr) { if (db) { - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } else { - auto con = this->get_connection(); - this->executor.perform_void_exec(con.get(), sql); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql.data()); } } }; diff --git a/dev/storage.h b/dev/storage.h index 33077fac4..2754abbaa 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -194,7 +194,7 @@ namespace sqlite_orm { context_t context{this->db_objects}; statement_serializer serializer; const std::string sql = serializer.serialize(table, context, tableName); - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } /** @@ -217,7 +217,7 @@ namespace sqlite_orm { << streaming_identifier(columnName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } #endif @@ -1186,7 +1186,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; const auto sql = serialize(virtualTable, context); - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); return res; } @@ -1197,7 +1197,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; const auto sql = serialize(index, context); - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); return res; } @@ -1208,7 +1208,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly context_t context{this->db_objects}; const auto sql = serialize(trigger, context); - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); return res; } @@ -1230,7 +1230,7 @@ namespace sqlite_orm { << serialize(column, context) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } template diff --git a/dev/storage_base.h b/dev/storage_base.h index 6153f8654..a03324fdd 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -156,7 +156,7 @@ namespace sqlite_orm { << streaming_identifier(newName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } /** @@ -732,7 +732,7 @@ namespace sqlite_orm { void begin_transaction_internal(const std::string& sql) { this->connection->retain(); sqlite3* db = this->connection->get(); - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } connection_ref get_connection() { @@ -748,7 +748,7 @@ namespace sqlite_orm { ss << "PRAGMA foreign_keys = " << value << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } bool foreign_keys(sqlite3* db) { @@ -970,7 +970,7 @@ namespace sqlite_orm { ss << ' ' << streaming_identifier(tableName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } void drop_index_internal(const std::string& indexName, bool ifExists) { @@ -985,7 +985,7 @@ namespace sqlite_orm { sql = ss.str(); } auto connection = this->get_connection(); - this->executor.perform_void_exec(connection.get(), sql); + this->executor.perform_void_exec(connection.get(), sql.data()); } void drop_trigger_internal(const std::string& triggerName, bool ifExists) { @@ -1000,7 +1000,7 @@ namespace sqlite_orm { sql = ss.str(); } auto connection = this->get_connection(); - this->executor.perform_void_exec(connection.get(), sql); + this->executor.perform_void_exec(connection.get(), sql.data()); } static int @@ -1069,7 +1069,7 @@ namespace sqlite_orm { std::function _busy_handler; std::list scalarFunctions; std::list aggregateFunctions; - sqlite_executor executor; + const sqlite_executor executor; }; } } diff --git a/dev/util.h b/dev/util.h index f1e6cb3d3..b458f4dea 100644 --- a/dev/util.h +++ b/dev/util.h @@ -59,15 +59,31 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { namespace sqlite_orm { namespace internal { + template + int perform_step(sqlite3_stmt* stmt, L&& lambda) { + const int rc = sqlite3_step(stmt); + switch (rc) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(rc); + } + } + return rc; + } + struct sqlite_executor { std::function will_run_query; std::function did_run_query; - inline void perform_void_exec(sqlite3* db, serialize_arg_type sql) const { + inline void perform_void_exec(sqlite3* db, const char* sql) const { if (this->will_run_query) { this->will_run_query(sql); } - const int rc = sqlite3_exec(db, sql.data(), nullptr, nullptr, nullptr); + const int rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr); if (rc != SQLITE_OK) { throw_translated_sqlite_error(rc); } @@ -93,30 +109,24 @@ namespace sqlite_orm { } inline void perform_exec(sqlite3* db, - const serialize_result_type& query, + const std::string& query, int (*callback)(void* data, int argc, char** argv, char**), void* user_data) const { return perform_exec(db, query.data(), callback, user_data); } template - void perform_steps(sqlite3_stmt* stmt, L&& lambda) { - const auto sql = sqlite3_sql(stmt); + void perform_steps(sqlite3_stmt* stmt, L&& lambda) const { + const char* sql = nullptr; + if (this->will_run_query || this->did_run_query) { + sql = sqlite3_sql(stmt); + } if (this->will_run_query) { this->will_run_query(sql); } - int rc; + int rc = 0; do { - switch (rc = sqlite3_step(stmt)) { - case SQLITE_ROW: { - lambda(stmt); - } break; - case SQLITE_DONE: - break; - default: { - throw_translated_sqlite_error(rc); - } - } + rc = internal::perform_step(stmt, lambda); } while (rc != SQLITE_DONE); if (this->did_run_query) { this->did_run_query(sql); @@ -130,10 +140,9 @@ namespace sqlite_orm { return stmt; } - // note: query is deliberately taken by value, such that it is thrown away early inline sqlite3_stmt* prepare_stmt(sqlite3* db, serialize_arg_type query) { sqlite3_stmt* stmt; - const int rc = sqlite3_prepare_v2(db, query.data(), -1, &stmt, nullptr); + const int rc = sqlite3_prepare_v2(db, query.data(), query.size(), &stmt, nullptr); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { throw_translated_sqlite_error(rc); } @@ -147,19 +156,5 @@ namespace sqlite_orm { throw_translated_sqlite_error(rc); } } - - template - void perform_step(sqlite3_stmt* stmt, L&& lambda) { - switch (int rc = sqlite3_step(stmt)) { - case SQLITE_ROW: { - lambda(stmt); - } break; - case SQLITE_DONE: - return; - default: { - throw_translated_sqlite_error(rc); - } - } - } } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index db36334e6..079bfca04 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13586,15 +13586,31 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { namespace sqlite_orm { namespace internal { + template + int perform_step(sqlite3_stmt* stmt, L&& lambda) { + const int rc = sqlite3_step(stmt); + switch (rc) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(rc); + } + } + return rc; + } + struct sqlite_executor { std::function will_run_query; std::function did_run_query; - inline void perform_void_exec(sqlite3* db, serialize_arg_type sql) const { + inline void perform_void_exec(sqlite3* db, const char* sql) const { if (this->will_run_query) { this->will_run_query(sql); } - const int rc = sqlite3_exec(db, sql.data(), nullptr, nullptr, nullptr); + const int rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr); if (rc != SQLITE_OK) { throw_translated_sqlite_error(rc); } @@ -13620,30 +13636,24 @@ namespace sqlite_orm { } inline void perform_exec(sqlite3* db, - const serialize_result_type& query, + const std::string& query, int (*callback)(void* data, int argc, char** argv, char**), void* user_data) const { return perform_exec(db, query.data(), callback, user_data); } template - void perform_steps(sqlite3_stmt* stmt, L&& lambda) { - const auto sql = sqlite3_sql(stmt); + void perform_steps(sqlite3_stmt* stmt, L&& lambda) const { + const char* sql = nullptr; + if (this->will_run_query || this->did_run_query) { + sql = sqlite3_sql(stmt); + } if (this->will_run_query) { this->will_run_query(sql); } - int rc; + int rc = 0; do { - switch (rc = sqlite3_step(stmt)) { - case SQLITE_ROW: { - lambda(stmt); - } break; - case SQLITE_DONE: - break; - default: { - throw_translated_sqlite_error(rc); - } - } + rc = internal::perform_step(stmt, lambda); } while (rc != SQLITE_DONE); if (this->did_run_query) { this->did_run_query(sql); @@ -13657,10 +13667,9 @@ namespace sqlite_orm { return stmt; } - // note: query is deliberately taken by value, such that it is thrown away early inline sqlite3_stmt* prepare_stmt(sqlite3* db, serialize_arg_type query) { sqlite3_stmt* stmt; - const int rc = sqlite3_prepare_v2(db, query.data(), -1, &stmt, nullptr); + const int rc = sqlite3_prepare_v2(db, query.data(), query.size(), &stmt, nullptr); if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { throw_translated_sqlite_error(rc); } @@ -13674,20 +13683,6 @@ namespace sqlite_orm { throw_translated_sqlite_error(rc); } } - - template - void perform_step(sqlite3_stmt* stmt, L&& lambda) { - switch (int rc = sqlite3_step(stmt)) { - case SQLITE_ROW: { - lambda(stmt); - } break; - case SQLITE_DONE: - return; - default: { - throw_translated_sqlite_error(rc); - } - } - } } } @@ -16971,10 +16966,10 @@ namespace sqlite_orm { void set_pragma_impl(const std::string& sql, sqlite3* db = nullptr) { if (db) { - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } else { - auto con = this->get_connection(); - this->executor.perform_void_exec(con.get(), sql); + auto connection = this->get_connection(); + this->executor.perform_void_exec(connection.get(), sql.data()); } } }; @@ -17917,7 +17912,7 @@ namespace sqlite_orm { << streaming_identifier(newName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } /** @@ -18493,7 +18488,7 @@ namespace sqlite_orm { void begin_transaction_internal(const std::string& sql) { this->connection->retain(); sqlite3* db = this->connection->get(); - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } connection_ref get_connection() { @@ -18509,7 +18504,7 @@ namespace sqlite_orm { ss << "PRAGMA foreign_keys = " << value << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } bool foreign_keys(sqlite3* db) { @@ -18731,7 +18726,7 @@ namespace sqlite_orm { ss << ' ' << streaming_identifier(tableName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } void drop_index_internal(const std::string& indexName, bool ifExists) { @@ -18746,7 +18741,7 @@ namespace sqlite_orm { sql = ss.str(); } auto connection = this->get_connection(); - this->executor.perform_void_exec(connection.get(), sql); + this->executor.perform_void_exec(connection.get(), sql.data()); } void drop_trigger_internal(const std::string& triggerName, bool ifExists) { @@ -18761,7 +18756,7 @@ namespace sqlite_orm { sql = ss.str(); } auto connection = this->get_connection(); - this->executor.perform_void_exec(connection.get(), sql); + this->executor.perform_void_exec(connection.get(), sql.data()); } static int @@ -18830,7 +18825,7 @@ namespace sqlite_orm { std::function _busy_handler; std::list scalarFunctions; std::list aggregateFunctions; - sqlite_executor executor; + const sqlite_executor executor; }; } } @@ -22706,7 +22701,7 @@ namespace sqlite_orm { context_t context{this->db_objects}; statement_serializer serializer; const std::string sql = serializer.serialize(table, context, tableName); - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } /** @@ -22729,7 +22724,7 @@ namespace sqlite_orm { << streaming_identifier(columnName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } #endif @@ -23698,7 +23693,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; const auto sql = serialize(virtualTable, context); - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); return res; } @@ -23709,7 +23704,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; const auto sql = serialize(index, context); - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); return res; } @@ -23720,7 +23715,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly context_t context{this->db_objects}; const auto sql = serialize(trigger, context); - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); return res; } @@ -23742,7 +23737,7 @@ namespace sqlite_orm { << serialize(column, context) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } template @@ -24913,7 +24908,7 @@ namespace sqlite_orm { << streaming_identifier(sourceTableName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql); + this->executor.perform_void_exec(db, sql.data()); } } } From 7aada905f8a6fdf8cd02c9488f4fafa04a0e366c Mon Sep 17 00:00:00 2001 From: Jackson Kaplan Date: Wed, 2 Apr 2025 12:19:07 -0700 Subject: [PATCH 168/170] run generator --- include/sqlite_orm/sqlite_orm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 100d5acd4..2dcfcc61f 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -14004,9 +14004,9 @@ namespace sqlite_orm { const std::function _didOpenDb; public: + const std::string filename; const std::string vfs_name; const db_open_mode open_mode; - const std::string filename; }; struct connection_ref { From 2a2600dd2410c760fd3658d9d59a20c2f83598b2 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sat, 5 Apr 2025 09:44:55 +0500 Subject: [PATCH 169/170] reverted perform step(s) implementation --- dev/storage.h | 8 ++++---- dev/util.h | 27 +++++++++++++++---------- include/sqlite_orm/sqlite_orm.h | 35 ++++++++++++++++++++------------- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index 2754abbaa..b82c1f4ce 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -320,8 +320,8 @@ namespace sqlite_orm { mapped_view iterate(Args&&... args) { this->assert_mapped_type(); - auto con = this->get_connection(); - return {*this, std::move(con), std::forward(args)...}; + auto connection = this->get_connection(); + return {*this, std::move(connection), std::forward(args)...}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -348,8 +348,8 @@ namespace sqlite_orm { requires (is_select_v) #endif result_set_view, db_objects_type> iterate(with_t expression) { - auto con = this->get_connection(); - return {this->db_objects, std::move(con), std::move(expression)}; + auto connection = this->get_connection(); + return {this->db_objects, std::move(connection), std::move(expression)}; } #endif #endif diff --git a/dev/util.h b/dev/util.h index b458f4dea..982040d0a 100644 --- a/dev/util.h +++ b/dev/util.h @@ -61,18 +61,17 @@ namespace sqlite_orm { template int perform_step(sqlite3_stmt* stmt, L&& lambda) { - const int rc = sqlite3_step(stmt); - switch (rc) { + switch (int rc = sqlite3_step(stmt)) { case SQLITE_ROW: { lambda(stmt); } break; case SQLITE_DONE: - break; + return rc; default: { throw_translated_sqlite_error(rc); } } - return rc; + return SQLITE_OK; } struct sqlite_executor { @@ -124,12 +123,20 @@ namespace sqlite_orm { if (this->will_run_query) { this->will_run_query(sql); } - int rc = 0; - do { - rc = internal::perform_step(stmt, lambda); - } while (rc != SQLITE_DONE); - if (this->did_run_query) { - this->did_run_query(sql); + for (;;) { + switch (int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + if (this->did_run_query) { + this->did_run_query(sql); + } + return; + default: { + throw_translated_sqlite_error(stmt); + } + } } } }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 4f373cf49..8e9d761b9 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13599,18 +13599,17 @@ namespace sqlite_orm { template int perform_step(sqlite3_stmt* stmt, L&& lambda) { - const int rc = sqlite3_step(stmt); - switch (rc) { + switch (int rc = sqlite3_step(stmt)) { case SQLITE_ROW: { lambda(stmt); } break; case SQLITE_DONE: - break; + return rc; default: { throw_translated_sqlite_error(rc); } } - return rc; + return SQLITE_OK; } struct sqlite_executor { @@ -13662,12 +13661,20 @@ namespace sqlite_orm { if (this->will_run_query) { this->will_run_query(sql); } - int rc = 0; - do { - rc = internal::perform_step(stmt, lambda); - } while (rc != SQLITE_DONE); - if (this->did_run_query) { - this->did_run_query(sql); + for (;;) { + switch (int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + if (this->did_run_query) { + this->did_run_query(sql); + } + return; + default: { + throw_translated_sqlite_error(stmt); + } + } } } }; @@ -22921,8 +22928,8 @@ namespace sqlite_orm { mapped_view iterate(Args&&... args) { this->assert_mapped_type(); - auto con = this->get_connection(); - return {*this, std::move(con), std::forward(args)...}; + auto connection = this->get_connection(); + return {*this, std::move(connection), std::forward(args)...}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES @@ -22949,8 +22956,8 @@ namespace sqlite_orm { requires (is_select_v) #endif result_set_view, db_objects_type> iterate(with_t expression) { - auto con = this->get_connection(); - return {this->db_objects, std::move(con), std::move(expression)}; + auto connection = this->get_connection(); + return {this->db_objects, std::move(connection), std::move(expression)}; } #endif #endif From 047762f2a9bda3233429e69a9ffa1100c492c56e Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sat, 5 Apr 2025 14:30:02 +0500 Subject: [PATCH 170/170] changed return type to void --- dev/util.h | 5 ++--- include/sqlite_orm/sqlite_orm.h | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dev/util.h b/dev/util.h index 982040d0a..8d12cdd0f 100644 --- a/dev/util.h +++ b/dev/util.h @@ -60,18 +60,17 @@ namespace sqlite_orm { namespace internal { template - int perform_step(sqlite3_stmt* stmt, L&& lambda) { + void perform_step(sqlite3_stmt* stmt, L&& lambda) { switch (int rc = sqlite3_step(stmt)) { case SQLITE_ROW: { lambda(stmt); } break; case SQLITE_DONE: - return rc; + return; default: { throw_translated_sqlite_error(rc); } } - return SQLITE_OK; } struct sqlite_executor { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 8e9d761b9..129ec5139 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13598,18 +13598,17 @@ namespace sqlite_orm { namespace internal { template - int perform_step(sqlite3_stmt* stmt, L&& lambda) { + void perform_step(sqlite3_stmt* stmt, L&& lambda) { switch (int rc = sqlite3_step(stmt)) { case SQLITE_ROW: { lambda(stmt); } break; case SQLITE_DONE: - return rc; + return; default: { throw_translated_sqlite_error(rc); } } - return SQLITE_OK; } struct sqlite_executor {