diff --git a/appveyor.yml b/appveyor.yml index ffc1cc5f1..473f66d65 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -108,7 +108,7 @@ for: install: - |- cd C:\Tools\vcpkg - git fetch --tags && git checkout 2024.06.15 + git fetch --tags && git checkout 2024.07.12 cd %APPVEYOR_BUILD_FOLDER% C:\Tools\vcpkg\bootstrap-vcpkg.bat -disableMetrics C:\Tools\vcpkg\vcpkg integrate install @@ -141,7 +141,7 @@ for: install: - |- pushd $HOME/vcpkg - git fetch --tags && git checkout 2024.06.15 + git fetch --tags && git checkout 2024.07.12 popd $HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics $HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets @@ -169,7 +169,7 @@ for: # using custom vcpkg triplets for building and linking dynamic dependent libraries install: - |- - git clone --depth 1 --branch 2024.06.15 https://github.com/microsoft/vcpkg.git $HOME/vcpkg + git clone --depth 1 --branch 2024.07.12 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 diff --git a/dev/alias.h b/dev/alias.h index 822b41be5..fd1541ee9 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 @@ -291,7 +291,7 @@ namespace sqlite_orm { polyfill::conjunction_v, internal::is_cte_moniker>>, bool> = true> constexpr auto alias_column(C c) { - using namespace internal; + using namespace ::sqlite_orm::internal; using cte_moniker_t = type_t; if constexpr(is_column_pointer_v) { diff --git a/dev/alias_traits.h b/dev/alias_traits.h index 5d64de530..4aaeee5f4 100644 --- a/dev/alias_traits.h +++ b/dev/alias_traits.h @@ -5,7 +5,6 @@ #include #endif -#include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" #include "type_traits.h" #include "table_reference.h" diff --git a/dev/arithmetic_tag.h b/dev/arithmetic_tag.h index 26570fff6..a89e7a2d8 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 5500e2820..b2a65c70f 100644 --- a/dev/ast/match.h +++ b/dev/ast/match.h @@ -1,5 +1,7 @@ #pragma once +#include // std::move + namespace sqlite_orm { namespace internal { diff --git a/dev/ast/where.h b/dev/ast/where.h index 29ca50021..ebf80b1c7 100644 --- a/dev/ast/where.h +++ b/dev/ast/where.h @@ -3,7 +3,6 @@ #include // std::false_type, std::true_type #include // std::move -#include "../functional/cxx_universal.h" #include "../functional/cxx_type_traits_polyfill.h" #include "../serialize_result_type.h" diff --git a/dev/collate_argument.h b/dev/collate_argument.h index 720886622..8de5e9976 100644 --- a/dev/collate_argument.h +++ b/dev/collate_argument.h @@ -10,5 +10,4 @@ namespace sqlite_orm { rtrim, }; } - } diff --git a/dev/column_result.h b/dev/column_result.h index 6ab442763..6bf9ad7d6 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -3,7 +3,6 @@ #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" diff --git a/dev/conditions.h b/dev/conditions.h index e1a1fccdc..2a9d3be77 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -6,8 +6,8 @@ #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" diff --git a/dev/constraints.h b/dev/constraints.h index eed1db455..9a6a41889 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -1,12 +1,11 @@ #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" #include "functional/mpl.h" #include "tuple_helper/same_or_void.h" diff --git a/dev/cte_column_names_collector.h b/dev/cte_column_names_collector.h index 8cee112e1..69bb9b5e0 100644 --- a/dev/cte_column_names_collector.h +++ b/dev/cte_column_names_collector.h @@ -7,7 +7,6 @@ #include #endif -#include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" #include "type_traits.h" #include "member_traits/member_traits.h" diff --git a/dev/cte_moniker.h b/dev/cte_moniker.h index 79e623ef9..ebd065aa3 100644 --- a/dev/cte_moniker.h +++ b/dev/cte_moniker.h @@ -10,7 +10,6 @@ #include #endif -#include "functional/cxx_universal.h" #include "functional/cstring_literal.h" #include "alias.h" diff --git a/dev/cte_storage.h b/dev/cte_storage.h index 61dca9102..023e6ebe8 100644 --- a/dev/cte_storage.h +++ b/dev/cte_storage.h @@ -7,7 +7,6 @@ #include #endif -#include "functional/cxx_universal.h" // ::size_t #include "tuple_helper/tuple_fy.h" #include "table_type_of.h" #include "column_result.h" diff --git a/dev/error_code.h b/dev/error_code.h index 7410c2687..662de285b 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -39,7 +39,6 @@ namespace sqlite_orm { value_is_null, no_tables_specified, }; - } namespace std { diff --git a/dev/expression.h b/dev/expression.h index 657bf56e2..1906f9a47 100644 --- a/dev/expression.h +++ b/dev/expression.h @@ -5,9 +5,9 @@ #include // std::move, std::forward, std::declval #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" #include "tags.h" +#include "operators.h" namespace sqlite_orm { diff --git a/dev/field_printer.h b/dev/field_printer.h index 6bd990348..5cab14456 100644 --- a/dev/field_printer.h +++ b/dev/field_printer.h @@ -10,7 +10,6 @@ #endif #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" #include "is_std_ptr.h" #include "type_traits.h" diff --git a/dev/function.h b/dev/function.h index b26335ffc..a0b4a29db 100644 --- a/dev/function.h +++ b/dev/function.h @@ -8,7 +8,6 @@ #include // std::min, std::copy_n #include // std::move, std::forward -#include "functional/cxx_universal.h" // ::size_t, ::nullptr_t #include "functional/cxx_type_traits_polyfill.h" #include "functional/cstring_literal.h" #include "functional/function_traits.h" diff --git a/dev/functional/cstring_literal.h b/dev/functional/cstring_literal.h index db1d8471f..f78eefe91 100644 --- a/dev/functional/cstring_literal.h +++ b/dev/functional/cstring_literal.h @@ -5,8 +5,6 @@ #include // std::copy_n #endif -#include "cxx_universal.h" // ::size_t - #ifdef SQLITE_ORM_WITH_CPP20_ALIASES namespace sqlite_orm::internal { /* 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/functional/cxx_tuple_polyfill.h b/dev/functional/cxx_tuple_polyfill.h index e77fe2ae5..d8fee12c3 100644 --- a/dev/functional/cxx_tuple_polyfill.h +++ b/dev/functional/cxx_tuple_polyfill.h @@ -5,7 +5,6 @@ #include // std::forward, std::index_sequence, std::make_index_sequence #endif -#include "../functional/cxx_universal.h" // ::size_t #include "../functional/cxx_functional_polyfill.h" // std::invoke namespace sqlite_orm { diff --git a/dev/functional/cxx_type_traits_polyfill.h b/dev/functional/cxx_type_traits_polyfill.h index e5c985696..31d99e444 100644 --- a/dev/functional/cxx_type_traits_polyfill.h +++ b/dev/functional/cxx_type_traits_polyfill.h @@ -1,7 +1,6 @@ #pragma once #include -#include "cxx_universal.h" #include "mpl/conditional.h" namespace sqlite_orm { diff --git a/dev/functional/index_sequence_util.h b/dev/functional/index_sequence_util.h index cf51a23f1..c0fec862e 100644 --- a/dev/functional/index_sequence_util.h +++ b/dev/functional/index_sequence_util.h @@ -2,8 +2,6 @@ #include // std::index_sequence -#include "../functional/cxx_universal.h" // ::size_t - namespace sqlite_orm { namespace internal { #if defined(SQLITE_ORM_PACK_INDEXING_SUPPORTED) diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index 06cb4b09e..61dd6edb2 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -35,7 +35,6 @@ #include #endif -#include "cxx_universal.h" // ::size_t #include "cxx_type_traits_polyfill.h" #include "mpl/conditional.h" diff --git a/dev/get_prepared_statement.h b/dev/get_prepared_statement.h index 10848fa43..2f7d0a246 100644 --- a/dev/get_prepared_statement.h +++ b/dev/get_prepared_statement.h @@ -3,7 +3,6 @@ #include // std::is_same, std::remove_reference, std::remove_cvref #include // std::get -#include "functional/cxx_universal.h" // ::size_t #include "functional/cxx_type_traits_polyfill.h" #include "functional/static_magic.h" #include "type_traits.h" 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..c7f6177a6 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -3,11 +3,11 @@ * 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 -#include "../functional/cxx_universal.h" // ::size_t #include "../type_printer.h" #include "../schema/column.h" #include "../schema/table.h" diff --git a/dev/indexed_column.h b/dev/indexed_column.h index e8bd3f1d4..daa36d488 100644 --- a/dev/indexed_column.h +++ b/dev/indexed_column.h @@ -3,7 +3,6 @@ #include // std::string #include // std::move -#include "functional/cxx_universal.h" #include "ast/where.h" namespace sqlite_orm { @@ -66,5 +65,4 @@ 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 dc48b1dc6..3570df780 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/mapped_iterator.h b/dev/mapped_iterator.h index 4b5d57ef8..82a23ed3d 100644 --- a/dev/mapped_iterator.h +++ b/dev/mapped_iterator.h @@ -7,7 +7,6 @@ #include // std::system_error #include // std::bind -#include "functional/cxx_universal.h" // ::ptrdiff_t #include "statement_finalizer.h" #include "error_code.h" #include "object_from_column_builder.h" diff --git a/dev/member_traits/member_traits.h b/dev/member_traits/member_traits.h index 652d58ade..eba948877 100644 --- a/dev/member_traits/member_traits.h +++ b/dev/member_traits/member_traits.h @@ -2,7 +2,6 @@ #include // std::enable_if, std::is_function, std::true_type, std::false_type -#include "../functional/cxx_universal.h" #include "../functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { diff --git a/dev/operators.h b/dev/operators.h index d6cd2b225..180747dbe 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -272,5 +272,4 @@ namespace sqlite_orm { internal::assign_t assign(L l, R r) { return {std::move(l), std::move(r)}; } - } diff --git a/dev/pragma.h b/dev/pragma.h index c30008cbc..8f9ab7d3d 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -1,11 +1,13 @@ #pragma once #include +#include // atoi #include // std::string #include // std::function #include // std::shared_ptr #include // std::vector #include +#include // std::flush #include "error_code.h" #include "row_extractor.h" @@ -148,14 +150,14 @@ namespace sqlite_orm { auto& res = *(std::vector*)data; if(argc) { auto index = 0; - auto cid = std::atoi(argv[index++]); + auto cid = atoi(argv[index++]); std::string name = argv[index++]; std::string type = argv[index++]; - bool notnull = !!std::atoi(argv[index++]); + bool notnull = !!atoi(argv[index++]); std::string dflt_value = argv[index] ? argv[index] : ""; ++index; - auto pk = std::atoi(argv[index++]); - auto hidden = std::atoi(argv[index++]); + auto pk = atoi(argv[index++]); + auto hidden = atoi(argv[index++]); res.emplace_back(cid, std::move(name), std::move(type), @@ -185,13 +187,13 @@ namespace sqlite_orm { auto& res = *(std::vector*)data; if(argc) { auto index = 0; - auto cid = std::atoi(argv[index++]); + auto cid = atoi(argv[index++]); std::string name = argv[index++]; std::string type = argv[index++]; - bool notnull = !!std::atoi(argv[index++]); + bool notnull = !!atoi(argv[index++]); std::string dflt_value = argv[index] ? argv[index] : ""; ++index; - auto pk = std::atoi(argv[index++]); + auto pk = atoi(argv[index++]); res.emplace_back(cid, std::move(name), std::move(type), notnull, std::move(dflt_value), pk); } return 0; diff --git a/dev/prepared_statement.h b/dev/prepared_statement.h index 9dbe7d0d1..5e79dc20c 100644 --- a/dev/prepared_statement.h +++ b/dev/prepared_statement.h @@ -8,7 +8,6 @@ #include // std::move, std::forward, std::pair #include // std::tuple -#include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" #include "functional/cxx_functional_polyfill.h" #include "tuple_helper/tuple_traits.h" diff --git a/dev/result_set_iterator.h b/dev/result_set_iterator.h index e2430bc3b..32a5f7742 100644 --- a/dev/result_set_iterator.h +++ b/dev/result_set_iterator.h @@ -5,7 +5,6 @@ #include // std::input_iterator_tag, std::default_sentinel_t #include // std::reference_wrapper -#include "functional/cxx_universal.h" // ::ptrdiff_t #include "statement_finalizer.h" #include "row_extractor.h" #include "column_result_proxy.h" diff --git a/dev/row_extractor.h b/dev/row_extractor.h index 42becde10..e18598afc 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 // atof, atoi, atoll +#include // 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,6 @@ #include #endif -#include "functional/cxx_universal.h" #include "functional/cxx_functional_polyfill.h" #include "functional/static_magic.h" #include "tuple_helper/tuple_transformer.h" @@ -389,7 +388,7 @@ 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 ? 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 45c95a367..c3967dd83 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -6,7 +6,6 @@ #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 "../tuple_helper/tuple_traits.h" #include "../tuple_helper/tuple_filter.h" diff --git a/dev/schema/index.h b/dev/schema/index.h index a846e51d9..63c6846ea 100644 --- a/dev/schema/index.h +++ b/dev/schema/index.h @@ -4,7 +4,6 @@ #include // std::string #include // std::forward -#include "../functional/cxx_universal.h" #include "../tuple_helper/tuple_traits.h" #include "../indexed_column.h" #include "../table_type_of.h" diff --git a/dev/schema/table.h b/dev/schema/table.h index 055e70be5..aa569c0cf 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -6,7 +6,6 @@ #include // std::tuple_element #include // std::forward, std::move -#include "../functional/cxx_universal.h" // ::size_t #include "../functional/cxx_type_traits_polyfill.h" #include "../functional/cxx_functional_polyfill.h" #include "../functional/static_magic.h" @@ -22,6 +21,7 @@ #include "../alias_traits.h" #include "../constraints.h" #include "../table_info.h" +#include "index.h" #include "column.h" namespace sqlite_orm { diff --git a/dev/schema/triggers.h b/dev/schema/triggers.h index a090c4015..5ec53e38b 100644 --- a/dev/schema/triggers.h +++ b/dev/schema/triggers.h @@ -5,7 +5,6 @@ #include #include -#include "../functional/cxx_universal.h" #include "../optional_container.h" // NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 4b7226b1e..0de6af43d 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -3,13 +3,12 @@ #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 #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" // ::size_t #include "functional/cxx_type_traits_polyfill.h" #include "is_base_of_template.h" #include "tuple_helper/tuple_traits.h" @@ -21,6 +20,7 @@ #include "core_functions.h" #include "alias_traits.h" #include "cte_moniker.h" +#include "schema/column.h" namespace sqlite_orm { diff --git a/dev/serializing_util.h b/dev/serializing_util.h index abfcfe0d0..407c3eb4e 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -5,15 +5,17 @@ #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" +#include "functional/cxx_functional_polyfill.h" #include "tuple_helper/tuple_iteration.h" +#include "type_traits.h" #include "error_code.h" #include "serializer_context.h" #include "serialize_result_type.h" #include "util.h" +#include "schema/column.h" namespace sqlite_orm { namespace internal { diff --git a/dev/statement_binder.h b/dev/statement_binder.h index 0473f7943..a3a979c47 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -5,17 +5,16 @@ #include // std::default_delete #include // std::string, std::wstring #include // std::vector -#include // ::strncpy, ::strlen +#include // strncpy, strlen #include "functional/cxx_string_view.h" #ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED -#include // ::wcsncpy, ::wcslen +#include // wcsncpy, wcslen #endif #ifndef SQLITE_ORM_OMITS_CODECVT #include // std::wstring_convert #include // std::codecvt_utf8_utf16 #endif -#include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" #include "functional/cxx_functional_polyfill.h" #include "is_std_ptr.h" @@ -140,7 +139,7 @@ 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); + strncpy(dataCopy, stringData.first, stringData.second + 1); sqlite3_result_text(context, dataCopy, stringData.second, obtain_xdestroy_for(deleter, dataCopy)); } @@ -155,7 +154,7 @@ namespace sqlite_orm { } std::pair string_data(const char* s) const { - return {s, int(::strlen(s))}; + return {s, int(strlen(s))}; } #endif }; @@ -194,7 +193,7 @@ namespace sqlite_orm { } std::pair string_data(const wchar_t* s) const { - return {s, int(::wcslen(s))}; + return {s, int(wcslen(s))}; } #endif }; diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 0e7249257..2131c1273 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1,8 +1,8 @@ #pragma once +#include // std::enable_if, std::remove_pointer #include // std::stringstream #include // std::string -#include // std::enable_if, std::remove_pointer #include // std::vector #ifndef SQLITE_ORM_OMITS_CODECVT #include // std::wstring_convert @@ -14,7 +14,6 @@ #include "functional/cxx_string_view.h" #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" #include "functional/cxx_functional_polyfill.h" #include "functional/mpl.h" #include "tuple_helper/tuple_filter.h" @@ -28,7 +27,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 +43,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 27f3f7c0c..c2370ec8a 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 @@ -14,7 +15,6 @@ #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" #include "functional/mpl.h" diff --git a/dev/storage_base.h b/dev/storage_base.h index 20a8d93f2..187824aab 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -1,10 +1,12 @@ #pragma once #include +#include // atoi #include // std::allocator #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 @@ -14,7 +16,6 @@ #include // std::is_same #include // std::find_if, std::ranges::find -#include "functional/cxx_universal.h" // ::size_t #include "functional/cxx_tuple_polyfill.h" // std::apply #include "tuple_helper/tuple_iteration.h" #include "pragma.h" @@ -30,6 +31,7 @@ #include "xdestroy_handling.h" #include "udf_proxy.h" #include "serializing_util.h" +#include "table_info.h" namespace sqlite_orm { @@ -168,7 +170,7 @@ namespace sqlite_orm { [](void* data, int argc, char** argv, char** /*azColName*/) -> int { auto& res = *(bool*)data; if(argc) { - res = !!std::atoi(argv[0]); + res = !!atoi(argv[0]); } return 0; }, diff --git a/dev/storage_impl.h b/dev/storage_impl.h index be629ced5..74e75b727 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -2,7 +2,6 @@ #include // std::string -#include "functional/cxx_universal.h" // ::size_t #include "functional/static_magic.h" #include "functional/index_sequence_util.h" #include "tuple_helper/tuple_traits.h" @@ -11,6 +10,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/storage_lookup.h b/dev/storage_lookup.h index 4992e602e..a4f097529 100644 --- a/dev/storage_lookup.h +++ b/dev/storage_lookup.h @@ -4,7 +4,6 @@ #include #include // std::index_sequence, std::make_index_sequence -#include "functional/cxx_universal.h" // ::size_t #include "functional/cxx_type_traits_polyfill.h" #include "type_traits.h" diff --git a/dev/table_info.h b/dev/table_info.h index 087a3c6f2..975b203eb 100644 --- a/dev/table_info.h +++ b/dev/table_info.h @@ -2,8 +2,6 @@ #include // std::string -#include "functional/cxx_universal.h" - namespace sqlite_orm { struct table_info { diff --git a/dev/table_reference.h b/dev/table_reference.h index 8b20c9162..0a6aba495 100644 --- a/dev/table_reference.h +++ b/dev/table_reference.h @@ -1,9 +1,6 @@ #pragma once #include // std::remove_const, std::type_identity -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED -#include -#endif #include "functional/cxx_type_traits_polyfill.h" 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..ce501760b 100644 --- a/dev/tuple_helper/tuple_filter.h +++ b/dev/tuple_helper/tuple_filter.h @@ -1,9 +1,8 @@ #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" #include "../functional/index_sequence_util.h" diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index afea35007..e1d7c9662 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -4,8 +4,6 @@ #include // std::index_sequence, std::make_index_sequence #include // std::forward, std::move -#include "../functional/cxx_universal.h" // ::size_t - namespace sqlite_orm { namespace internal { #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index a4333a0d3..b96ba0f7b 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -3,7 +3,6 @@ #include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval #include // std::tuple_size, std::get -#include "../functional/cxx_universal.h" // ::size_t #include "../functional/cxx_type_traits_polyfill.h" #include "../functional/cxx_functional_polyfill.h" #include "../functional/mpl.h" 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 68ce9639d..02d347255 100644 --- a/dev/values.h +++ b/dev/values.h @@ -2,9 +2,8 @@ #include // std::vector #include // std::tuple -#include // std::forward +#include // std::forward, std::move -#include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { @@ -40,5 +39,4 @@ namespace sqlite_orm { internal::dynamic_values_t values(std::vector vector) { return {{std::move(vector)}}; } - } diff --git a/dev/values_to_tuple.h b/dev/values_to_tuple.h index 76f7af3b6..83c5be4cf 100644 --- a/dev/values_to_tuple.h +++ b/dev/values_to_tuple.h @@ -4,7 +4,6 @@ #include // std::enable_if, std::is_same, std::index_sequence, std::make_index_sequence #include // std::tuple, std::tuple_size, std::tuple_element -#include "functional/cxx_universal.h" // ::size_t #include "functional/cxx_functional_polyfill.h" #include "type_traits.h" #include "row_extractor.h" diff --git a/dev/xdestroy_handling.h b/dev/xdestroy_handling.h index 0b4b40afd..4d73977c4 100644 --- a/dev/xdestroy_handling.h +++ b/dev/xdestroy_handling.h @@ -5,7 +5,6 @@ #include #endif -#include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { diff --git a/examples/blob_binding.cpp b/examples/blob_binding.cpp index 2d7e7fffe..af23f5e72 100644 --- a/examples/blob_binding.cpp +++ b/examples/blob_binding.cpp @@ -1,4 +1,6 @@ +#include #include +#include #include using namespace sqlite_orm; @@ -66,7 +68,7 @@ namespace sqlite_orm { std::vector blobValue; blobValue.reserve(16); auto encodeInteger = [&blobValue](int value) { - auto preciseValue = int32_t(value); + auto preciseValue = std::int32_t(value); const auto intPointer = &preciseValue; auto charPointer = (const char*)(intPointer); blobValue.push_back(charPointer[0]); @@ -110,7 +112,7 @@ namespace sqlite_orm { Rect value; auto decodeInteger = [charPointer](int& integer, int index) { auto pointerWithOffset = charPointer + index * 4; - auto intPointer = (const int32_t*)pointerWithOffset; + auto intPointer = (const std::int32_t*)pointerWithOffset; integer = int(*intPointer); }; decodeInteger(value.x, 0); diff --git a/examples/chrono_binding.cpp b/examples/chrono_binding.cpp index d887b8071..65b500d73 100644 --- a/examples/chrono_binding.cpp +++ b/examples/chrono_binding.cpp @@ -1,4 +1,10 @@ +#include #include +#ifdef __has_include +#if __has_include() +#include +#endif +#endif #if __cpp_lib_chrono >= 201907L && __cpp_lib_format >= 201907L #define ENABLE_THIS_EXAMPLE #endif diff --git a/examples/collate.cpp b/examples/collate.cpp index 426790019..77748426a 100644 --- a/examples/collate.cpp +++ b/examples/collate.cpp @@ -11,7 +11,7 @@ using std::endl; struct User { int id; std::string name; - time_t createdAt; + std::time_t createdAt; }; struct Foo { diff --git a/examples/common_table_expressions.cpp b/examples/common_table_expressions.cpp index 978e141db..e45120d2d 100644 --- a/examples/common_table_expressions.cpp +++ b/examples/common_table_expressions.cpp @@ -286,8 +286,8 @@ void family_tree() { std::string name; std::optional mom; std::optional dad; - time_t born; - std::optional died; + std::time_t born; + std::optional died; }; auto storage = make_storage("", @@ -915,7 +915,7 @@ void neevek_issue_222() { struct user_activity { int64 id; int64 uid; - time_t timestamp; + std::time_t timestamp; }; auto storage = make_storage("", @@ -925,7 +925,7 @@ void neevek_issue_222() { make_column("timestamp", &user_activity::timestamp))); storage.sync_schema(); storage.transaction([&storage]() { - time_t now = std::time(nullptr); + std::time_t now = std::time(nullptr); auto values = {user_activity{0, 1, now - 86400 * 3}, user_activity{0, 1, now - 86400 * 2}, user_activity{0, 1, now}, @@ -993,7 +993,7 @@ void greatest_n_per_group() { struct some_result { int64 result_id; int64 item_id; - time_t timestamp; + std::time_t timestamp; bool flag; }; @@ -1006,7 +1006,7 @@ void greatest_n_per_group() { make_column("flag", &some_result::flag))); storage.sync_schema(); storage.transaction([&storage]() { - time_t now = std::time(nullptr); + std::time_t now = std::time(nullptr); auto values = std::initializer_list{{-1, 1, now - 86400 * 3, false}, {-1, 1, now - 86400 * 2, true}, {-1, 1, now, true}, diff --git a/examples/composite_key.cpp b/examples/composite_key.cpp index 08b77a842..2335bbfbd 100644 --- a/examples/composite_key.cpp +++ b/examples/composite_key.cpp @@ -27,7 +27,7 @@ struct User { struct UserVisit { int userId; std::string userFirstName; - time_t time; + std::time_t time; }; #endif diff --git a/examples/enum_binding.cpp b/examples/enum_binding.cpp index 0bee82ca8..4982d4cf8 100644 --- a/examples/enum_binding.cpp +++ b/examples/enum_binding.cpp @@ -1,4 +1,4 @@ - +#include #include #include diff --git a/examples/iteration.cpp b/examples/iteration.cpp index 1ca54b8d3..cd183ce4d 100644 --- a/examples/iteration.cpp +++ b/examples/iteration.cpp @@ -35,9 +35,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()) { @@ -51,7 +51,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/examples/nullable_enum_binding.cpp b/examples/nullable_enum_binding.cpp index 375097144..bbd8f0185 100644 --- a/examples/nullable_enum_binding.cpp +++ b/examples/nullable_enum_binding.cpp @@ -1,4 +1,4 @@ - +#include #include #include #include diff --git a/examples/synchronous.cpp b/examples/synchronous.cpp index 6147efff2..d506bf848 100644 --- a/examples/synchronous.cpp +++ b/examples/synchronous.cpp @@ -1,15 +1,15 @@ - #include +#include #include struct Query { std::string src_ip; - uint16_t src_port; - uint16_t txn_id; - uint32_t tv_sec; - uint32_t tv_usec; + std::uint16_t src_port; + std::uint16_t txn_id; + std::uint32_t tv_sec; + std::uint32_t tv_usec; std::string name; - uint16_t type; + std::uint16_t type; }; int main(int, char**) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index adf7767bd..84f58b6d1 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -278,20 +278,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_functional_polyfill.h" -#include +#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_universal.h" +// #include "cxx_type_traits_polyfill.h" + +#include // #include "mpl/conditional.h" @@ -476,163 +498,249 @@ 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_type_traits_polyfill.h" +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; + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) + template + struct setter_field_type {}; - template - using field_type_t = typename T::field_type; + template + using setter_field_type_t = typename setter_field_type::type; - template - using constraints_type_t = typename T::constraints_type; + template + struct setter_field_type : setter_field_type {}; - template - using columns_tuple_t = typename T::columns_tuple; + template + struct setter_field_type : polyfill::remove_cvref {}; - template - using object_type_t = typename T::object_type; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct setter_field_type : polyfill::remove_cvref {}; +#endif - template - using elements_type_t = typename T::elements_type; + template + struct is_getter : std::false_type {}; + template + struct is_getter>> : std::true_type {}; - template - using table_type_t = typename T::table_type; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter::value; - template - using target_type_t = typename T::target_type; + template + struct is_setter : std::false_type {}; + template + struct is_setter>> : std::true_type {}; - template - using left_type_t = typename T::left_type; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter::value; - template - using right_type_t = typename T::right_type; + template + struct member_field_type : object_field_type, getter_field_type, setter_field_type {}; - template - using on_type_t = typename T::on_type; + template + using member_field_type_t = typename member_field_type::type; - template - using expression_type_t = typename T::expression_type; + template + struct member_object_type {}; - template - using alias_type_t = typename As::alias_type; + template + struct member_object_type : polyfill::type_identity {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES template - using udf_type_t = typename T::udf_type; + using member_object_type_t = typename member_object_type::type; + } +} - template - using auto_udf_type_t = typename decltype(a)::udf_type; -#endif +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); + } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template - using cte_moniker_type_t = typename T::cte_moniker_type; + using is_transparent = int; + }; +#endif - template - using cte_mapper_type_t = typename T::cte_mapper_type; +#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; + } - // T::alias_type or nonesuch - template - using alias_holder_type_or_none = polyfill::detected; + // 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)...); + } - template - using alias_holder_type_or_none_t = typename alias_holder_type_or_none::type; -#endif + // 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_CPP20_CONCEPTS_SUPPORTED - template - concept stateless = std::is_empty_v; + // functor + template + decltype(auto) invoke(Callable&& callable, Args&&... args) { + return std::forward(callable)(std::forward(args)...); + } #endif + } } -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - template - concept orm_names_type = requires { typename T::type; }; -#endif + namespace polyfill = internal::polyfill; } -#pragma once -namespace sqlite_orm { +// #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 { - enum class collate_argument { - binary, - nocase, - rtrim, + // 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{}; -} -#pragma once +#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); + } + } -#include // std::system_error -#include // std::ostream -#include // std::string -#include // std::tuple -#include // std::is_base_of, std::false_type, std::true_type + template + decltype(auto) static_if([[maybe_unused]] T&& trueFn) { + if constexpr(B) { + return std::forward(trueFn); + } else { + return empty_callable<>; + } + } -// #include "functional/cxx_universal.h" + 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); + } -// #include "functional/cxx_type_traits_polyfill.h" + 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" @@ -671,8 +779,6 @@ namespace sqlite_orm { #include #endif -// #include "cxx_universal.h" -// ::size_t // #include "cxx_type_traits_polyfill.h" // #include "mpl/conditional.h" @@ -1180,53 +1286,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,19 +1356,14 @@ 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 // #include "../functional/mpl/conditional.h" // #include "../functional/index_sequence_util.h" #include // std::index_sequence -// #include "../functional/cxx_universal.h" -// ::size_t - namespace sqlite_orm { namespace internal { #if defined(SQLITE_ORM_PACK_INDEXING_SUPPORTED) @@ -1458,4259 +1512,4620 @@ 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_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"; - } + /* + * 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{}; + } + }; - std::string message(int c) const override final { - return sqlite3_errstr(c); - } - }; + 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 orm_error_category& get_orm_error_category() { - static orm_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 const sqlite_error_category& get_sqlite_error_category() { - static sqlite_error_category res; - return res; + /* + * 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(sqlite_errc ev) noexcept { - return {static_cast(ev), get_sqlite_error_category()}; - } +// #include "tuple_helper/tuple_iteration.h" - inline std::error_code make_error_code(orm_error_code ev) noexcept { - return {static_cast(ev), get_orm_error_category()}; - } +#include // std::get, std::tuple_element, std::tuple_size +#include // std::index_sequence, std::make_index_sequence +#include // std::forward, std::move - 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(); - } +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*/) {} - 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)...)}; - } + 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)); + } - inline std::system_error sqlite_to_system_error(int ev) { - return {sqlite_errc(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)); + } - inline std::system_error sqlite_to_system_error(sqlite3* db) { - return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(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(int ev) { - throw sqlite_to_system_error(ev); + /* + * 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)}; + } } +} - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { - throw sqlite_to_system_error(db); - } +// #include "type_traits.h" - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { - throw sqlite_to_system_error(sqlite3_db_handle(stmt)); - } -} +#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 -// #include "table_type_of.h" +// #include "functional/cxx_core_features.h" -#include // std::enable_if, std::is_convertible +// #include "functional/cxx_type_traits_polyfill.h" 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; - }; +#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; + } - template - struct primary_key_with_autoincrement : T { - using primary_key_type = T; + constexpr cstring_literal(const char (&cstr)[N]) { + std::copy_n(cstr, N, this->cstr); + } - 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 - }; + char cstr[N]; + }; - /** - * 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; - - columns_tuple columns; - - primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} - - self asc() const { - auto res = *this; - res.options.asc_option = order_by::ascending; - return res; - } + template class Template, cstring_literal literal, size_t... Idx> + consteval auto explode_into(std::index_sequence) { + return Template{}; + } +} +#endif - self desc() const { - auto res = *this; - res.options.asc_option = order_by::descending; - return res; - } +// #include "type_traits.h" - primary_key_with_autoincrement autoincrement() const { - return {*this}; - } +// #include "alias_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 // std::is_base_of, std::is_same +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include +#endif - 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 - struct unique_base { - operator std::string() const { - return "UNIQUE"; - } - }; +// #include "functional/cxx_type_traits_polyfill.h" - /** - * UNIQUE constraint class. +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; +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, - }; - - 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; - } + template + struct is_recordset_alias : polyfill::bool_constant> {}; - struct on_update_delete_base { - const bool update; // true if update and false if delete + /** @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; - operator std::string() const { - if(this->update) { - return "ON UPDATE"; - } else { - return "ON DELETE"; - } - } - }; + template + struct is_table_alias : polyfill::bool_constant> {}; - /** - * 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; - } - - 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; - } - - 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; - } - - 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; - } - - 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 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 bool() const { - return this->_action != foreign_key_action::none; - } - }; + /** @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); - 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 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 foreign_key_t, std::tuple> { - using columns_type = std::tuple; - using references_type = std::tuple; - using self = foreign_key_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>); - /** - * Holds obect type of all referenced columns. - */ - using target_type = same_or_void_t...>; + /** @short Specifies that a type refers to a mapped table (possibly aliased). + */ + template + concept orm_refers_to_table = (orm_table_reference || orm_table_alias); - /** - * Holds obect type of all source columns. - */ - using source_type = same_or_void_t...>; + /** @short Specifies that a type refers to a recordset. + */ + template + concept orm_refers_to_recordset = (orm_table_reference || orm_recordset_alias); - columns_type columns; - references_type references; + /** @short Specifies that a type is a mapped recordset (table reference). + */ + template + concept orm_mapped_recordset = (orm_table_reference || orm_cte_moniker); +#endif +} - on_update_delete_t on_update; - on_update_delete_t on_delete; +// #include "table_type_of.h" - 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"); +#include // std::enable_if, std::is_convertible - 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) {} +namespace sqlite_orm { - 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) {} + namespace internal { - 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 column_pointer; - 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 indexed_column_t; /** - * 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 + * 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 foreign_key_intermediate_t { - using tuple_type = std::tuple; - - tuple_type columns; + template + struct table_type_of; - template - foreign_key_t, std::tuple> references(Rs... refs) { - return {std::move(this->columns), {std::forward(refs)...}}; - } + template + struct table_type_of { + using type = O; }; -#endif - - 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); - } - 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}; - } + template + struct table_type_of> { + using type = T; }; - template - struct check_t { - using expression_type = T; + template + struct table_type_of> : table_type_of {}; - expression_type expression; - }; + template + using table_type_of_t = typename table_type_of::type; - struct basic_generated_always { - enum class storage_type { - not_specified, - virtual_, - stored, - }; + /* + * 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; -#if SQLITE_VERSION_NUMBER >= 3031000 - bool full = true; - storage_type storage = storage_type::not_specified; -#endif + /* + * `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; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} -#endif - }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; + } +} -#if SQLITE_VERSION_NUMBER >= 3031000 - template - struct generated_always_t : basic_generated_always { - using expression_type = T; +// #include "tags.h" - expression_type expression; +// #include "functional/cxx_functional_polyfill.h" - generated_always_t(expression_type expression_, bool full, storage_type storage) : - basic_generated_always{full, storage}, expression(std::move(expression_)) {} +namespace sqlite_orm { + namespace internal { + struct negatable_t {}; - generated_always_t virtual_() { - return {std::move(this->expression), this->full, storage_type::virtual_}; - } + /** + * Inherit from this class to support arithmetic types overloading + */ + struct arithmetic_t {}; - generated_always_t stored() { - return {std::move(this->expression), this->full, storage_type::stored}; - } - }; -#endif + /** + * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + */ + struct condition_t {}; - struct null_t {}; + /** + * 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; - struct not_null_t {}; + template + using is_operator_argument = polyfill::bool_constant>; } +} - namespace internal { +// #include "column_pointer.h" - template - SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = -#if SQLITE_VERSION_NUMBER >= 3006019 - polyfill::is_specialization_of::value; -#else - false; -#endif +#include // std::enable_if, std::is_convertible +#include // std::move - template - struct is_foreign_key : polyfill::bool_constant> {}; +// #include "functional/cxx_core_features.h" - template - SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = std::is_base_of::value; +// #include "functional/cxx_type_traits_polyfill.h" - template - struct is_primary_key : polyfill::bool_constant> {}; +// #include "type_traits.h" - template - SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = -#if SQLITE_VERSION_NUMBER >= 3031000 - polyfill::is_specialization_of::value; -#else - false; -#endif +// #include "table_reference.h" - template - struct is_generated_always : polyfill::bool_constant> {}; +// #include "alias_traits.h" + +// #include "tags.h" +namespace sqlite_orm { + namespace internal { /** - * PRIMARY KEY INSERTABLE traits. + * 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 is_primary_key_insertable - : polyfill::disjunction< - mpl::invoke_t, - check_if_has_template>, - constraints_type_t>, - std::is_base_of>>> { + template + struct column_pointer { + using type = T; + using field_type = F; - static_assert(tuple_has, is_primary_key>::value, - "an unexpected type was passed"); + field_type field; }; 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>; - } + SQLITE_ORM_INLINE_VAR constexpr bool is_column_pointer_v = + polyfill::is_specialization_of::value; -#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 + struct is_column_pointer : polyfill::bool_constant> {}; - template - internal::generated_always_t as(T expression) { - return {std::move(expression), false, internal::basic_generated_always::storage_type::not_specified}; - } + 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 /** - * 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; - /** - * 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)}; - } + static_assert(is_cte_moniker_v, "`Moniker' must be a CTE moniker"); - /** - * 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 {}; + 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; } -#endif +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * PRIMARY KEY table constraint builder function. + * 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::primary_key_t primary_key(Cs... cs) { - return {{std::forward(cs)...}}; + template + constexpr auto column(F field) { + using Moniker = std::remove_const_t; + return column(std::forward(field)); } /** - * PRIMARY KEY column constraint builder function. + * 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)); */ - inline internal::primary_key_t<> primary_key() { - return {{}}; + template + constexpr auto operator->*(const Moniker& /*moniker*/, F field) { + return column(std::forward(field)); } - - 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 {}; - } -} -#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" +#endif +#endif +} 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; - } - }; - - /** - * 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 {}; + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = true; - template - using object_field_type_t = typename object_field_type::type; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + struct recordset_alias_builder { + template + [[nodiscard]] consteval recordset_alias for_() const { + return {}; + } - template - struct object_field_type : std::enable_if::value, F> {}; + 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 return type) - template - struct getter_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 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 {}; - -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct getter_field_type : polyfill::remove_cvref {}; - - template - struct getter_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 + } - // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) - template - struct setter_field_type {}; - - template - using setter_field_type_t = typename setter_field_type::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 - struct setter_field_type : setter_field_type {}; + return alias_column_t{std::move(field)}; + } - template - struct setter_field_type : polyfill::remove_cvref {}; + /** + * 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"); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct setter_field_type : polyfill::remove_cvref {}; -#endif + using C1 = + mpl::conditional_t::value, F O::*, column_pointer>; + return alias_column_t{C1{field}}; + } - template - struct is_getter : std::false_type {}; - template - struct is_getter>> : std::true_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 - SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter::value; + 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 is_setter : std::false_type {}; - template - struct is_setter>> : std::true_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 - SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter::value; +#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 ::sqlite_orm::internal; + using cte_moniker_t = type_t; - template - struct member_field_type : object_field_type, getter_field_type, setter_field_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)}; + } + } - template - using member_field_type_t = typename member_field_type::type; +#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)); + } - template - struct member_object_type {}; + /** + * 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 - template - struct member_object_type : polyfill::type_identity {}; + /** + * Alias a column expression. + */ + template = true> + internal::as_t as(E expression) { + return {std::move(expression)}; + } - template - using member_object_type_t = typename member_object_type::type; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Alias a column expression. + */ + template + auto as(E expression) { + return internal::as_t{std::move(expression)}; } -} -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; + /** + * Alias a column expression. + */ + template + internal::as_t operator>>=(E expression, const A&) { + return {std::move(expression)}; + } #else - struct identity { - template - constexpr T&& operator()(T&& v) const noexcept { - return std::forward(v); - } - - using is_transparent = int; - }; + /** + * Alias a column expression. + */ + template = true> + internal::as_t operator>>=(E expression, const A&) { + return {std::move(expression)}; + } #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; - } + /** + * Wrap a column alias in an alias holder. + */ + template + internal::alias_holder get() { + static_assert(internal::is_column_alias_v, ""); + return {}; + } - // 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 - } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto get() { + return internal::alias_holder{}; } +#endif - namespace polyfill = internal::polyfill; -} + 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; -namespace sqlite_orm { - namespace internal { - struct negatable_t {}; + 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'>; - /** - * Inherit from this class to support arithmetic types overloading +#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{}; + } } -} - -// #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 +// #include "error_code.h" -#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include +#include // std::error_code, std::system_error #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 - } -} +#include +#include // std::ostringstream +#include namespace sqlite_orm { - namespace internal { - - template - struct binary_operator : Ds... { - using left_type = L; - using right_type = R; - - left_type lhs; - right_type rhs; + /** @short Enables classifying sqlite error codes. - binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} - }; + @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 {}; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_of_template::value; + 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, + }; +} - template - using is_binary_operator = polyfill::bool_constant>; +namespace std { + template<> + struct is_error_code_enum<::sqlite_orm::sqlite_errc> : true_type {}; - struct conc_string { - serialize_result_type serialize() const { - return "||"; - } - }; + template<> + struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; +} - /** - * Result of concatenation || operator - */ - template - using conc_t = binary_operator; +namespace sqlite_orm { - struct add_string { - serialize_result_type serialize() const { - return "+"; - } - }; + class orm_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "ORM error"; + } - /** - * Result of addition + operator - */ - template - using add_t = binary_operator; - - struct sub_string { - serialize_result_type serialize() const { - return "-"; + 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"; } - }; + } + }; - /** - * Result of substitute - operator - */ - template - using sub_t = binary_operator; + class sqlite_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "SQLite error"; + } - struct mul_string { - serialize_result_type serialize() const { - return "*"; - } - }; + std::string message(int c) const override final { + return sqlite3_errstr(c); + } + }; - /** - * Result of multiply * operator - */ - template - using mul_t = binary_operator; + inline const orm_error_category& get_orm_error_category() { + static orm_error_category res; + return res; + } - struct div_string { - serialize_result_type serialize() const { - return "/"; - } - }; + inline const sqlite_error_category& get_sqlite_error_category() { + static sqlite_error_category res; + return res; + } - /** - * Result of divide / operator - */ - template - using div_t = binary_operator; + inline std::error_code make_error_code(sqlite_errc ev) noexcept { + return {static_cast(ev), get_sqlite_error_category()}; + } - struct mod_operator_string { - serialize_result_type serialize() const { - return "%"; - } - }; + inline std::error_code make_error_code(orm_error_code ev) noexcept { + return {static_cast(ev), get_orm_error_category()}; + } - /** - * Result of mod % operator - */ - template - using mod_t = binary_operator; + 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(); + } - struct bitwise_shift_left_string { - serialize_result_type serialize() const { - return "<<"; - } - }; + 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)...)}; + } - /** - * Result of bitwise shift left << operator - */ - template - using bitwise_shift_left_t = binary_operator; + inline std::system_error sqlite_to_system_error(int ev) { + return {sqlite_errc(ev)}; + } - struct bitwise_shift_right_string { - serialize_result_type serialize() const { - return ">>"; - } - }; + inline std::system_error sqlite_to_system_error(sqlite3* db) { + return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(db)}; + } - /** - * Result of bitwise shift right >> operator - */ - template - using bitwise_shift_right_t = binary_operator; + [[noreturn]] inline void throw_translated_sqlite_error(int ev) { + throw sqlite_to_system_error(ev); + } - struct bitwise_and_string { - serialize_result_type serialize() const { - return "&"; - } - }; + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { + throw sqlite_to_system_error(db); + } - /** - * Result of bitwise and & operator - */ - template - using bitwise_and_t = binary_operator; + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { + throw sqlite_to_system_error(sqlite3_db_handle(stmt)); + } +} - struct bitwise_or_string { - serialize_result_type serialize() const { - return "|"; - } - }; +// #include "type_printer.h" - /** - * Result of bitwise or | operator - */ - template - using bitwise_or_t = binary_operator; +#include // std::string +#include // std::shared_ptr, std::unique_ptr +#include // std::vector +// #include "functional/cxx_optional.h" - struct bitwise_not_string { - serialize_result_type serialize() const { - return "~"; - } - }; +// #include "functional/cxx_type_traits_polyfill.h" - /** - * Result of bitwise not ~ operator - */ - template - struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { - using argument_type = T; +// #include "type_traits.h" - argument_type argument; +// #include "is_std_ptr.h" - bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} - }; +#include +#include - struct assign_string { - serialize_result_type serialize() const { - return "="; - } - }; +namespace sqlite_orm { - /** - * Result of assign = operator - */ - template - using assign_t = binary_operator; + /** + * Specialization for optional type (std::shared_ptr / std::unique_ptr). + */ + template + struct is_std_ptr : std::false_type {}; - /** - * Assign operator traits. Common case - */ - template - struct is_assign_t : public std::false_type {}; + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::shared_ptr::element_type; - /** - * Assign operator traits. Specialized case - */ - template - struct is_assign_t> : public std::true_type {}; - } + static std::shared_ptr make(std::remove_cv_t&& v) { + return std::make_shared(std::move(v)); + } + }; + + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::unique_ptr::element_type; + + static auto make(std::remove_cv_t&& v) { + return std::make_unique(std::move(v)); + } + }; +} + +namespace sqlite_orm { /** - * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT - * name || '@gmail.com' FROM users + * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) */ - 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)}; - } - - /** - * 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)}; - } + 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 "../tuple_helper/tuple_traits.h" +// #include "constraints.h" -// #include "../tuple_helper/tuple_filter.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 "../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 { - - /** - * Column name. - */ - std::string name; - }; - - struct empty_setter {}; - - /* - * 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` + /** + * Accepts any number of arguments and evaluates a nested `type` typename as `T` if all arguments are the same, otherwise `void`. */ - 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; - - /** - * 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 { + using type = void; + }; - /** - * Setter member function to write a field value - */ - SQLITE_ORM_NOUNIQUEADDRESS - const setter_type setter; + template + struct same_or_void { + using type = A; + }; - /** - * Simplified interface for `NOT NULL` constraint - */ - constexpr bool is_not_null() const { - return !type_is_nullable::value; - } + template + struct same_or_void { + using type = A; }; - /* - * 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; + template + using same_or_void_t = typename same_or_void::type; - SQLITE_ORM_NOUNIQUEADDRESS - constraints_type constraints; + template + struct same_or_void : same_or_void {}; - /** - * Checks whether contraints contain specified type. - */ - template class Trait> - constexpr static bool is() { - return tuple_has::value; - } + template + struct common_type_of; - /** - * Simplified interface for `DEFAULT` constraint - * @return string representation of default value if it exists otherwise nullptr - */ - std::unique_ptr default_value() const; - }; + template class Pack, class... Types> + struct common_type_of> : std::common_type {}; /** - * Column definition. + * Accepts a pack of types and defines a nested `type` typename to a common type if possible, otherwise nonexistent. * - * It is a composition of orthogonal information stored in different base classes. + * @note: SFINAE friendly like `std::common_type`. */ - 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 - }; + template + using common_type_of_t = typename common_type_of::type; + } +} - template - struct column_field_expression { - using type = void; - }; +// #include "tuple_helper/tuple_traits.h" - template - struct column_field_expression, void> { - using type = typename column_t::member_pointer_t; - }; +// #include "tuple_helper/tuple_filter.h" - template - using column_field_expression_t = typename column_field_expression::type; +// #include "type_traits.h" - template - SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of::value; - - template - using is_column = polyfill::bool_constant>; +// #include "collate_argument.h" - template - using col_index_sequence_with_field_type = - filter_tuple_sequence_t::template fn, - field_type_t, - filter_tuple_sequence_t>; +namespace sqlite_orm { - template class TraitFn> - using col_index_sequence_with = filter_tuple_sequence_t::template fn, - constraints_type_t, - filter_tuple_sequence_t>; + namespace internal { - template class TraitFn> - using col_index_sequence_excluding = filter_tuple_sequence_t::template fn, - constraints_type_t, - filter_tuple_sequence_t>; + enum class collate_argument { + binary, + nocase, + rtrim, + }; } +} - /** - * 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"); - - // 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)...}}); - } +// #include "error_code.h" - /** - * 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"); +// #include "table_type_of.h" - // 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 "type_printer.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"); +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), getter, setter, std::tuple{std::move(constraints)...}}); - } -} -#pragma once + namespace internal { -#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 + enum class conflict_clause_t { + rollback, + abort, + fail, + ignore, + replace, + }; -// #include "functional/cxx_universal.h" + 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; + }; -// #include "functional/cxx_type_traits_polyfill.h" + template + struct primary_key_with_autoincrement : T { + using primary_key_type = T; -// #include "is_base_of_template.h" + 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 + }; -// #include "type_traits.h" + /** + * 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; -// #include "collate_argument.h" + columns_tuple columns; -// #include "constraints.h" + primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} -// #include "optional_container.h" + self asc() const { + auto res = *this; + res.options.asc_option = order_by::ascending; + return res; + } -namespace sqlite_orm { + self desc() const { + auto res = *this; + res.options.asc_option = order_by::descending; + return res; + } - namespace internal { + primary_key_with_autoincrement autoincrement() const { + return {*this}; + } - /** - * This is a cute class which allows storing something or nothing - * depending on template argument. Useful for optional class members - */ - template - struct optional_container { - using type = T; + 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; + } - type field; + 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; + } - template - void apply(const L& l) const { - l(this->field); + 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; } - }; - template<> - struct optional_container { - using type = void; + 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; + } - template - void apply(const L&) const { - //.. + 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 "serializer_context.h" + struct unique_base { + operator std::string() const { + return "UNIQUE"; + } + }; -namespace sqlite_orm { + /** + * UNIQUE constraint class. + */ + template + struct unique_t : unique_base { + using columns_tuple = std::tuple; - namespace internal { + columns_tuple columns; - struct serializer_context_base { - bool replace_bindable_with_question = false; - bool skip_table_name = true; - bool use_parentheses = true; - bool fts5_columns = false; + unique_t(columns_tuple columns_) : columns(std::move(columns_)) {} }; - template - struct serializer_context : serializer_context_base { - using db_objects_type = DBOs; + struct unindexed_t {}; - const db_objects_type& db_objects; + template + struct prefix_t { + using value_type = T; - serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} + value_type value; }; - template - struct serializer_context_builder { - using storage_type = S; - using db_objects_type = typename storage_type::db_objects_type; + template + struct tokenize_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)}; - } + template + struct content_t { + using value_type = T; - const storage_type& storage; + value_type value; }; - } -} + template + struct table_content_t { + using mapped_type = T; + }; -// #include "serialize_result_type.h" - -// #include "tags.h" - -// #include "table_reference.h" + /** + * DEFAULT constraint class. + * T is a value type. + */ + template + struct default_t { + using value_type = T; -#include // std::remove_const, std::type_identity -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED -#include -#endif + value_type value; -// #include "functional/cxx_type_traits_polyfill.h" + operator std::string() const { + return "DEFAULT"; + } + }; -namespace sqlite_orm { - namespace internal { - /* - * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. +#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 */ - 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 foreign_key_t; - 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 + enum class foreign_key_action { + none, // not specified + no_action, + restrict_, + set_null, + set_default, + cascade, + }; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_table_reference_v = - polyfill::is_specialization_of_v, table_reference>; + 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; + } - template - struct is_table_reference : polyfill::bool_constant> {}; - } + struct on_update_delete_base { + const bool update; // true if update and false if delete -#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 -} + operator std::string() const { + if(this->update) { + return "ON UPDATE"; + } else { + return "ON DELETE"; + } + } + }; -// #include "alias_traits.h" + /** + * F - foreign key class + */ + template + struct on_update_delete_t : on_update_delete_base { + using foreign_key_type = F; -#include // std::is_base_of, std::is_same -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include -#endif + const foreign_key_type& fk; -// #include "functional/cxx_universal.h" + on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : + on_update_delete_base{update_}, fk(fk_), _action(action_) {} -// #include "functional/cxx_type_traits_polyfill.h" + foreign_key_action _action = foreign_key_action::none; -// #include "type_traits.h" + 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 "table_reference.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; + } -namespace sqlite_orm { + 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 Base class for a custom table alias, column alias or expression alias. - */ - struct alias_tag {}; + 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; + } - namespace internal { + 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; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_alias_v = std::is_base_of::value; + operator bool() const { + return this->_action != foreign_key_action::none; + } + }; - template - struct is_alias : polyfill::bool_constant> {}; + template + bool operator==(const on_update_delete_t& lhs, const on_update_delete_t& rhs) { + return lhs._action == rhs._action; + } - /** @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; + template + struct foreign_key_t, std::tuple> { + using columns_type = std::tuple; + using references_type = std::tuple; + using self = foreign_key_t; - template - struct is_column_alias : is_alias {}; + /** + * Holds obect type of all referenced columns. + */ + using target_type = same_or_void_t...>; - /** @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; + /** + * Holds obect type of all source columns. + */ + using source_type = same_or_void_t...>; - template - struct is_recordset_alias : polyfill::bool_constant> {}; + columns_type columns; + references_type references; - /** @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; + on_update_delete_t on_update; + on_update_delete_t on_delete; - template - struct is_table_alias : polyfill::bool_constant> {}; + 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"); - /** @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; -#endif + 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) {} - template - using is_cte_moniker = polyfill::bool_constant>; - } + 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) {} -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - concept orm_alias = std::derived_from; + 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 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); - - /** @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); - - /** @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>); - - /** @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>); + 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; + } - /** @short Specifies that a type refers to a mapped table (possibly aliased). - */ - template - concept orm_refers_to_table = (orm_table_reference || orm_table_alias); + /** + * 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 + struct foreign_key_intermediate_t { + using tuple_type = std::tuple; - /** @short Specifies that a type refers to a recordset. - */ - template - concept orm_refers_to_recordset = (orm_table_reference || orm_recordset_alias); + tuple_type columns; - /** @short Specifies that a type is a mapped recordset (table reference). - */ - template - concept orm_mapped_recordset = (orm_table_reference || orm_cte_moniker); + template + foreign_key_t, std::tuple> references(Rs... refs) { + return {std::move(this->columns), {std::forward(refs)...}}; + } + }; #endif -} -// #include "expression.h" + 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 -#include // std::enable_if -#include // std::move, std::forward, std::declval -// #include "functional/cxx_optional.h" + operator std::string() const { + return "COLLATE " + this->string_from_collate_argument(this->argument); + } -// #include "functional/cxx_universal.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}; + } + }; -// #include "functional/cxx_type_traits_polyfill.h" + template + struct check_t { + using expression_type = T; -// #include "tags.h" + expression_type expression; + }; -namespace sqlite_orm { + struct basic_generated_always { + enum class storage_type { + not_specified, + virtual_, + stored, + }; - namespace internal { +#if SQLITE_VERSION_NUMBER >= 3031000 + bool full = true; + storage_type storage = storage_type::not_specified; +#endif - template - struct in_t; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} +#endif + }; - template - struct and_condition_t; +#if SQLITE_VERSION_NUMBER >= 3031000 + template + struct generated_always_t : basic_generated_always { + using expression_type = T; - template - struct or_condition_t; + expression_type expression; - /** - * Result of c(...) function. Has operator= overloaded which returns assign_t - */ - template - struct expression_t { - T value; + generated_always_t(expression_type expression_, bool full, storage_type storage) : + basic_generated_always{full, storage}, expression(std::move(expression_)) {} - template - assign_t operator=(R r) const { - return {this->value, std::move(r)}; + generated_always_t virtual_() { + return {std::move(this->expression), this->full, storage_type::virtual_}; } - 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}; + generated_always_t stored() { + return {std::move(this->expression), this->full, storage_type::stored}; } + }; #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}; - } + struct null_t {}; - template - and_condition_t and_(R right) const { - return {this->value, std::move(right)}; - } + struct not_null_t {}; + } - template - or_condition_t or_(R right) const { - return {this->value, std::move(right)}; - } - }; + namespace internal { template - SQLITE_ORM_INLINE_VAR constexpr bool - is_operator_argument_v::value>> = true; + SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = +#if SQLITE_VERSION_NUMBER >= 3006019 + polyfill::is_specialization_of::value; +#else + false; +#endif template - T get_from_expression(T value) { - return std::move(value); - } + struct is_foreign_key : polyfill::bool_constant> {}; template - T get_from_expression(expression_t expression) { - return std::move(expression.value); - } + SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = std::is_base_of::value; template - using unwrap_expression_t = decltype(get_from_expression(std::declval())); - } - - /** - * 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)}; - } -} - -// #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" + struct is_primary_key : polyfill::bool_constant> {}; -// #include "alias_traits.h" + template + SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = +#if SQLITE_VERSION_NUMBER >= 3031000 + polyfill::is_specialization_of::value; +#else + false; +#endif -// #include "tags.h" + template + struct is_generated_always : polyfill::bool_constant> {}; -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. + * PRIMARY KEY INSERTABLE traits. */ - template - struct column_pointer { - using type = T; - using field_type = F; + template + struct is_primary_key_insertable + : polyfill::disjunction< + mpl::invoke_t, + check_if_has_template>, + constraints_type_t>, + std::is_base_of>>> { - field_type field; + static_assert(tuple_has, is_primary_key>::value, + "an unexpected type was passed"); }; template - SQLITE_ORM_INLINE_VAR constexpr bool is_column_pointer_v = - polyfill::is_specialization_of::value; - - template - struct is_column_pointer : polyfill::bool_constant> {}; + 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 - SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v::value>> = - true; +#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}; + } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template - struct alias_holder; -#endif + 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 /** - * 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_type_traits_polyfill.h" - /** - * Stores LIMIT/OFFSET info - */ - template - struct limit_t : limit_string { - T lim; - optional_container off; +// #include "is_std_ptr.h" - limit_t() = default; +// #include "type_traits.h" - limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} +namespace sqlite_orm { - limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} - }; + /** + * 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; - template - struct is_limit : std::false_type {}; + 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 indirectly_test_printable; - template - struct is_limit> : std::true_type {}; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v = false; + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_printable_v{})>>> = true; - /** - * Stores OFFSET only info - */ template - struct offset_t { - T off; - }; + struct is_printable : polyfill::bool_constant> {}; + } - template - using is_offset = polyfill::is_specialization_of; + template + struct field_printer> { + std::string operator()(const T& t) const { + std::stringstream ss; + ss << t; + return ss.str(); + } + }; - /** - * Collated something - */ - template - struct collate_t : public condition_t { - T expr; - collate_argument argument; + /** + * 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(); + } + }; - collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} + /** + * 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(); + } + }; - operator std::string() const { - return collate_constraint_t{this->argument}; - } - }; + /** + * 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(); + } + }; - struct named_collate_base { - std::string name; + template + struct field_printer> { + std::string operator()(std::string string) const { + return string; + } + }; - operator std::string() const { - return "COLLATE " + this->name; + 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; } - }; - - /** - * Collated something with custom collate function - */ - template - struct named_collate : named_collate_base { - T expr; - - named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} - }; + 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; - struct negated_condition_string { - operator std::string() const { - return "NOT"; + std::string operator()(const T& t) const { + if(t) { + return field_printer()(*t); + } else { + return field_printer{}(nullptr); } - }; - - /** - * Result of not operator - */ - template - struct negated_condition_t : condition_t, negated_condition_string { - C c; - - negated_condition_t(C c_) : c(std::move(c_)) {} - }; + } + }; - /** - * 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; +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 + } +} + +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 binary_condition::binary_condition; + using bitwise_shift_right_t = binary_operator; - collate_t collate_binary() const { - return {*this, collate_argument::binary}; + struct bitwise_and_string { + serialize_result_type serialize() const { + return "&"; } + }; - collate_t collate_nocase() const { - return {*this, collate_argument::nocase}; - } + /** + * Result of bitwise and & operator + */ + template + 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 less_or_equal_string { + /** + * Result of bitwise or | operator + */ + template + using bitwise_or_t = binary_operator; + + struct bitwise_not_string { serialize_result_type serialize() const { - return "<="; + return "~"; } }; /** - * <= operator object. + * Result of bitwise not ~ operator */ - template - struct less_or_equal_t : binary_condition, negatable_t { - using self = less_or_equal_t; - - using binary_condition::binary_condition; + template + struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { + using argument_type = T; - collate_t collate_binary() const { - return {*this, collate_argument::binary}; - } + argument_type argument; - collate_t collate_nocase() const { - return {*this, collate_argument::nocase}; - } + bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} + }; - collate_t collate_rtrim() const { - return {*this, collate_argument::rtrim}; + struct assign_string { + serialize_result_type serialize() const { + return "="; } }; - struct in_base { - bool negative = false; // used in not_in + /** + * Result of assign = operator + */ + template + using assign_t = binary_operator; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - in_base(bool negative) : negative{negative} {} -#endif - }; + /** + * Assign operator traits. Common case + */ + template + struct is_assign_t : public std::false_type {}; /** - * IN operator object. + * Assign operator traits. Specialized case */ - template - struct dynamic_in_t : condition_t, in_base, negatable_t { - using self = dynamic_in_t; + template + struct is_assign_t> : public std::true_type {}; + } - L left; // left expression - A argument; // in arg + /** + * 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)}; + } - dynamic_in_t(L left_, A argument_, bool negative_) : - in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} - }; + /** + * 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)}; + } - template - struct in_t : condition_t, in_base, negatable_t { - L left; - std::tuple argument; + /** + * 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)}; + } - in_t(L left_, decltype(argument) argument_, bool negative_) : - in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} - }; + /** + * 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 is_null_string { - operator std::string() const { - return "IS NULL"; - } - }; + /** + * 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)}; + } + + /** + * 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)}; + } + + template + internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_and_t bitwise_and(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_or_t bitwise_or(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::bitwise_not_t bitwise_not(T t) { + return {std::move(t)}; + } + + template + internal::assign_t assign(L l, R r) { + return {std::move(l), std::move(r)}; + } +} + +// #include "select_constraints.h" + +#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" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_base_of_template.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_transformer.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "optional_container.h" + +namespace sqlite_orm { + + namespace internal { /** - * IS NULL operator object. + * This is a cute class which allows storing something or nothing + * depending on template argument. Useful for optional class members */ template - struct is_null_t : is_null_string, negatable_t { - using self = is_null_t; + struct optional_container { + using type = T; - T t; + type field; - is_null_t(T t_) : t(std::move(t_)) {} + template + void apply(const L& l) const { + l(this->field); + } }; - struct is_not_null_string { - operator std::string() const { - return "IS NOT NULL"; + template<> + struct optional_container { + using type = void; + + template + void apply(const L&) const { + //.. } }; + } +} - /** - * IS NOT NULL operator object. - */ - template - struct is_not_null_t : is_not_null_string, negatable_t { - using self = is_not_null_t; - - T t; +// #include "ast/where.h" - is_not_null_t(T t_) : t(std::move(t_)) {} - }; +#include // std::false_type, std::true_type +#include // std::move - struct order_by_base { - int asc_desc = 0; // 1: asc, -1: desc - std::string _collate_argument; +// #include "../functional/cxx_type_traits_polyfill.h" -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - order_by_base() = default; +// #include "../serialize_result_type.h" - order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : - asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} -#endif - }; +namespace sqlite_orm { + namespace internal { - struct order_by_string { - operator std::string() const { - return "ORDER BY"; + struct where_string { + serialize_result_type serialize() const { + return "WHERE"; } }; /** - * ORDER BY argument holder. + * 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 order_by_t : order_by_base, order_by_string { - using expression_type = O; - using self = order_by_t; + template + struct where_t : where_string { + using expression_type = C; expression_type expression; - order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} + where_t(expression_type expression_) : expression(std::move(expression_)) {} + }; - self asc() const { - auto res = *this; - res.asc_desc = 1; - return res; - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of::value; - self desc() const { - auto res = *this; - res.asc_desc = -1; - return res; - } + template + struct is_where : polyfill::bool_constant> {}; + } - self collate_binary() const { - auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); - return res; - } + /** + * 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)}; + } +} - self collate_nocase() const { - auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); - return res; - } +// #include "ast/group_by.h" - self collate_rtrim() const { - auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); - return res; - } +#include // std::tuple, std::make_tuple +#include // std::true_type, std::false_type +#include // std::forward, std::move - self collate(std::string name) const { - auto res = *this; - res._collate_argument = std::move(name); - return res; - } +// #include "../functional/cxx_type_traits_polyfill.h" - template - self collate() const { - std::stringstream ss; - ss << C::name() << std::flush; - return this->collate(ss.str()); - } +namespace sqlite_orm { + namespace internal { + + template + struct group_by_with_having { + using args_type = std::tuple; + using expression_type = T; + + args_type args; + expression_type expression; }; /** - * ORDER BY pack holder. + * GROUP BY pack holder. */ template - struct multi_order_by_t : order_by_string { + struct group_by_t { using args_type = std::tuple; args_type args; - multi_order_by_t(args_type args_) : args{std::move(args_)} {} + template + group_by_with_having having(T expression) { + return {std::move(this->args), std::move(expression)}; + } }; - 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_)) {} - }; + template + using is_group_by = polyfill::disjunction, + polyfill::is_specialization_of>; + } - /** - * 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; + /** + * GROUP BY column. + * Example: storage.get_all(group_by(&Employee::name)) + */ + template + internal::group_by_t group_by(Args... args) { + return {{std::forward(args)...}}; + } +} - dynamic_order_by_t(const context_t& context_) : context(context_) {} +// #include "core_functions.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 // 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 - const_iterator begin() const { - return this->entries.begin(); - } +// #include "functional/cxx_type_traits_polyfill.h" - const_iterator end() const { - return this->entries.end(); - } +// #include "functional/mpl/conditional.h" - void clear() { - this->entries.clear(); - } +// #include "is_base_of_template.h" - protected: - std::vector entries; - context_t context; - }; +// #include "tuple_helper/tuple_traits.h" - template - SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = - polyfill::disjunction, - polyfill::is_specialization_of, - polyfill::is_specialization_of>::value; +// #include "conditions.h" - template - struct is_order_by : polyfill::bool_constant> {}; +#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 - struct between_string { - operator std::string() const { - return "BETWEEN"; - } - }; +// #include "functional/cxx_type_traits_polyfill.h" - /** - * BETWEEN operator object. - */ - template - struct between_t : condition_t, between_string { - using expression_type = A; - using lower_type = T; - using upper_type = T; +// #include "is_base_of_template.h" - expression_type expr; - lower_type b1; - upper_type b2; +// #include "type_traits.h" - between_t(expression_type expr_, lower_type b1_, upper_type b2_) : - expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} - }; +// #include "collate_argument.h" - struct like_string { - operator std::string() const { - return "LIKE"; - } - }; +// #include "constraints.h" - /** - * 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; +// #include "optional_container.h" - arg_t arg; - pattern_t pattern; - optional_container arg3; // not escape cause escape exists as a function here +// #include "serializer_context.h" - like_t(arg_t arg_, pattern_t pattern_, optional_container escape_) : - arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} +namespace sqlite_orm { - 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)}; - } - }; + namespace internal { - struct glob_string { - operator std::string() const { - return "GLOB"; - } + struct serializer_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; + bool fts5_columns = false; }; - template - struct glob_t : condition_t, glob_string, negatable_t { - using self = glob_t; - using arg_t = A; - using pattern_t = T; + template + struct serializer_context : serializer_context_base { + using db_objects_type = DBOs; - arg_t arg; - pattern_t pattern; + const db_objects_type& db_objects; - glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} + serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} }; - struct cross_join_string { - operator std::string() const { - return "CROSS JOIN"; - } - }; + template + struct serializer_context_builder { + using storage_type = S; + using db_objects_type = typename storage_type::db_objects_type; - /** - * CROSS JOIN holder. - * T is joined type which represents any mapped table. - */ - template - struct cross_join_t : cross_join_string { - using type = T; - }; + serializer_context_builder(const storage_type& storage_) : storage{storage_} {} - struct natural_join_string { - operator std::string() const { - return "NATURAL JOIN"; + serializer_context operator()() const { + return {obtain_db_objects(this->storage)}; } - }; - /** - * NATURAL JOIN holder. - * T is joined type which represents any mapped table. - */ - template - struct natural_join_t : natural_join_string { - using type = T; + const storage_type& storage; }; + } - struct left_join_string { - operator std::string() const { - return "LEFT JOIN"; - } - }; +} - /** - * 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; +// #include "serialize_result_type.h" - on_type constraint; +// #include "tags.h" - left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} - }; +// #include "table_reference.h" - struct join_string { - operator std::string() const { - return "JOIN"; - } - }; +// #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_type_traits_polyfill.h" + +// #include "tags.h" + +// #include "operators.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct in_t; + + template + struct and_condition_t; + + template + struct or_condition_t; /** - * Simple JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. + * Result of c(...) function. Has operator= overloaded which returns assign_t */ - template - struct join_t : join_string { - using type = T; - using on_type = O; + template + struct expression_t { + T value; - on_type constraint; + template + assign_t operator=(R r) const { + return {this->value, std::move(r)}; + } - join_t(on_type constraint_) : constraint(std::move(constraint_)) {} - }; + 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}; + } - struct left_outer_join_string { - operator std::string() const { - return "LEFT OUTER JOIN"; + 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)}; } }; - /** - * LEFT OUTER JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. + 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); + } + + template + using unwrap_expression_t = decltype(get_from_expression(std::declval())); + } + + /** + * 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)}; + } +} + +// #include "column_pointer.h" + +// #include "tags.h" + +// #include "type_printer.h" + +// #include "literal.h" + +namespace sqlite_orm { + namespace internal { + + /* + * Protect an otherwise bindable element so that it is always serialized as a literal value. */ - template - struct left_outer_join_t : left_outer_join_string { + template + struct literal_holder { using type = T; - using on_type = O; - on_type constraint; - - left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + type value; }; - struct on_string { + } +} + +namespace sqlite_orm { + + namespace internal { + + struct limit_string { operator std::string() const { - return "ON"; + return "LIMIT"; } }; /** - * on(...) argument holder used for JOIN, LEFT JOIN, LEFT OUTER JOIN and INNER JOIN - * T is on type argument. + * 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_)) {} + + limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} + }; + template - struct on_t : on_string { - using arg_type = T; + struct is_limit : std::false_type {}; - arg_type arg; + template + struct is_limit> : std::true_type {}; - on_t(arg_type arg_) : arg(std::move(arg_)) {} + /** + * Stores OFFSET only info + */ + template + struct offset_t { + T off; }; + template + using is_offset = polyfill::is_specialization_of; + /** - * USING argument holder. + * Collated something */ - template - struct using_t { - column_pointer column; + 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_) {} operator std::string() const { - return "USING"; + return collate_constraint_t{this->argument}; } }; - struct inner_join_string { + struct named_collate_base { + std::string name; + operator std::string() const { - return "INNER JOIN"; + return "COLLATE " + this->name; } }; /** - * INNER JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. + * Collated something with custom collate function */ - template - struct inner_join_t : inner_join_string { - using type = T; - using on_type = O; - - on_type constraint; + template + struct named_collate : named_collate_base { + T expr; - inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} }; - struct cast_string { + struct negated_condition_string { operator std::string() const { - return "CAST"; + return "NOT"; } }; /** - * CAST holder. - * T is a type to cast to - * E is an expression type - * Example: cast(&User::id) + * Result of not operator */ - template - struct cast_t : cast_string { - using to_type = T; - using expression_type = E; - - expression_type expression; + template + struct negated_condition_t : condition_t, negated_condition_string { + C c; - cast_t(expression_type expression_) : expression(std::move(expression_)) {} + negated_condition_t(C c_) : c(std::move(c_)) {} }; - template - struct from_t { - using tuple_type = std::tuple; - }; + /** + * 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 - using is_from = polyfill::is_specialization_of; + left_type lhs; + right_type rhs; - template - using is_constrained_join = polyfill::is_detected; - } + binary_condition() = default; - /** - * Explicit FROM function. Usage: - * `storage.select(&User::id, from());` - */ - template - internal::from_t from() { - static_assert(sizeof...(Tables) > 0, ""); - return {}; - } + binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Explicit FROM function. Usage: - * `storage.select(&User::id, from<"a"_alias.for_>());` - */ - template - auto from() { - return from...>(); - } -#endif + template + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; - // 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)}; - } + template + struct is_binary_condition : polyfill::bool_constant> {}; - 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 and_condition_string { + serialize_result_type serialize() const { + return "AND"; + } + }; - 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))}; - } + /** + * Result of and operator + */ + template + struct and_condition_t : binary_condition { + using super = binary_condition; - 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))}; - } + using super::super; + }; - 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 or_condition_string { + serialize_result_type serialize() const { + return "OR"; + } + }; - 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))}; - } + /** + * Result of or operator + */ + template + struct or_condition_t : binary_condition { + using super = binary_condition; - 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))}; - } + using super::super; + }; - 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 is_equal_string { + serialize_result_type serialize() const { + return "="; + } + }; - 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))}; - } + /** + * = and == operators object + */ + template + struct is_equal_t : binary_condition, negatable_t { + using self = is_equal_t; - 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))}; - } - } + using binary_condition::binary_condition; - template - internal::using_t using_(F O::*field) { - return {field}; - } - template - internal::using_t using_(internal::column_pointer field) { - return {std::move(field)}; - } + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - template - internal::on_t on(T t) { - return {std::move(t)}; - } + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } - template - internal::cross_join_t cross_join() { - return {}; - } + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } - template - internal::natural_join_t natural_join() { - return {}; - } + named_collate collate(std::string name) const { + return {*this, std::move(name)}; + } - template - internal::left_join_t left_join(O o) { - return {std::move(o)}; - } + template + named_collate collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return {*this, ss.str()}; + } + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto left_join(On on) { - return left_join, On>(std::move(on)); - } -#endif + template + struct is_equal_with_table_t : negatable_t { + using left_type = L; + using right_type = R; - template - internal::join_t join(O o) { - return {std::move(o)}; - } + right_type rhs; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto join(On on) { - return join, On>(std::move(on)); - } -#endif + is_equal_with_table_t(right_type rhs) : rhs(std::move(rhs)) {} + }; - template - internal::left_outer_join_t left_outer_join(O o) { - return {std::move(o)}; - } + struct is_not_equal_string { + serialize_result_type serialize() const { + return "!="; + } + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto left_outer_join(On on) { - return left_outer_join, On>(std::move(on)); - } -#endif + /** + * != operator object + */ + template + struct is_not_equal_t : binary_condition, negatable_t { + using self = is_not_equal_t; - template - internal::inner_join_t inner_join(O o) { - return {std::move(o)}; - } + using binary_condition::binary_condition; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto inner_join(On on) { - return inner_join, On>(std::move(on)); - } -#endif + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - template - internal::offset_t offset(T off) { - return {std::move(off)}; - } + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } - template - internal::limit_t limit(T lim) { - return {std::move(lim)}; - } + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; - template = true> - internal::limit_t limit(O off, T lim) { - return {std::move(lim), {std::move(off)}}; - } + struct greater_than_string { + serialize_result_type serialize() const { + return ">"; + } + }; - template - internal::limit_t limit(T lim, internal::offset_t offt) { - return {std::move(lim), {std::move(offt.off)}}; - } + /** + * > operator object. + */ + template + struct greater_than_t : binary_condition, negatable_t { + using self = greater_than_t; - 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))}; - } + using binary_condition::binary_condition; - 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))}; - } + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - template - internal::is_not_null_t is_not_null(T t) { - return {std::move(t)}; - } + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } - template - internal::is_null_t is_null(T t) { - return {std::move(t)}; - } + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; - template - internal::dynamic_in_t> in(L l, std::vector values) { - return {std::move(l), std::move(values), false}; - } + struct greater_or_equal_string { + serialize_result_type serialize() const { + return ">="; + } + }; - template - internal::dynamic_in_t> in(L l, std::initializer_list values) { - return {std::move(l), std::move(values), false}; - } + /** + * >= operator object. + */ + template + struct greater_or_equal_t : binary_condition, negatable_t { + using self = greater_or_equal_t; - template - internal::dynamic_in_t in(L l, A arg) { - return {std::move(l), std::move(arg), false}; - } + using binary_condition::binary_condition; - template - internal::dynamic_in_t> not_in(L l, std::vector values) { - return {std::move(l), std::move(values), true}; - } + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - template - internal::dynamic_in_t> not_in(L l, std::initializer_list values) { - return {std::move(l), std::move(values), true}; - } + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } - template - internal::dynamic_in_t not_in(L l, A arg) { - return {std::move(l), std::move(arg), true}; - } + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; - template - internal::is_equal_t is_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct less_than_string { + serialize_result_type serialize() const { + return "<"; + } + }; - template - internal::is_equal_t eq(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * < operator object. + */ + template + struct less_than_t : binary_condition, negatable_t { + using self = less_than_t; - template - internal::is_equal_with_table_t is_equal(R rhs) { - return {std::move(rhs)}; - } + using binary_condition::binary_condition; - template - internal::is_not_equal_t is_not_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - template - internal::is_not_equal_t ne(L l, R r) { - return {std::move(l), std::move(r)}; - } + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } - template - internal::greater_than_t greater_than(L l, R r) { - return {std::move(l), std::move(r)}; - } + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; - template - internal::greater_than_t gt(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct less_or_equal_string { + serialize_result_type serialize() const { + return "<="; + } + }; - template - internal::greater_or_equal_t greater_or_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * <= operator object. + */ + template + struct less_or_equal_t : binary_condition, negatable_t { + using self = less_or_equal_t; - template - internal::greater_or_equal_t ge(L l, R r) { - return {std::move(l), std::move(r)}; - } + using binary_condition::binary_condition; - template - internal::less_than_t less_than(L l, R r) { - return {std::move(l), std::move(r)}; - } + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - /** - * [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)}; - } + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } - template - internal::less_than_t lt(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 less_or_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct in_base { + bool negative = false; // used in not_in - /** - * [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)}; - } +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + in_base(bool negative) : negative{negative} {} +#endif + }; - template - internal::less_or_equal_t le(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * IN operator object. + */ + template + struct dynamic_in_t : condition_t, in_base, negatable_t { + using self = dynamic_in_t; - /** - * 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)}; - } - - /** - * 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)}}; - } - - /** - * 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)...}}; - } + L left; // left expression + A argument; // in arg - /** - * 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(); - } + dynamic_in_t(L left_, A argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + }; - /** - * 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)}; - } + template + struct in_t : condition_t, in_base, negatable_t { + L left; + std::tuple argument; - /** - * 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), {}}; - } + in_t(L left_, decltype(argument) argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + }; - /** - * 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)}; - } + struct is_null_string { + operator std::string() const { + return "IS NULL"; + } + }; - /** - * 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)}}; - } + /** + * IS NULL operator object. + */ + template + struct is_null_t : is_null_string, negatable_t { + using self = is_null_t; - /** - * CAST(X AS type). - * Example: cast(&User::id) - */ - template - internal::cast_t cast(E e) { - return {std::move(e)}; - } -} -#pragma once + T t; -#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 + is_null_t(T t_) : t(std::move(t_)) {} + }; -// #include "functional/cxx_type_traits_polyfill.h" + struct is_not_null_string { + operator std::string() const { + return "IS NOT NULL"; + } + }; -// #include "functional/mpl/conditional.h" + /** + * IS NOT NULL operator object. + */ + template + struct is_not_null_t : is_not_null_string, negatable_t { + using self = is_not_null_t; -// #include "functional/cstring_literal.h" + T t; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include // std::index_sequence -#include // std::copy_n -#endif + is_not_null_t(T t_) : t(std::move(t_)) {} + }; -// #include "cxx_universal.h" -// ::size_t + struct order_by_base { + int asc_desc = 0; // 1: asc, -1: desc + std::string _collate_argument; -#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; - } +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + order_by_base() = default; - constexpr cstring_literal(const char (&cstr)[N]) { - std::copy_n(cstr, N, this->cstr); - } + order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : + asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} +#endif + }; - char cstr[N]; - }; + struct order_by_string { + operator std::string() const { + return "ORDER BY"; + } + }; - template class Template, cstring_literal literal, size_t... Idx> - consteval auto explode_into(std::index_sequence) { - return Template{}; - } -} -#endif + /** + * ORDER BY argument holder. + */ + template + struct order_by_t : order_by_base, order_by_string { + using expression_type = O; + using self = order_by_t; -// #include "type_traits.h" + expression_type expression; -// #include "alias_traits.h" + order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} -// #include "table_type_of.h" + self asc() const { + auto res = *this; + res.asc_desc = 1; + return res; + } -// #include "tags.h" + self desc() const { + auto res = *this; + res.asc_desc = -1; + return res; + } -// #include "column_pointer.h" + self collate_binary() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); + return res; + } -namespace sqlite_orm { + self collate_nocase() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); + return res; + } - namespace internal { -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - inline constexpr bool is_operator_argument_v>> = true; -#endif + self collate_rtrim() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); + return res; + } - /** - * 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; + self collate(std::string name) const { + auto res = *this; + res._collate_argument = std::move(name); + return res; + } - static std::string get() { - return {A, X...}; + template + self collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return this->collate(ss.str()); } }; /** - * Column expression with table alias attached like 'C.ID'. This is not a column alias + * ORDER BY pack holder. */ - template - struct alias_column_t { - using alias_type = T; - using column_type = C; + template + struct multi_order_by_t : order_by_string { + using args_type = std::tuple; - column_type column; - }; + args_type args; - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_operator_argument_v::value>> = - true; + multi_order_by_t(args_type args_) : args{std::move(args_)} {} + }; - struct basic_table; + struct dynamic_order_by_entry_t : order_by_base { + std::string name; - /* - * 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. + 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 alias_extractor { - static std::string extract() { - return {}; - } + 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; - static std::string as_alias() { - return {}; - } + dynamic_order_by_t(const context_t& context_) : context(context_) {} - template - static const std::string& as_qualifier(const X& table) { - return table.name; + 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)); } - }; - /* - * 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(); + const_iterator begin() const { + return this->entries.begin(); } - // for column and regular table aliases -> alias identifier - template, A> = true> - static std::string as_alias() { - return alias_extractor::extract(); + const_iterator end() const { + return this->entries.end(); } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - // for CTE monikers -> empty - template, A> = true> - static std::string as_alias() { - return {}; + void clear() { + this->entries.clear(); } -#endif - // for regular table aliases -> alias identifier - template = true> - static std::string as_qualifier(const basic_table&) { - return alias_extractor::extract(); + protected: + std::vector entries; + context_t context; + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = + polyfill::disjunction, + polyfill::is_specialization_of, + polyfill::is_specialization_of>::value; + + template + struct is_order_by : polyfill::bool_constant> {}; + + struct between_string { + operator std::string() const { + return "BETWEEN"; } }; /** - * Used to store alias for expression + * BETWEEN operator object. */ - template - struct as_t { - using alias_type = T; - using expression_type = E; + template + struct between_t : condition_t, between_string { + using expression_type = A; + using lower_type = T; + using upper_type = T; - expression_type expression; + expression_type expr; + lower_type b1; + upper_type b2; + + between_t(expression_type expr_, lower_type b1_, upper_type b2_) : + expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} + }; + + struct like_string { + operator std::string() const { + return "LIKE"; + } }; /** - * 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`. + * LIKE operator object. */ - template - struct column_alias : alias_tag { - static std::string get() { - return {A, X...}; + 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 + 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 glob_string { + operator std::string() const { + return "GLOB"; + } + }; + + template + struct glob_t : condition_t, glob_string, negatable_t { + using self = glob_t; + using arg_t = A; + using pattern_t = T; + + arg_t arg; + pattern_t pattern; + + glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} + }; + + struct cross_join_string { + operator std::string() const { + return "CROSS JOIN"; } }; + /** + * CROSS JOIN holder. + * T is joined type which represents any mapped table. + */ template - struct alias_holder { + struct cross_join_t : cross_join_string { using type = T; + }; - 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 {} + struct natural_join_string { + operator std::string() const { + return "NATURAL JOIN"; + } }; + /** + * NATURAL JOIN holder. + * T is joined type which represents any mapped table. + */ template - SQLITE_ORM_INLINE_VAR constexpr bool - is_operator_argument_v::value>> = true; + struct natural_join_t : natural_join_string { + using type = T; + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - struct recordset_alias_builder { - template - [[nodiscard]] consteval recordset_alias for_() const { - return {}; + struct left_join_string { + operator std::string() const { + return "LEFT JOIN"; } + }; - template - [[nodiscard]] consteval auto for_() const { - using T = std::remove_const_t; - return recordset_alias{}; - } + /** + * 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; + + on_type constraint; + + left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; -#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 join_string { + operator std::string() const { + return "JOIN"; } - } + }; - 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 - } + /** + * 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; - /** - * 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"); + on_type constraint; - return alias_column_t{std::move(field)}; - } + join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; - /** - * 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"); + struct left_outer_join_string { + operator std::string() const { + return "LEFT OUTER JOIN"; + } + }; - using C1 = - mpl::conditional_t::value, F O::*, column_pointer>; - return alias_column_t{C1{field}}; - } + /** + * 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; -#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"); + on_type constraint; - 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}}; - } - } + left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; - /** - * 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 + struct on_string { + operator std::string() const { + return "ON"; + } + }; -#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; + /** + * 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; - 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)}; - } + arg_type arg; + + on_t(arg_type arg_) : arg(std::move(arg_)) {} + }; + + /** + * USING argument holder. + */ + template + struct using_t { + column_pointer column; + + operator std::string() const { + return "USING"; + } + }; + + struct inner_join_string { + operator std::string() const { + return "INNER JOIN"; + } + }; + + /** + * 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; + + on_type constraint; + + inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + }; + + struct cast_string { + operator std::string() const { + return "CAST"; + } + }; + + /** + * 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; + + expression_type expression; + + cast_t(expression_type expression_) : expression(std::move(expression_)) {} + }; + + template + struct from_t { + using tuple_type = std::tuple; + }; + + template + using is_from = polyfill::is_specialization_of; + + template + using is_constrained_join = polyfill::is_detected; } -#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` + * Explicit FROM function. Usage: + * `storage.select(&User::id, from());` */ - template - requires(orm_cte_moniker>) - constexpr auto operator->*(const A& /*tableAlias*/, C c) { - return alias_column(std::move(c)); + template + internal::from_t from() { + static_assert(sizeof...(Tables) > 0, ""); + return {}; } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Create a column reference to an aliased CTE column. + * Explicit FROM function. Usage: + * `storage.select(&User::id, from<"a"_alias.for_>());` */ - template - requires(orm_cte_moniker>) - constexpr auto alias_column(C c) { - using A = std::remove_const_t; - return alias_column(std::move(c)); + template + auto from() { + return from...>(); } -#endif #endif - /** - * Alias a column expression. - */ - template = true> - internal::as_t as(E expression) { - return {std::move(expression)}; - } + // 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)}; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Alias a column expression. - */ - template - auto as(E expression) { - return internal::as_t{std::move(expression)}; + 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))}; + } + + 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))}; + } + + 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))}; + } + + 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))}; + } + + 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))}; + } + + 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))}; + } + + 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))}; + } } - /** - * Alias a column expression. - */ - template - internal::as_t operator>>=(E expression, const A&) { - return {std::move(expression)}; + template + internal::using_t using_(F O::*field) { + return {field}; } -#else - /** - * Alias a column expression. - */ - template = true> - internal::as_t operator>>=(E expression, const A&) { - return {std::move(expression)}; + template + internal::using_t using_(internal::column_pointer field) { + return {std::move(field)}; } -#endif - /** - * Wrap a column alias in an alias holder. - */ template - internal::alias_holder get() { - static_assert(internal::is_column_alias_v, ""); - return {}; + internal::on_t on(T t) { + return {std::move(t)}; } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto get() { - return internal::alias_holder{}; + template + internal::cross_join_t cross_join() { + return {}; } -#endif 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; + internal::natural_join_t natural_join() { + return {}; + } - 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'>; + template + internal::left_join_t left_join(O o) { + return {std::move(o)}; + } #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{}; + template + auto left_join(On on) { + return left_join, On>(std::move(on)); + } +#endif - 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{}); - } + template + internal::join_t join(O o) { + return {std::move(o)}; + } - /** @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{}); - } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto join(On on) { + return join, On>(std::move(on)); } #endif -#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 - */ - 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{}; - } + template + internal::left_outer_join_t left_outer_join(O o) { + return {std::move(o)}; } -#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 +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto left_outer_join(On on) { + return left_outer_join, On>(std::move(on)); + } +#endif -// #include "functional/cxx_type_traits_polyfill.h" + template + internal::inner_join_t inner_join(O o) { + return {std::move(o)}; + } -// #include "functional/mpl/conditional.h" +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto inner_join(On on) { + return inner_join, On>(std::move(on)); + } +#endif -// #include "is_base_of_template.h" + template + internal::offset_t offset(T off) { + return {std::move(off)}; + } -// #include "tuple_helper/tuple_traits.h" + template + internal::limit_t limit(T lim) { + return {std::move(lim)}; + } -// #include "conditions.h" + template = true> + internal::limit_t limit(O off, T lim) { + return {std::move(lim), {std::move(off)}}; + } -// #include "serialize_result_type.h" + template + internal::limit_t limit(T lim, internal::offset_t offt) { + return {std::move(lim), {std::move(offt.off)}}; + } -// #include "operators.h" + 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))}; + } -// #include "tags.h" + 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))}; + } -// #include "table_reference.h" + template + internal::is_not_null_t is_not_null(T t) { + return {std::move(t)}; + } -// #include "ast/into.h" + template + internal::is_null_t is_null(T t) { + return {std::move(t)}; + } -// #include "../functional/cxx_type_traits_polyfill.h" + template + internal::dynamic_in_t> in(L l, std::vector values) { + return {std::move(l), std::move(values), false}; + } -namespace sqlite_orm { - namespace internal { + template + internal::dynamic_in_t> in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), false}; + } - template - struct into_t { - using type = T; - }; + template + internal::dynamic_in_t in(L l, A arg) { + return {std::move(l), std::move(arg), false}; + } - template - using is_into = polyfill::is_specialization_of; + template + internal::dynamic_in_t> not_in(L l, std::vector values) { + return {std::move(l), std::move(values), true}; } - template - internal::into_t into() { - return {}; + template + internal::dynamic_in_t> not_in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), true}; } -} -namespace sqlite_orm { + template + internal::dynamic_in_t not_in(L l, A arg) { + return {std::move(l), std::move(arg), true}; + } - using int64 = sqlite_int64; - using uint64 = sqlite_uint64; + template + internal::is_equal_t is_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } - namespace internal { + template + internal::is_equal_t eq(L l, R r) { + return {std::move(l), std::move(r)}; + } - template - struct unique_ptr_result_of {}; + template + internal::is_equal_with_table_t is_equal(R rhs) { + return {std::move(rhs)}; + } - /** - * 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; + template + internal::is_not_equal_t is_not_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } - static constexpr size_t args_size = std::tuple_size::value; + template + internal::is_not_equal_t ne(L l, R r) { + return {std::move(l), std::move(r)}; + } - args_type args; + template + internal::greater_than_t greater_than(L l, R r) { + return {std::move(l), std::move(r)}; + } - built_in_function_t(args_type&& args_) : args(std::move(args_)) {} - }; + template + internal::greater_than_t gt(L l, R r) { + return {std::move(l), std::move(r)}; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = - is_base_of_template::value; + template + internal::greater_or_equal_t greater_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } - template - struct is_built_in_function : polyfill::bool_constant> {}; + template + internal::greater_or_equal_t ge(L l, R r) { + return {std::move(l), std::move(r)}; + } - template - struct filtered_aggregate_function { - using function_type = F; - using where_expression = W; + template + internal::less_than_t less_than(L l, R r) { + return {std::move(l), std::move(r)}; + } - function_type function; - where_expression where; - }; + /** + * [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)}; + } - template - struct where_t; + template + internal::less_than_t lt(L l, R r) { + return {std::move(l), std::move(r)}; + } - template - struct built_in_aggregate_function_t : built_in_function_t { - using super = built_in_function_t; + template + internal::less_or_equal_t less_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; + } - using super::super; + /** + * [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)}; + } - template - filtered_aggregate_function, W> filter(where_t wh) { - return {*this, std::move(wh.expression)}; - } - }; + template + internal::less_or_equal_t le(L l, R r) { + return {std::move(l), std::move(r)}; + } - struct typeof_string { - serialize_result_type serialize() const { - return "TYPEOF"; - } - }; + /** + * 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)}; + } - struct unicode_string { - serialize_result_type serialize() const { - return "UNICODE"; - } - }; + /** + * 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)}}; + } + + /** + * 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)...}}; + } + + /** + * 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(); + } + + /** + * 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)}; + } + + /** + * 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)}; + } +} + +// #include "serialize_result_type.h" + +// #include "operators.h" + +// #include "tags.h" + +// #include "table_reference.h" + +// #include "ast/into.h" + +// #include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + + template + struct into_t { + using type = T; + }; + + template + using is_into = polyfill::is_specialization_of; + } + + template + internal::into_t into() { + return {}; + } +} + +namespace sqlite_orm { + + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; + + namespace internal { + + template + struct unique_ptr_result_of {}; + + /** + * 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; + + static constexpr size_t args_size = std::tuple_size::value; + + args_type args; + + built_in_function_t(args_type&& args_) : args(std::move(args_)) {} + }; + + 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; + }; + + template + struct where_t; + + template + struct built_in_aggregate_function_t : built_in_function_t { + using super = built_in_function_t; + + using super::super; + + template + filtered_aggregate_function, W> filter(where_t wh) { + return {*this, std::move(wh.expression)}; + } + }; + + struct typeof_string { + serialize_result_type serialize() const { + return "TYPEOF"; + } + }; + + struct unicode_string { + serialize_result_type serialize() const { + return "UNICODE"; + } + }; struct length_string { serialize_result_type serialize() const { @@ -7725,502 +8140,404 @@ namespace sqlite_orm { return {std::move(x), std::move(y), std::move(z)}; } } -#pragma once +// #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 // std::remove_const -#include // std::string -#include // std::move -#include // std::tuple, std::get, std::tuple_size -// #include "functional/cxx_optional.h" - -// #include "functional/cxx_universal.h" -// ::size_t -// #include "functional/cxx_type_traits_polyfill.h" - -// #include "is_base_of_template.h" - -// #include "tuple_helper/tuple_traits.h" - -// #include "tuple_helper/tuple_transformer.h" - -#include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval -#include // std::tuple_size, std::get - -// #include "../functional/cxx_universal.h" -// ::size_t -// #include "../functional/cxx_type_traits_polyfill.h" -// #include "../functional/cxx_functional_polyfill.h" +// #include "functional/cstring_literal.h" -// #include "../functional/mpl.h" +// #include "alias.h" +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) namespace sqlite_orm { - namespace internal { - - template class Op> - struct tuple_transformer; - - template class Pack, class... Types, template class Op> - struct tuple_transformer, Op> { - using type = Pack...>; - }; - - /* - * Transform specified tuple. - * - * `Op` is a metafunction. - */ - 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. - * - * @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))...); - } - /* - * 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. + namespace internal { + /** + * A special record set alias that is both, a storage lookup type (mapping type) and an alias. */ - 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)); - } - - /* - * 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{}; - } + 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 }; + } - /* - * 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. + inline namespace literals { + /** + * cte_moniker<'n'> from a numeric literal. + * E.g. 1_ctealias, 2_ctealias */ - template class NestedProject> - struct project_nested_tuple_size { - template - constexpr auto operator()(const T&) const { - return typename std::tuple_size>::type{}; - } - }; - - template class NestedProject, class Tpl, class IdxSeq> - using nested_tuple_size_for_t = decltype(recombine_tuple(plus_fold_integrals{}, - std::declval(), - IdxSeq{}, - project_nested_tuple_size{}, - std::integral_constant{})); -#endif - - template - constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { - return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + template + [[nodiscard]] SQLITE_ORM_CONSTEVAL auto operator"" _ctealias() { + return internal::cte_moniker{}; } - /* - * Like `std::make_from_tuple`, but using a projection on the tuple elements. +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * cte_moniker<'1'[, ...]> from a string literal. + * E.g. "1"_cte, "2"_cte */ - 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)); + template + [[nodiscard]] consteval auto operator"" _cte() { + return internal::explode_into(std::make_index_sequence{}); } +#endif } } +#endif -// #include "tuple_helper/tuple_iteration.h" +// #include "schema/column.h" -#include // std::get, std::tuple_element, std::tuple_size -#include // std::index_sequence, std::make_index_sequence -#include // std::forward, std::move +#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" -// ::size_t +// #include "../functional/cxx_type_traits_polyfill.h" -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*/) {} +// #include "../tuple_helper/tuple_traits.h" - 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)); - } +// #include "../tuple_helper/tuple_filter.h" -#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)); - } +// #include "../type_traits.h" - 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); - } - }; +// #include "../member_traits/member_traits.h" - /* - * 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 "../type_is_nullable.h" -// #include "optional_container.h" +#include // std::false_type, std::true_type, std::enable_if +#include // std::shared_ptr, std::unique_ptr +// #include "functional/cxx_optional.h" -// #include "ast/where.h" +// #include "functional/cxx_type_traits_polyfill.h" -#include // std::false_type, std::true_type -#include // std::move +namespace sqlite_orm { -// #include "../functional/cxx_universal.h" + /** + * 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; + } + }; -// #include "../functional/cxx_type_traits_polyfill.h" + /** + * This is a specialization for std::shared_ptr, std::unique_ptr, std::optional, which are nullable in sqlite_orm. + */ + template + 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); + } + }; +} -// #include "../serialize_result_type.h" +// #include "../constraints.h" namespace sqlite_orm { + namespace internal { - struct where_string { - serialize_result_type serialize() const { - return "WHERE"; - } + struct column_identifier { + + /** + * Column name. + */ + std::string name; }; - /** - * 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. + struct empty_setter {}; + + /* + * 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 where_t : where_string { - using expression_type = C; + 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; - expression_type expression; + /** + * 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; - where_t(expression_type expression_) : expression(std::move(expression_)) {} + /** + * 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; + } + + /** + * Simplified interface for `DEFAULT` constraint + * @return string representation of default value if it exists otherwise nullptr + */ + std::unique_ptr default_value() const; }; + /** + * 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 + }; + + template + struct column_field_expression { + using type = void; + }; + + template + struct column_field_expression, void> { + using type = typename column_t::member_pointer_t; + }; + + template + using column_field_expression_t = typename column_field_expression::type; + template - SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of::value; + SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of::value; template - struct is_where : polyfill::bool_constant> {}; + using is_column = polyfill::bool_constant>; + + template + using col_index_sequence_with_field_type = + filter_tuple_sequence_t::template fn, + field_type_t, + filter_tuple_sequence_t>; + + template class TraitFn> + using col_index_sequence_with = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; + + template class TraitFn> + using col_index_sequence_excluding = filter_tuple_sequence_t::template fn, + constraints_type_t, + filter_tuple_sequence_t>; } /** - * 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))); + * Factory function for a column definition from a member object pointer of the object to be mapped. */ - template - internal::where_t where(C expression) { - return {std::move(expression)}; + template = true> + 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`, + // 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)...}}); } -} -// #include "ast/group_by.h" + /** + * 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"); -#include // std::tuple, std::make_tuple -#include // std::true_type, std::false_type -#include // std::forward, std::move + // 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_type_traits_polyfill.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"); + + // 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)...}}); + } +} namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct as_optional_t { + using value_type = T; - template - struct group_by_with_having { - using args_type = std::tuple; - using expression_type = T; + value_type value; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - args_type args; - expression_type expression; + struct distinct_string { + operator std::string() const { + return "DISTINCT"; + } }; /** - * GROUP BY pack holder. + * DISCTINCT generic container. */ - template - struct group_by_t { - using args_type = std::tuple; + template + struct distinct_t : distinct_string { + using value_type = T; - args_type args; + value_type value; - template - group_by_with_having having(T expression) { - return {std::move(this->args), std::move(expression)}; + distinct_t(value_type value_) : value(std::move(value_)) {} + }; + + struct all_string { + operator std::string() const { + return "ALL"; } }; + /** + * ALL generic container. + */ template - using is_group_by = polyfill::disjunction, - polyfill::is_specialization_of>; - } + struct all_t : all_string { + T value; - /** - * GROUP BY column. - * Example: storage.get_all(group_by(&Employee::name)) - */ - template - internal::group_by_t group_by(Args... args) { - return {{std::forward(args)...}}; - } -} + all_t(T value_) : value(std::move(value_)) {} + }; -// #include "core_functions.h" + template + struct columns_t { + using columns_type = std::tuple; -// #include "alias_traits.h" + columns_type columns; + bool distinct = false; -// #include "cte_moniker.h" + static constexpr int count = std::tuple_size::value; -#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 +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + columns_t(columns_type columns) : columns{std::move(columns)} {} #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 { - - 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 { - /** - * 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{}; - } - -#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 - } -} -#endif - -namespace sqlite_orm { - - namespace internal { -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct as_optional_t { - using value_type = T; - - value_type value; - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - - struct distinct_string { - operator std::string() const { - return "DISTINCT"; - } - }; - - /** - * DISCTINCT generic container. - */ - template - struct distinct_t : distinct_string { - using value_type = T; - - value_type value; - - distinct_t(value_type value_) : value(std::move(value_)) {} - }; - - struct all_string { - operator std::string() const { - return "ALL"; - } - }; - - /** - * ALL generic container. - */ - template - struct all_t : all_string { - T value; - - all_t(T value_) : value(std::move(value_)) {} - }; - - template - struct columns_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 - 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 + SQLITE_ORM_INLINE_VAR constexpr bool is_columns_v = polyfill::is_specialization_of::value; template using is_columns = polyfill::bool_constant>; @@ -8940,532 +9257,191 @@ namespace sqlite_orm { } #endif } -#pragma once -#include // std::string +// #include "core_functions.h" -// #include "functional/cxx_universal.h" +// #include "conditions.h" -namespace sqlite_orm { +// #include "statement_binder.h" - struct table_info { - int cid = 0; - std::string name; - std::string type; - bool notnull = false; - std::string dflt_value; - int pk = 0; +#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" -#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_) {} +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include // wcsncpy, wcslen #endif - }; - - 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 - -#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_} {} +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert +#include // std::codecvt_utf8_utf16 #endif - }; -} -#pragma once -#include -#include -#include -#include +// #include "functional/cxx_type_traits_polyfill.h" -// #include "../functional/cxx_universal.h" +// #include "functional/cxx_functional_polyfill.h" -// #include "../optional_container.h" +// #include "is_std_ptr.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) -// It could be an interesting feature to bring to sqlite_orm that other libraries don't have ? +// #include "tuple_helper/tuple_filter.h" -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 }; +// #include "type_traits.h" - /** - * 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; +// #include "error_code.h" - /** - * 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; +// #include "arithmetic_tag.h" - partial_trigger_t(T trigger_base, S... statements) : - base{std::move(trigger_base)}, statements{std::make_tuple(std::forward(statements)...)} {} +#include // std::is_integral - partial_trigger_t& end() { - return *this; - } - }; +// #include "functional/mpl/conditional.h" - struct base_trigger { - /** - * Name of the trigger - */ - std::string name; - }; +namespace sqlite_orm { - /** - * 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; + /** + * Helper classes used by statement_binder and row_extractor. + */ + struct int_or_smaller_tag {}; + struct bigint_tag {}; + struct real_tag {}; - /** - * Base of the trigger (contains its type, timing and associated table) - */ - T base; + template + using arithmetic_tag_t = + mpl::conditional_t::value, + // Integer class + mpl::conditional_t, + // Floating-point class + real_tag>; +} - /** - * Statements of the triggers (to be executed when the trigger fires) - */ - elements_type elements; +// #include "xdestroy_handling.h" -#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)) {} +#include // std::integral_constant +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include #endif - }; - - /** - * 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; - /** - * 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 "functional/cxx_type_traits_polyfill.h" - trigger_base_t(trigger_type_base type_base_) : type_base(std::move(type_base_)) {} +namespace sqlite_orm { - trigger_base_t& for_each_row() { - this->do_for_each_row = true; - return *this; - } + using xdestroy_fn_t = void (*)(void*); + using null_xdestroy_t = std::integral_constant; + SQLITE_ORM_INLINE_VAR constexpr null_xdestroy_t null_xdestroy_f{}; +} - template - trigger_base_t when(WW expression) { - trigger_base_t res(this->type_base); - res.container_when.field = std::move(expression); - return res; - } +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; - template - partial_trigger_t, S...> begin(S... statements) { - return {*this, std::forward(statements)...}; - } + /** + * 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>; }; /** - * Contains the trigger type and timing + * Constrains a deleter to be or to yield a function pointer. */ - 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 + concept yields_fp = requires(D d) { + // yielding function pointer by using the plus trick + { +d }; + requires std::is_function_v>; }; +#endif +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED /** - * Special case for UPDATE OF (columns) - * Contains the trigger type and timing + * Yield a deleter's function pointer. */ - 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 + struct yield_fp_of { + using type = decltype(+std::declval()); }; +#else - struct trigger_timing_t { - trigger_timing timing; - - trigger_type_base_t delete_() { - return {timing, trigger_type::trigger_delete}; - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_stateless_deleter_v = + std::is_empty::value && std::is_default_constructible::value; - trigger_type_base_t insert() { - return {timing, trigger_type::trigger_insert}; - } + 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; - trigger_type_base_t update() { - return {timing, trigger_type::trigger_update}; - } + 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 - trigger_update_type_t update_of(Cs... columns) { - return {timing, trigger_type::trigger_update, std::forward(columns)...}; - } + template> + struct yield_fp_of { + using type = void; }; - - struct raise_t { - enum class type_t { - ignore, - rollback, - abort, - fail, - }; - - 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 + struct yield_fp_of { + using type = decltype(+std::declval()); }; +#endif + template + using yielded_fn_t = typename yield_fp_of::type; - template - struct new_t { - using expression_type = T; +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept is_unusable_for_xdestroy = + (!stateless_deleter && (yields_fp && !std::convertible_to, xdestroy_fn_t>)); - expression_type expression; - }; + /** + * 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 - struct old_t { - using expression_type = T; + template + concept needs_xdestroy_proxy = + (stateless_deleter && (!yields_fp || !std::convertible_to, xdestroy_fn_t>)); - expression_type expression; - }; - } // NAMESPACE internal - - /** - * NEW.expression function used within TRIGGER expressions - */ - template - internal::new_t new_(T expression) { - return {std::move(expression)}; - } - - /** - * OLD.expression function used within TRIGGER expressions - */ - template - internal::old_t old(T expression) { - return {std::move(expression)}; - } - - /** - * 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)}; - } - - /** - * 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)}; - } - - 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)}); - } - - inline internal::trigger_timing_t before() { - return {internal::trigger_timing::trigger_before}; - } - - inline internal::trigger_timing_t after() { - return {internal::trigger_timing::trigger_after}; - } - - inline internal::trigger_timing_t instead_of() { - return {internal::trigger_timing::trigger_instead_of}; - } -} -#pragma once - -#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" - -#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 - -// #include "functional/cxx_universal.h" - -// #include "functional/cxx_type_traits_polyfill.h" - -// #include "functional/cxx_functional_polyfill.h" - -// #include "is_std_ptr.h" - -// #include "tuple_helper/tuple_filter.h" - -// #include "type_traits.h" - -// #include "error_code.h" - -// #include "arithmetic_tag.h" - -#include // std::is_integral - -// #include "functional/mpl/conditional.h" - -namespace sqlite_orm { - - /** - * 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>; -} - -// #include "xdestroy_handling.h" - -#include // std::integral_constant -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED -#include -#endif - -// #include "functional/cxx_universal.h" - -// #include "functional/cxx_type_traits_polyfill.h" - -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 { - 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 - -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - /** - * Yield a deleter's function pointer. - */ - template - struct yield_fp_of { - using type = decltype(+std::declval()); - }; -#else - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_stateless_deleter_v = - std::is_empty::value && std::is_default_constructible::value; - - 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 - struct yield_fp_of { - using type = decltype(+std::declval()); - }; -#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>)); - - /** - * 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); - } + /** + * 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); + } /** * xDestroy function that invokes the integral function pointer constant. @@ -10004,7 +9980,7 @@ 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); + strncpy(dataCopy, stringData.first, stringData.second + 1); sqlite3_result_text(context, dataCopy, stringData.second, obtain_xdestroy_for(deleter, dataCopy)); } @@ -10019,7 +9995,7 @@ namespace sqlite_orm { } std::pair string_data(const char* s) const { - return {s, int(::strlen(s))}; + return {s, int(strlen(s))}; } #endif }; @@ -10058,7 +10034,7 @@ namespace sqlite_orm { } std::pair string_data(const wchar_t* s) const { - return {s, int(::wcslen(s))}; + return {s, int(wcslen(s))}; } #endif }; @@ -10232,111 +10208,91 @@ namespace sqlite_orm { using bindable_filter_t = filter_tuple_t; } } -#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 -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED -#include -#endif +// #include "column_result.h" -// #include "functional/cxx_universal.h" +#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of +#include // std::reference_wrapper -// #include "functional/cxx_functional_polyfill.h" +// #include "functional/cxx_type_traits_polyfill.h" -// #include "functional/static_magic.h" +// #include "functional/mpl.h" -#ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED -#include // std::false_type, std::true_type, std::integral_constant -#endif -#include // std::forward +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_fy.h" + +#include 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 + struct tuplify { + using type = std::tuple; + }; + template + struct tuplify> { + using type = std::tuple; }; - 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 + using tuplify_t = typename tuplify::type; + } +} - template - decltype(auto) static_if([[maybe_unused]] T&& trueFn) { - if constexpr(B) { - return std::forward(trueFn); - } else { - return empty_callable<>; - } - } +// #include "tuple_helper/tuple_filter.h" - 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); - } +// #include "tuple_helper/tuple_transformer.h" - template - decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { - return std::forward(falseFn); - } +// #include "tuple_helper/same_or_void.h" - template - decltype(auto) static_if(T&& trueFn, F&& falseFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); - } +// #include "type_traits.h" - template - decltype(auto) static_if(T&& trueFn) { - return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable<>); - } +// #include "member_traits/member_traits.h" - template - void call_if_constexpr(L&& lambda, Args&&... args) { - static_if(std::forward(lambda))(std::forward(args)...); - } +// #include "mapped_type_proxy.h" + +#include // std::remove_const + +// #include "type_traits.h" + +// #include "table_reference.h" + +// #include "alias_traits.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; + } } -// #include "tuple_helper/tuple_transformer.h" +// #include "core_functions.h" + +// #include "select_constraints.h" + +// #include "operators.h" + +// #include "rowid.h" // #include "column_result_proxy.h" @@ -10381,1402 +10337,1390 @@ namespace sqlite_orm { } } -// #include "arithmetic_tag.h" +// #include "alias.h" -// #include "pointer_value.h" +// #include "cte_types.h" -// #include "journal_mode.h" +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#include +#include +#endif -#include // std::back_inserter -#include // std::string -#include // std::unique_ptr -#include // std::array -#include // std::transform -#include // std::toupper +// #include "functional/cxx_core_features.h" -#if defined(_WINNT_) -// DELETE is a macro defined in the Windows SDK (winnt.h) -#pragma push_macro("DELETE") -#undef DELETE -#endif +// #include "functional/cxx_type_traits_polyfill.h" -namespace sqlite_orm { +// #include "tuple_helper/tuple_fy.h" - /** - * 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, - }; +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +namespace sqlite_orm { 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)]; - } + /** + * 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; - 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 {}; - } + F field; + }; + + /** + * 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; + + // 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; + }; } } - -#if defined(_WINNT_) -#pragma pop_macro("DELETE") #endif -// #include "error_code.h" +// #include "storage_traits.h" -// #include "is_std_ptr.h" +#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" -namespace sqlite_orm { +// #include "storage_lookup.h" - /** - * 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; +#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 - /* - * Called during multi-step query execution (result set) for each column of a result row. - */ - V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; +// #include "functional/cxx_type_traits_polyfill.h" - /* - * 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; - }; +// #include "type_traits.h" -#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; - }; +namespace sqlite_orm { + namespace internal { - 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 storage_t; - template - concept orm_boxed_value_extractable = requires(const row_extractor& extractor, sqlite3_value* value) { - { extractor.extract(value) } -> std::same_as; - }; -#endif + template + using db_objects_tuple = std::tuple; - 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 {}; - } + struct basic_table; + struct index_base; + struct base_trigger; - /* - * Make a row extractor to be used for converting a value from a SQL result row set to a C++ typed value. + template + struct is_storage : std::false_type {}; + + template + struct is_storage> : std::true_type {}; + template + struct is_storage> : std::true_type {}; + + template + 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 {}; + + /** + * `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 -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - requires(orm_row_value_extractable) -#endif - row_extractor row_value_extractor() { - return {}; - } + template + struct object_type_matches : polyfill::conjunction>>, + std::is_same>> {}; - /* - * Make a row extractor to be used for unboxing a dynamically typed SQL value to a C++ typed value. + /** + * `std::true_type` if given lookup type (object or moniker) is mapped, `std::false_type` otherwise. */ - template -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - requires(orm_boxed_value_extractable) -#endif - row_extractor boxed_value_extractor() { - return {}; - } + template + using lookup_type_matches = object_type_matches; } - 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; - } + // pick/lookup metafunctions + namespace internal { -#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; + /** + * Indirect enabler for DBO, accepting an index to disambiguate non-unique DBOs + */ + template + struct enable_found_table : std::enable_if::value, DBO> {}; - V extract(const char* columnText) const = delete; + /** + * 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; - V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; + template + struct storage_pick_table, db_objects_tuple> + : enable_found_table... {}; - V extract(sqlite3_value* value) const { - return {(P*)sqlite3_value_pointer(value, T::value)}; - } - }; + /** + * 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; - /** - * Undefine using pointer_binding<> for querying values - */ - template - struct row_extractor, void>; + /** + * 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 {}; + + /** + * 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; + +#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 - /** - * Specialization for arithmetic types. - */ - template - struct row_extractor::value>> { - V extract(const char* columnText) const { - return this->extract(columnText, tag()); - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_mapped_v = is_mapped::value; + } +} - V extract(sqlite3_stmt* stmt, int columnIndex) const { - return this->extract(stmt, columnIndex, tag()); +// 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); } - V extract(sqlite3_value* value) const { - return this->extract(value, tag()); + /** + * Return passed in DBOs. + */ + template = true> + decltype(auto) db_objects_for_expression(DBOs& dbObjects, const E&) { + return dbObjects; } - private: - using tag = arithmetic_tag_t; + template = true> + decltype(auto) lookup_table_name(const DBOs& dbObjects); + } +} - V extract(const char* columnText, const int_or_smaller_tag&) const { - return static_cast(atoi(columnText)); - } +// #include "schema/column.h" - V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { - return static_cast(sqlite3_column_int(stmt, columnIndex)); - } +namespace sqlite_orm { + namespace internal { - V extract(sqlite3_value* value, const int_or_smaller_tag&) const { - return static_cast(sqlite3_value_int(value)); - } + namespace storage_traits { - V extract(const char* columnText, const bigint_tag&) const { - return static_cast(atoll(columnText)); - } + /** + * DBO - db object (table) + */ + template + struct storage_mapped_columns_impl + : tuple_transformer, is_column>, field_type_t> {}; - V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { - return static_cast(sqlite3_column_int64(stmt, columnIndex)); - } + template<> + struct storage_mapped_columns_impl { + using type = std::tuple<>; + }; - V extract(sqlite3_value* value, const bigint_tag&) const { - return static_cast(sqlite3_value_int64(value)); - } + /** + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type + */ + template + struct storage_mapped_columns : storage_mapped_columns_impl> {}; - V extract(const char* columnText, const real_tag&) const { - return static_cast(atof(columnText)); - } + /** + * DBO - db object (table) + */ + template + struct storage_mapped_column_expressions_impl + : tuple_transformer, is_column>, column_field_expression_t> {}; - V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { - return static_cast(sqlite3_column_double(stmt, columnIndex)); - } + template<> + struct storage_mapped_column_expressions_impl { + using type = std::tuple<>; + }; - V extract(sqlite3_value* value, const real_tag&) const { - return static_cast(sqlite3_value_double(value)); + /** + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type + */ + template + struct storage_mapped_column_expressions + : storage_mapped_column_expressions_impl> {}; } - }; + } +} - /** - * Specialization for std::string. - */ - template - struct row_extractor::value>> { - T extract(const char* columnText) const { - if(columnText) { - return columnText; - } else { - return {}; - } - } +// #include "function.h" - T extract(sqlite3_stmt* stmt, int columnIndex) const { - if(auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex)) { - return cStr; - } else { - return {}; - } - } +#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 - 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 {}; - } - } +// #include "functional/cxx_type_traits_polyfill.h" - 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 {}; - } - } +// #include "functional/cstring_literal.h" - 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 +// #include "functional/function_traits.h" - template - struct row_extractor::value>> { - using unqualified_type = std::remove_cv_t; +// #include "cxx_type_traits_polyfill.h" - 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 {}; - } - } +// #include "mpl.h" - 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 {}; - } - } +namespace sqlite_orm { + namespace internal { + /* + * Define nested typenames: + * - return_type + * - arguments_tuple + * - signature_type + */ + template + struct function_traits; - 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 {}; - } - } - }; + /* + * A function's return type + */ + template + using function_return_type_t = typename function_traits::return_type; -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct row_extractor>> { - using unqualified_type = std::remove_cv_t; + /* + * A function's arguments tuple + */ + template + class Tuple, + template class ProjectOp = polyfill::type_identity_t> + using function_arguments = typename function_traits::template arguments_tuple; - 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; - } - } + /* + * A function's signature + */ + template + using function_signature_type_t = typename function_traits::signature_type; - 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; - } - } + template + struct function_traits { + using return_type = R; - 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 class Tuple, template class ProjectOp> + using arguments_tuple = Tuple...>; - template<> - struct row_extractor { - nullptr_t extract(const char* /*columnText*/) const { - return nullptr; - } + using signature_type = R(Args...); + }; - nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { - return nullptr; - } + // non-exhaustive partial specializations of `function_traits` - 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 ? ::strlen(columnText) : 0)}; - } - - 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}; - } - - 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}; - } - }; - - /** - * 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}; - } - } - - journal_mode extract(sqlite3_stmt* stmt, int columnIndex) const { - auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); - return this->extract(cStr); - } + template + struct function_traits : function_traits { + using signature_type = R(Args...) const; + }; - journal_mode extract(sqlite3_value* value) const = delete; - }; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct function_traits : function_traits { + using signature_type = R(Args...) noexcept; + }; - namespace internal { + template + struct function_traits : function_traits { + using signature_type = R(Args...) const noexcept; + }; +#endif /* - * Helper to extract a structure from a rowset. - */ - 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. - */ - 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. + * Pick signature of function pointer */ - 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 function_traits : function_traits {}; - /* - * Overload for a table reference, tuple or aggregate of column results returns a structure extractor. + /* + * Pick signature of function reference */ - template, - polyfill::is_specialization_of, - is_table_reference>::value, - bool> = true> - struct_extractor make_row_extractor(const DBOs& dbObjects) { - return {dbObjects}; - } -#endif + template + struct function_traits : function_traits {}; - /** - * Specialization for a tuple of top-level column results. + /* + * Pick signature of pointer-to-member function */ - template - struct struct_extractor, DBOs> { - const DBOs& db_objects; + template + struct function_traits : function_traits {}; + } +} - std::tuple extract(const char* columnText) const = delete; +// #include "type_traits.h" - // 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)...}; - } +// #include "tags.h" - // unused to date - std::tuple...> extract(sqlite3_stmt* stmt, int& columnIndex) const = delete; +namespace sqlite_orm { - std::tuple extract(sqlite3_value* value) const = delete; - }; + struct arg_values; - /** - * 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; + // 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; - O extract(const char* columnText) const = delete; + namespace internal { + template + using scalar_call_function_t = decltype(&F::operator()); - // 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 + using aggregate_step_function_t = decltype(&F::step); - 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{}); - } + template + using aggregate_fin_function_t = decltype(&F::fin); - // 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 + 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> - 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 + struct is_scalar_udf : polyfill::bool_constant> {}; - O extract(sqlite3_value* value) const = delete; - }; - } -} -#pragma once + 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; -#include + template + struct is_aggregate_udf : polyfill::bool_constant> {}; -namespace sqlite_orm { + template + struct function; + } - enum class sync_schema_result { +#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; - /** - * created new table, table with the same tablename did not exist - */ - new_table_created, + /** @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(); })); - /** - * table schema is the same as storage, nothing to be done - */ - already_in_sync, + /** @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; + }; - /** - * removed excess columns in table (than storage) without dropping a table - */ - old_columns_removed, - - /** - * lacking columns in table (than storage) added without dropping a table - */ - new_columns_added, - - /** - * both old_columns_removed and new_columns_added - */ - new_columns_added_and_old_columns_removed, - - /** - * 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, + /** @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>; }; - 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; - } -} -#pragma once - -#include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t -#include // std::string -#include // std::forward + /** @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); -// #include "../functional/cxx_universal.h" + /** @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); -// #include "../tuple_helper/tuple_traits.h" + /** @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 -// #include "../indexed_column.h" + namespace internal { + template + struct callable_arguments_impl; -#include // std::string -#include // std::move + template + struct callable_arguments_impl> { + using args_tuple = function_arguments, std::tuple, std::decay_t>; + using return_type = function_return_type_t>; + }; -// #include "functional/cxx_universal.h" + template + struct callable_arguments_impl> { + using args_tuple = function_arguments, std::tuple, std::decay_t>; + using return_type = function_return_type_t>; + }; -// #include "ast/where.h" +#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 -namespace sqlite_orm { + template + struct callable_arguments : callable_arguments_impl {}; - namespace internal { +#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 indexed_column_t { - using column_type = C; + using std::string::basic_string; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - indexed_column_t(column_type _column_or_expression) : - column_or_expression(std::move(_column_or_expression)) {} + const std::string& operator()() const { + return *this; + } + }; #endif - column_type column_or_expression; - std::string _collation_name; - int _order = 0; // -1 = desc, 1 = asc, 0 = not specified +#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; - indexed_column_t collate(std::string name) { - auto res = std::move(*this); - res._collation_name = std::move(name); - return res; + template>::value, bool> = true> + decltype(auto) operator()() const { + return UDF::name(); } - indexed_column_t asc() { - auto res = std::move(*this); - res._order = 1; - return res; + template::value, bool> = true> + std::string operator()() const { + return std::string{UDF::name()}; } + }; - indexed_column_t desc() { - auto res = std::move(*this); - res._order = -1; - return res; - } + /* + * 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; }; - template - indexed_column_t make_indexed_column(C col) { - return {std::move(col)}; - } + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = true; - template - where_t make_indexed_column(where_t wher) { - return std::move(wher); - } + 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 - indexed_column_t make_indexed_column(indexed_column_t col) { - return std::move(col); + template + SQLITE_ORM_CONSTEVAL bool expected_pointer_value() { + static_assert(polyfill::always_false_v, "Expected a pointer value for I-th argument"); + return false; } - } - /** - * 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)}; - } + template + constexpr bool is_same_pvt_v = expected_pointer_value(); -} + // 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; -// #include "../table_type_of.h" + 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; + } -namespace sqlite_orm { +#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; + } - namespace internal { + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_tag(); +#endif - struct index_base { - std::string name; - bool unique = false; + // not a pointer value, currently leave it unchecked + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::false_type) { + return true; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} -#endif - }; + // check the type of pointer values + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::true_type) { + return is_same_pvt_v; + } - template - struct index_t : index_base { - using elements_type = std::tuple; - using object_type = void; - using table_mapped_type = T; + 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>; -#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_)) {} +#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) > {}); + + 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 + } - elements_type elements; - }; - } + /* + * 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"); + } - 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))...)}); - } + /* + * 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; - 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))...)}); - } + /* + * Generates the SQL function call. + */ + template + function_call operator()(CallArgs... callArgs) const { + check_function_call(); + return {this->udf_holder(), {std::forward(callArgs)...}}; + } - 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 + constexpr auto udf_holder() const { + return internal::udf_holder{}; + } -#include // std::string + // returns a character range + constexpr auto name() const { + return this->udf_holder()(); + } + }; -namespace sqlite_orm { +#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; - namespace internal { + /* + * Generates the SQL function call. + */ + template + function_call operator()(CallArgs... callArgs) const { + check_function_call(); + return {this->udf_holder(), {std::forward(callArgs)...}}; + } - struct rowid_t { - operator std::string() const { - return "rowid"; + /* + * 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); + } } - }; - struct oid_t { - operator std::string() const { - return "oid"; + constexpr auto udf_holder() const { + return internal::udf_holder{this->name()}; } - }; - struct _rowid_t { - operator std::string() const { - return "_rowid_"; + constexpr auto name() const { + return this->nme; } - }; - template - struct table_rowid_t : public rowid_t { - using type = T; - }; + template + consteval quoted_scalar_function(const char (&name)[N], Args&&... constructorArgs) : + udf(std::forward(constructorArgs)...) { + std::copy_n(name, N, this->nme); + } - template - struct table_oid_t : public oid_t { - using type = T; - }; - template - struct table__rowid_t : public _rowid_t { - using type = T; + F udf; + char nme[N]; }; - } - - inline internal::rowid_t rowid() { - return {}; - } - - inline internal::oid_t oid() { - return {}; - } + template + struct quoted_function_builder : cstring_literal { + using cstring_literal::cstring_literal; - inline internal::_rowid_t _rowid_() { - return {}; - } + /* + * From a freestanding function, possibly overloaded. + */ + template + [[nodiscard]] consteval auto quote(F* callable) const { + return quoted_scalar_function{this->cstr, std::move(callable)}; + } - template - internal::table_rowid_t rowid() { - return {}; - } + /* + * 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)}; + } - template - internal::table_oid_t oid() { - return {}; - } + /* + * 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)}; + } - template - internal::table__rowid_t _rowid_() { - return {}; - } -} -#pragma once - -#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 "../functional/cxx_universal.h" -// ::size_t -// #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" - -// #include "../tuple_helper/tuple_traits.h" - -// #include "../tuple_helper/tuple_iteration.h" - -// #include "../tuple_helper/tuple_transformer.h" - -// #include "../member_traits/member_traits.h" - -// #include "../typed_comparator.h" + /* + * 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)...}; + } -namespace sqlite_orm { + /* + * 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 + } - namespace internal { + /** @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{}; - 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; +#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 } -// #include "../type_traits.h" +// #include "ast/special_keywords.h" -// #include "../alias_traits.h" +namespace sqlite_orm { + namespace internal { + struct current_time_t {}; + struct current_date_t {}; + struct current_timestamp_t {}; + } -// #include "../constraints.h" + inline internal::current_time_t current_time() { + return {}; + } -// #include "../table_info.h" + inline internal::current_date_t current_date() { + return {}; + } -// #include "column.h" + inline internal::current_timestamp_t current_timestamp() { + return {}; + } +} namespace sqlite_orm { namespace internal { - template - using is_table_element_or_constraint = mpl::invoke_t, - check_if, - check_if, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_template>, - T>; - -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) /** - * A subselect mapper's CTE moniker, void otherwise. - */ - template - using moniker_of_or_void_t = polyfill::detected_or_t; - - /** - * 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. + * 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 - using mapped_object_type_for_t = polyfill::detected_or_t; + 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 + }; - struct basic_table { + template + using column_result_of_t = typename column_result_t::type; - /** - * Table name. - */ - std::string name; + template + using column_result_for_tuple_t = + transform_tuple_t::template fn>; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct column_result_t, void> { + using type = std::optional>; }; - /** - * Table definition. - */ - 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 + struct column_result_t, void> { + using type = std::optional; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - static constexpr bool is_without_rowid_v = WithoutRowId; + template + struct column_result_t, void> { + using type = bool; + }; - using is_without_rowid = polyfill::bool_constant; + template + struct column_result_t, void> { + using type = bool; + }; - elements_type elements; + template + struct column_result_t { + using type = std::string; + }; -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - table_t(std::string name_, elements_type elements_) : - basic_table{std::move(name_)}, elements{std::move(elements_)} {} -#endif + template + struct column_result_t { + using type = std::string; + }; - table_t without_rowid() const { - return {this->name, this->elements}; - } + template + struct column_result_t { + using type = std::string; + }; - /* - * 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 column_result_t> : member_field_type {}; - /* - * 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 + struct column_result_t, void> { + using type = R; + }; - /* - * 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 column_result_t, void> { + using type = R; + }; - /** - * 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 + struct column_result_t, void> { + using type = typename callable_arguments::return_type; + }; - 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 column_result_t, S, X, Rest...>, void> { + using type = std::unique_ptr>; + }; - 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 + struct column_result_t, S, X>, void> { + using type = std::unique_ptr>; + }; - /** - * 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 column_result_t, void> { + using type = int; + }; - 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 column_result_t { + using type = nullptr_t; + }; - std::vector primary_key_column_names() const { - using pkcol_index_sequence = col_index_sequence_with; + template + struct column_result_t { + using type = int; + }; - 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(); - } - } + template + struct column_result_t, void> : column_result_t {}; - template - void for_each_primary_key_column(L&& lambda) const { - iterate_tuple(this->elements, - col_index_sequence_with{}, - call_as_template_base([&lambda](const auto& column) { - lambda(column.member_pointer); - })); - this->for_each_primary_key([&lambda](auto& primaryKey) { - iterate_tuple(primaryKey.columns, lambda); - }); - } + template + struct column_result_t, void> : column_result_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; - } - }); - } + template + struct column_result_t, void> { + using type = std::string; + }; - /** - * 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 + struct column_result_t, void> { + using type = double; + }; - /** - * 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 + struct column_result_t, void> { + using type = double; + }; - 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 + struct column_result_t, void> { + using type = double; + }; - /** - * 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 column_result_t, void> { + using type = double; + }; - /** - * 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 column_result_t, void> { + using type = double; + }; - /** - * 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 column_result_t, void> { + using type = int; + }; - std::vector get_table_info() const; + template + struct column_result_t, void> { + using type = int; }; - template - struct is_table : std::false_type {}; + template + struct column_result_t, void> { + using type = int; + }; - template - struct is_table> : std::true_type {}; + template + struct column_result_t, void> { + using type = int; + }; - 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; + template + struct column_result_t, void> { + using type = int; + }; - static constexpr bool is_without_rowid_v = false; - using is_without_rowid = polyfill::bool_constant; + template + struct column_result_t { + using type = int64; + }; - module_details_type module_details; + template + struct column_result_t { + using type = int64; + }; -#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 + template + struct column_result_t { + using type = int64; + }; - /** - * 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 + struct column_result_t, void> { + using type = int64; + }; - /** - * 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 column_result_t, void> { + using type = int64; + }; - /** - * 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 column_result_t, void> { + using type = int64; }; - template - struct is_virtual_table : std::false_type {}; + template + struct column_result_t, void> : column_result_t {}; - template - struct is_virtual_table> : std::true_type {}; + template + struct column_result_t, void> : column_result_t {}; -#if SQLITE_VERSION_NUMBER >= 3009000 - template - struct using_fts5_t { - using object_type = T; - using columns_type = std::tuple; +#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; - columns_type columns; + // 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; + }; +#endif - using_fts5_t(columns_type columns) : columns(std::move(columns)) {} + template + struct column_result_t, void> + : conc_tuple>>...> {}; - /** - * 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 + struct column_result_t, void> { + using type = structure>>...>>; + }; - /** - * 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 column_result_t> : column_result_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 { - using col_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->columns, col_index_sequence{}, lambda); - } + template + struct column_result_t> { + using type = + polyfill::detected_t>; + static_assert(!std::is_same::value, + "Compound select statements must return a common type"); }; -#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 + struct column_result_t> { + using type = typename T::result_type; + }; - template - bool exists_in_composite_primary_key(const virtual_table_t& /*virtualTable*/, - const column_field& /*column*/) { - return false; - } - } + template + struct column_result_t, void> { + using type = std::string; + }; -#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"); + /** + * Result for the most simple queries like `SELECT 1` + */ + template + struct column_result_t> { + using type = T; + }; - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); - } + /** + * Result for the most simple queries like `SELECT 'ototo'` + */ + template + struct column_result_t { + using type = std::string; + }; - template - internal::using_fts5_t using_fts5(Cs... columns) { - static_assert(polyfill::conjunction_v...>, - "Incorrect table elements or constraints"); + template + struct column_result_t { + using type = std::string; + }; - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); - } -#endif + template + struct column_result_t, void> : column_result_t> {}; - /** - * 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"); + template + struct column_result_t, void> + : storage_traits::storage_mapped_columns> {}; - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), std::make_tuple(std::forward(args)...)}); - } + template + struct column_result_t, void> { + using type = table_reference; + }; - /** - * 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 + struct column_result_t, void> { + using type = T; + }; - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), std::make_tuple(std::forward(args)...)}); - } + template + struct column_result_t, void> { + using type = R; + }; -#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)...); - } -#endif + template + struct column_result_t, void> { + using type = bool; + }; - 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 + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> { + using type = bool; + }; + + template + struct column_result_t, void> : column_result_t {}; + } +} + +// #include "mapped_type_proxy.h" + +// #include "sync_schema_result.h" + +#include + +namespace sqlite_orm { + + enum class sync_schema_result { + + /** + * created new table, table with the same tablename did not exist + */ + new_table_created, + + /** + * table schema is the same as storage, nothing to be done + */ + already_in_sync, + + /** + * removed excess columns in table (than storage) without dropping a table + */ + old_columns_removed, + + /** + * lacking columns in table (than storage) added without dropping a table + */ + new_columns_added, + + /** + * both old_columns_removed and new_columns_added + */ + new_columns_added_and_old_columns_removed, + + /** + * 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, + }; + + 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; + } +} + +// #include "table_info.h" + +#include // std::string + +namespace sqlite_orm { + + 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 + }; + + 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 + +#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 + }; +} + +// #include "storage_impl.h" #include // std::string -// #include "functional/cxx_universal.h" -// ::size_t // #include "functional/static_magic.h" // #include "functional/index_sequence_util.h" @@ -11793,1747 +11737,1410 @@ namespace sqlite_orm { // #include "cte_types.h" -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include -#include -#endif +// #include "schema/column.h" -// #include "functional/cxx_core_features.h" +// #include "schema/table.h" -// #include "functional/cxx_type_traits_polyfill.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_type_traits_polyfill.h" -#include +// #include "../functional/cxx_functional_polyfill.h" -namespace sqlite_orm { +// #include "../functional/static_magic.h" - namespace internal { +// #include "../functional/mpl.h" - template - struct tuplify { - using type = std::tuple; - }; - template - struct tuplify> { - using type = std::tuple; - }; +// #include "../functional/index_sequence_util.h" - template - using tuplify_t = typename tuplify::type; - } -} +// #include "../tuple_helper/tuple_filter.h" -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -namespace sqlite_orm { +// #include "../tuple_helper/tuple_traits.h" - namespace internal { +// #include "../tuple_helper/tuple_iteration.h" - /** - * 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; +// #include "../tuple_helper/tuple_transformer.h" - F field; - }; +// #include "../member_traits/member_traits.h" - /** - * 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; +// #include "../typed_comparator.h" - // 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; - }; +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; + } } } -#endif -// #include "storage_lookup.h" +// #include "../type_traits.h" -#include // std::true_type, std::false_type, std::remove_const, std::enable_if, std::is_base_of, std::is_void -#include -#include // std::index_sequence, std::make_index_sequence +// #include "../alias_traits.h" -// #include "functional/cxx_universal.h" -// ::size_t -// #include "functional/cxx_type_traits_polyfill.h" +// #include "../constraints.h" -// #include "type_traits.h" +// #include "../table_info.h" -namespace sqlite_orm { - namespace internal { +// #include "index.h" - template - struct storage_t; +#include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t +#include // std::string +#include // std::forward - template - using db_objects_tuple = std::tuple; +// #include "../tuple_helper/tuple_traits.h" - struct basic_table; - struct index_base; - struct base_trigger; +// #include "../indexed_column.h" - template - struct is_storage : std::false_type {}; +#include // std::string +#include // std::move - template - struct is_storage> : std::true_type {}; - template - struct is_storage> : std::true_type {}; - - template - struct is_db_objects : std::false_type {}; +// #include "ast/where.h" - 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 {}; +namespace sqlite_orm { - /** - * `std::true_type` if given object is mapped, `std::false_type` otherwise. - * - * Note: unlike table_t<>, index_t<>::object_type and trigger_t<>::object_type is always void. - */ - template - struct object_type_matches : polyfill::conjunction>>, - std::is_same>> {}; + namespace internal { - /** - * `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 indexed_column_t { + using column_type = C; - // pick/lookup metafunctions - 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 - /** - * Indirect enabler for DBO, accepting an index to disambiguate non-unique DBOs - */ - template - struct enable_found_table : std::enable_if::value, DBO> {}; + column_type column_or_expression; + std::string _collation_name; + int _order = 0; // -1 = desc, 1 = asc, 0 = not specified - /** - * 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; + indexed_column_t collate(std::string name) { + auto res = std::move(*this); + res._collation_name = std::move(name); + return res; + } - template - struct storage_pick_table, db_objects_tuple> - : enable_found_table... {}; + indexed_column_t asc() { + auto res = std::move(*this); + res._order = 1; + return res; + } - /** - * 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; + indexed_column_t desc() { + auto res = std::move(*this); + res._order = -1; + return res; + } + }; - /** - * 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 + indexed_column_t make_indexed_column(C col) { + return {std::move(col)}; + } - /** - * 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 + where_t make_indexed_column(where_t wher) { + return std::move(wher); + } -#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 + indexed_column_t make_indexed_column(indexed_column_t col) { + return std::move(col); + } + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_mapped_v = is_mapped::value; + /** + * 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)}; } } -// runtime lookup functions +// #include "../table_type_of.h" + 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); - } - /** - * Return passed in DBOs. - */ - template = true> - decltype(auto) db_objects_for_expression(DBOs& dbObjects, const E&) { - return dbObjects; - } + struct index_base { + std::string name; + bool unique = false; - template = true> - decltype(auto) lookup_table_name(const DBOs& dbObjects); +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} +#endif + }; + + 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 + + elements_type elements; + }; + } + + 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))...)}); + } + + 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))...)}); } } -// interface functions -namespace sqlite_orm { - namespace internal { +// #include "column.h" - template - using tables_index_sequence = filter_tuple_sequence_t; +namespace sqlite_orm { - 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; - } + namespace internal { - 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 + using is_table_element_or_constraint = mpl::invoke_t, + check_if, + check_if, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_template>, + T>; +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) /** - * Find column name by its type and member pointer. + * A subselect mapper's CTE moniker, void otherwise. */ - template = true> - const std::string* find_column_name(const DBOs& dbObjects, F O::*field) { - return pick_table(dbObjects).find_column_name(field); - } + template + using moniker_of_or_void_t = polyfill::detected_or_t; - /** - * 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; - } - -#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()` + /** + * 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 = 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 + using mapped_object_type_for_t = polyfill::detected_or_t; +#endif - // 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."); + struct basic_table { - return &aliased_field< - ColAlias, - std::tuple_element_t>::field; - } -#endif + /** + * Table name. + */ + std::string name; + }; /** - * Find column name by: - * 1. by explicit object type and member pointer. - * 2. by moniker and member pointer. + * Table definition. */ - 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 table_t : basic_table { #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>; - - // 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; - } + // 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 - } -} -#pragma once + using elements_type = std::tuple; -#include // std::string + static constexpr bool is_without_rowid_v = WithoutRowId; -// #include "constraints.h" + using is_without_rowid = polyfill::bool_constant; -// #include "serializer_context.h" + elements_type elements; -// #include "storage_lookup.h" +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + table_t(std::string name_, elements_type elements_) : + basic_table{std::move(name_)}, elements{std::move(elements_)} {} +#endif -namespace sqlite_orm { + table_t without_rowid() const { + return {this->name, this->elements}; + } - namespace internal { + /* + * 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 - auto serialize(const T& t, const C& context); + /* + * 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()); + } - /** - * Serialize default value of a column's default valu - */ - template - std::string serialize_default_value(const default_t& dft) { - db_objects_tuple<> dbObjects; - serializer_context> context{dbObjects}; - return serialize(dft.value, context); - } + /* + * Returns the number of columns having the specified constraint trait. + */ + template class Trait> + static constexpr int count_of_columns_excluding() { + using excluded_col_index_sequence = col_index_sequence_excluding; + return int(excluded_col_index_sequence::size()); + } - } + /** + * Function used to get field value from object by mapped member pointer/setter/getter. + * + * For a setter the corresponding getter has to be searched, + * so the method returns a pointer to the field as returned by the found getter. + * Otherwise the method invokes the member pointer and returns its result. + */ + template = true> + decltype(auto) object_field_value(const object_type& object, M memberPointer) const { + return polyfill::invoke(memberPointer, object); + } -} -#pragma once + 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; + } -#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" + 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; + } -// #include "functional/cxx_universal.h" + /** + * 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 "functional/cxx_functional_polyfill.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 "functional/static_magic.h" + std::vector primary_key_column_names() const { + using pkcol_index_sequence = col_index_sequence_with; -// #include "functional/mpl.h" + 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(); + } + } -// #include "tuple_helper/tuple_traits.h" + 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); + }); + } -// #include "tuple_helper/tuple_filter.h" + 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; + } + }); + } -// #include "tuple_helper/tuple_transformer.h" + /** + * 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; + } -// #include "tuple_helper/tuple_iteration.h" + /** + * 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); + } -// #include "type_traits.h" + 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); + } -// #include "alias.h" + /** + * 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); + } -// #include "error_code.h" + /** + * 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); + } -// #include "type_printer.h" + /** + * 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); + } -// #include "constraints.h" + std::vector get_table_info() const; + }; -// #include "field_printer.h" + template + struct is_table : std::false_type {}; -#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" + template + struct is_table> : std::true_type {}; -// #include "functional/cxx_universal.h" + 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; -// #include "functional/cxx_type_traits_polyfill.h" + static constexpr bool is_without_rowid_v = false; + using is_without_rowid = polyfill::bool_constant; -// #include "is_std_ptr.h" + module_details_type module_details; -// #include "type_traits.h" +#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 -namespace sqlite_orm { + /** + * 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); + } - /** - * 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; + /** + * 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); + } - 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 indirectly_test_printable; + /** + * 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_printable_v = false; template - SQLITE_ORM_INLINE_VAR constexpr bool - is_printable_v{})>>> = true; + struct is_virtual_table : std::false_type {}; - template - struct is_printable : polyfill::bool_constant> {}; - } + template + struct is_virtual_table> : std::true_type {}; - template - struct field_printer> { - std::string operator()(const T& t) const { - std::stringstream ss; - ss << t; - return ss.str(); - } - }; +#if SQLITE_VERSION_NUMBER >= 3009000 + template + struct using_fts5_t { + using object_type = T; + using columns_type = std::tuple; - /** - * 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(); - } - }; + columns_type columns; - /** - * 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(); - } - }; + using_fts5_t(columns_type columns) : columns(std::move(columns)) {} - /** - * 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(); - } - }; + /** + * 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 - struct field_printer> { - std::string operator()(std::string string) const { - return string; - } - }; + /** + * 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 field_printer, void> { - std::string operator()(const std::vector& t) const { - std::stringstream ss; - ss << std::hex; - for(auto c: t) { - ss << c; + /** + * 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); } - return ss.str(); + }; +#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; } - }; -#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; - - std::string operator()(const T& t) const { - if(t) { - return field_printer()(*t); - } else { - return field_printer{}(nullptr); - } - } - }; - -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct field_printer< - T, - std::enable_if_t, - internal::is_printable>>>> { - using unqualified_type = std::remove_cv_t; - std::string operator()(const T& t) const { - if(t.has_value()) { - return field_printer()(*t); - } else { - return field_printer{}(std::nullopt); - } + template + bool exists_in_composite_primary_key(const virtual_table_t& /*virtualTable*/, + const column_field& /*column*/) { + return false; } - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED -} - -// #include "rowid.h" - -// #include "operators.h" - -// #include "select_constraints.h" - -// #include "core_functions.h" + } -// #include "conditions.h" +#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"); -// #include "statement_binder.h" + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); + } -// #include "column_result.h" + template + internal::using_fts5_t using_fts5(Cs... columns) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); -#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of -#include // std::reference_wrapper + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); + } +#endif -// #include "functional/cxx_universal.h" -// ::nullptr_t -// #include "functional/cxx_type_traits_polyfill.h" + /** + * 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"); -// #include "functional/mpl.h" + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } -// #include "tuple_helper/tuple_traits.h" + /** + * 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"); -// #include "tuple_helper/tuple_fy.h" + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } -// #include "tuple_helper/tuple_filter.h" +#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)...); + } +#endif -// #include "tuple_helper/tuple_transformer.h" + 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 "tuple_helper/same_or_void.h" +// #include "storage_lookup.h" -// #include "type_traits.h" +// interface functions +namespace sqlite_orm { + namespace internal { -// #include "member_traits/member_traits.h" + template + using tables_index_sequence = filter_tuple_sequence_t; -// #include "mapped_type_proxy.h" + 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; + } -#include // std::remove_const + 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); + } -// #include "type_traits.h" + /** + * 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); + } -// #include "table_reference.h" + /** + * 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; + } -// #include "alias_traits.h" +#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; -namespace sqlite_orm { + // 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."); - namespace internal { + return &aliased_field< + ColAlias, + std::tuple_element_t>::field; + } +#endif /** - * If T is a table reference or recordset alias then the typename mapped_type_proxy::type is the unqualified aliased type, - * otherwise unqualified T. + * Find column name by: + * 1. by explicit object type and member pointer. + * 2. by moniker and member pointer. */ - template - struct mapped_type_proxy : std::remove_const {}; + 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); + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - struct mapped_type_proxy : R {}; -#endif +#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>; - template - struct mapped_type_proxy> : std::remove_const> {}; + // 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."); - template - using mapped_type_proxy_t = typename mapped_type_proxy::type; + // 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 } } -// #include "core_functions.h" - -// #include "select_constraints.h" - -// #include "operators.h" +// #include "journal_mode.h" -// #include "rowid.h" - -// #include "column_result_proxy.h" - -// #include "alias.h" - -// #include "cte_types.h" - -// #include "storage_traits.h" - -#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" - -// #include "storage_lookup.h" +#include // std::back_inserter +#include // std::string +#include // std::unique_ptr +#include // std::array +#include // std::transform +#include // std::toupper -// #include "schema/column.h" +#if defined(_WINNT_) +// DELETE is a macro defined in the Windows SDK (winnt.h) +#pragma push_macro("DELETE") +#undef DELETE +#endif namespace sqlite_orm { - namespace internal { - - namespace storage_traits { - - /** - * DBO - db object (table) - */ - template - struct storage_mapped_columns_impl - : tuple_transformer, is_column>, field_type_t> {}; - - template<> - struct storage_mapped_columns_impl { - using type = std::tuple<>; - }; - /** - * DBOs - db_objects_tuple type - * Lookup - mapped or unmapped data type - */ - template - struct storage_mapped_columns : storage_mapped_columns_impl> {}; + /** + * 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, + }; - /** - * DBO - db object (table) - */ - template - struct storage_mapped_column_expressions_impl - : tuple_transformer, is_column>, column_field_expression_t> {}; + namespace internal { - template<> - struct storage_mapped_column_expressions_impl { - using type = std::tuple<>; + inline const std::string& to_string(journal_mode j) { + static std::string res[] = { + "DELETE", + "TRUNCATE", + "PERSIST", + "MEMORY", + "WAL", + "OFF", }; + return res[static_cast(j)]; + } - /** - * DBOs - db_objects_tuple type - * Lookup - mapped or unmapped data type - */ - template - struct storage_mapped_column_expressions - : storage_mapped_column_expressions_impl> {}; + 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 {}; } } } -// #include "function.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 +#if defined(_WINNT_) +#pragma pop_macro("DELETE") #endif -#include // std::tuple, std::tuple_size, std::tuple_element -#include // std::min, std::copy_n -#include // std::move, std::forward - -// #include "functional/cxx_universal.h" -// ::size_t, ::nullptr_t -// #include "functional/cxx_type_traits_polyfill.h" - -// #include "functional/cstring_literal.h" - -// #include "functional/function_traits.h" - -// #include "cxx_type_traits_polyfill.h" - -// #include "mpl.h" - -namespace sqlite_orm { - namespace internal { - /* - * Define nested typenames: - * - return_type - * - arguments_tuple - * - signature_type - */ - template - struct function_traits; - - /* - * A function's return type - */ - template - using function_return_type_t = typename function_traits::return_type; - - /* - * A function's arguments tuple - */ - template - class Tuple, - template class ProjectOp = polyfill::type_identity_t> - using function_arguments = typename function_traits::template arguments_tuple; - - /* - * A function's signature - */ - template - using function_signature_type_t = typename function_traits::signature_type; - - template - struct function_traits { - using return_type = R; - - template class Tuple, template class ProjectOp> - using arguments_tuple = Tuple...>; - - using signature_type = R(Args...); - }; - // non-exhaustive partial specializations of `function_traits` +// #include "mapped_view.h" - template - struct function_traits : function_traits { - using signature_type = R(Args...) const; - }; +#include +#include // std::forward, std::move -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct function_traits : function_traits { - using signature_type = R(Args...) noexcept; - }; +// #include "row_extractor.h" - template - struct function_traits : function_traits { - using signature_type = R(Args...) const noexcept; - }; +#include +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if +#include // atof, atoi, atoll +#include // 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 - - /* - * Pick signature of function pointer - */ - template - struct function_traits : function_traits {}; - - /* - * Pick signature of function reference - */ - template - struct function_traits : function_traits {}; - - /* - * Pick signature of pointer-to-member function - */ - template - struct function_traits : function_traits {}; - } -} - -// #include "type_traits.h" - -// #include "tags.h" - -namespace sqlite_orm { - - struct arg_values; - - // 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; - - namespace internal { - template - using scalar_call_function_t = decltype(&F::operator()); - - template - using aggregate_step_function_t = decltype(&F::step); - - template - using aggregate_fin_function_t = decltype(&F::fin); - - 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 - struct is_scalar_udf : polyfill::bool_constant> {}; - - 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; - - template - struct is_aggregate_udf : polyfill::bool_constant> {}; - - template - struct function; - } - -#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; - - /** @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(); })); - - /** @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; - }; - - /** @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>; - }; - - /** @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); - - /** @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 - - namespace internal { - template - struct callable_arguments_impl; - - template - struct callable_arguments_impl> { - using args_tuple = function_arguments, std::tuple, std::decay_t>; - using return_type = function_return_type_t>; - }; - - template - struct callable_arguments_impl> { - using args_tuple = function_arguments, std::tuple, std::decay_t>; - using return_type = function_return_type_t>; - }; - -#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 callable_arguments : callable_arguments_impl {}; - -#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; - - using std::string::basic_string; - - 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; - - template>::value, bool> = true> - decltype(auto) operator()() const { - return UDF::name(); - } - - template::value, bool> = true> - std::string operator()() const { - return std::string{UDF::name()}; - } - }; - - /* - * 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; - }; - - 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; - - 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 - constexpr bool is_same_pvt_v = expected_pointer_value(); - - // 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 - 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; - } - -#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 - - // not a pointer value, currently leave it unchecked - template - SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::false_type) { - return true; - } - - // check the type of pointer values - template - SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::true_type) { - return is_same_pvt_v; - } - - 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>; - -#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) > {}); - - 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 - } - - /* - * 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"); - } - - /* - * 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; - - /* - * Generates the SQL function call. - */ - template - function_call operator()(CallArgs... callArgs) const { - check_function_call(); - return {this->udf_holder(), {std::forward(callArgs)...}}; - } - - constexpr auto udf_holder() const { - return internal::udf_holder{}; - } - - // returns a character range - constexpr auto name() const { - return this->udf_holder()(); - } - }; - -#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; - - /* - * Generates the SQL function call. - */ - template - function_call operator()(CallArgs... callArgs) const { - check_function_call(); - return {this->udf_holder(), {std::forward(callArgs)...}}; - } - - /* - * 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); - } - } - - constexpr auto udf_holder() const { - return internal::udf_holder{this->name()}; - } - - constexpr auto name() const { - return this->nme; - } - - template - consteval quoted_scalar_function(const char (&name)[N], Args&&... constructorArgs) : - udf(std::forward(constructorArgs)...) { - std::copy_n(name, N, this->nme); - } - - F udf; - char nme[N]; - }; - - template - struct quoted_function_builder : cstring_literal { - using cstring_literal::cstring_literal; - - /* - * From a freestanding function, possibly overloaded. - */ - template - [[nodiscard]] consteval auto quote(F* callable) const { - return quoted_scalar_function{this->cstr, std::move(callable)}; - } - - /* - * 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)}; - } - - /* - * 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)}; - } - - /* - * 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)...}; - } - - /* - * 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 - } - - /** @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; - } - } +#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 -} -// #include "ast/special_keywords.h" +// #include "functional/cxx_functional_polyfill.h" -namespace sqlite_orm { - namespace internal { - struct current_time_t {}; - struct current_date_t {}; - struct current_timestamp_t {}; - } +// #include "functional/static_magic.h" - inline internal::current_time_t current_time() { - return {}; - } +// #include "tuple_helper/tuple_transformer.h" - inline internal::current_date_t current_date() { - return {}; - } +// #include "column_result_proxy.h" - inline internal::current_timestamp_t current_timestamp() { - return {}; - } -} +// #include "arithmetic_tag.h" + +// #include "pointer_value.h" + +// #include "journal_mode.h" + +// #include "error_code.h" + +// #include "is_std_ptr.h" + +// #include "type_traits.h" namespace sqlite_orm { - namespace internal { + /** + * 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; - /** - * 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 + /* + * Called during multi-step query execution (result set) for each column of a result row. */ - 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 - }; + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; - template - using column_result_of_t = typename column_result_t::type; + /* + * 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; + }; - template - using column_result_for_tuple_t = - transform_tuple_t::template fn>; +#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; + }; -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct column_result_t, void> { - using type = std::optional>; + 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 = std::optional; - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + concept orm_boxed_value_extractable = requires(const row_extractor& extractor, sqlite3_value* value) { + { extractor.extract(value) } -> std::same_as; + }; +#endif - template - struct column_result_t, void> { - using type = bool; - }; + 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 {}; + } - template - struct column_result_t, void> { - using type = bool; - }; + /* + * 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 {}; + } - template - struct column_result_t { - using type = std::string; - }; + /* + * 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 - struct column_result_t { - using type = std::string; - }; + 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 - struct column_result_t { - using type = std::string; - }; +#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; - template - struct column_result_t> : member_field_type {}; + V extract(const char* columnText) const = delete; - template - struct column_result_t, void> { - using type = R; - }; + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; - template - struct column_result_t, void> { - using type = R; - }; + V extract(sqlite3_value* value) const { + return {(P*)sqlite3_value_pointer(value, T::value)}; + } + }; - template - struct column_result_t, void> { - using type = typename callable_arguments::return_type; - }; + /** + * Undefine using pointer_binding<> for querying values + */ + template + struct row_extractor, void>; +#endif - template - struct column_result_t, S, X, Rest...>, void> { - using type = std::unique_ptr>; - }; + /** + * 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, S, X>, void> { - using type = std::unique_ptr>; - }; + V extract(sqlite3_stmt* stmt, int columnIndex) const { + return this->extract(stmt, columnIndex, tag()); + } - template - struct column_result_t, void> { - using type = int; - }; + V extract(sqlite3_value* value) const { + return this->extract(value, tag()); + } - template - struct column_result_t { - using type = nullptr_t; - }; + private: + using tag = arithmetic_tag_t; - template - struct column_result_t { - using type = int; - }; + V extract(const char* columnText, const int_or_smaller_tag&) const { + return static_cast(atoi(columnText)); + } + + V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { + return static_cast(sqlite3_column_int(stmt, columnIndex)); + } + + V extract(sqlite3_value* value, const int_or_smaller_tag&) const { + return static_cast(sqlite3_value_int(value)); + } + + V extract(const char* columnText, const bigint_tag&) const { + return static_cast(atoll(columnText)); + } - template - struct column_result_t, void> : column_result_t {}; + 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> : column_result_t {}; + V extract(sqlite3_value* value, const bigint_tag&) const { + return static_cast(sqlite3_value_int64(value)); + } - template - struct column_result_t, void> { - using type = std::string; - }; + V extract(const char* columnText, const real_tag&) const { + return static_cast(atof(columnText)); + } - template - struct column_result_t, void> { - using type = double; - }; + 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 = double; - }; + V extract(sqlite3_value* value, const real_tag&) const { + return static_cast(sqlite3_value_double(value)); + } + }; - template - struct column_result_t, void> { - using type = double; - }; + /** + * 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 = double; - }; + 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 = double; - }; + 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, void> { - using type = int; - }; + 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, void> { - using type = int; - }; + 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, void> { - using type = int; - }; + template + struct row_extractor::value>> { + using unqualified_type = std::remove_cv_t; - template - struct column_result_t, void> { - using type = int; - }; + 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 = int; - }; + 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 { - 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 { - using type = int64; - }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct row_extractor>> { + using unqualified_type = std::remove_cv_t; - template - struct column_result_t { - 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 std::make_optional(rowExtractor.extract(columnText)); + } else { + return std::nullopt; + } + } - 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 std::make_optional(rowExtractor.extract(stmt, columnIndex)); + } else { + return std::nullopt; + } + } - 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 std::make_optional(rowExtractor.extract(value)); + } else { + return std::nullopt; + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct column_result_t, void> { - using type = int64; - }; + template<> + struct row_extractor { + nullptr_t extract(const char* /*columnText*/) const { + return nullptr; + } - template - struct column_result_t, void> : column_result_t {}; + nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { + return nullptr; + } - template - struct column_result_t, void> : 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 ? strlen(columnText) : 0)}; + } -#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; + 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}; + } - // 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; - }; -#endif + 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> - : conc_tuple>>...> {}; + /** + * 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}; + } + } - template - struct column_result_t, void> { - using type = structure>>...>>; - }; + journal_mode extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); + return this->extract(cStr); + } - template - struct column_result_t> : column_result_t {}; + journal_mode extract(sqlite3_value* value) const = delete; + }; - 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; - }; + /* + * Helper to extract a structure from a rowset. + */ + template + struct struct_extractor; - template - struct column_result_t, void> { - using type = std::string; - }; +#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(); + } - /** - * Result for the most simple queries like `SELECT 1` + /* + * Overload for a table reference, tuple or aggregate of column results returns a structure extractor. */ - template - struct column_result_t> { - using type = T; - }; + template, + polyfill::is_specialization_of, + is_table_reference>::value, + bool> = true> + struct_extractor make_row_extractor(const DBOs& dbObjects) { + return {dbObjects}; + } +#endif /** - * Result for the most simple queries like `SELECT 'ototo'` + * Specialization for a tuple of top-level column results. */ - template - struct column_result_t { - using type = std::string; - }; + template + struct struct_extractor, DBOs> { + const DBOs& db_objects; - template - struct column_result_t { - using type = std::string; - }; + std::tuple extract(const char* columnText) const = delete; - template - struct column_result_t, void> : column_result_t> {}; + // 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> - : storage_traits::storage_mapped_columns> {}; + // unused to date + std::tuple...> extract(sqlite3_stmt* stmt, int& columnIndex) const = delete; - template - struct column_result_t, void> { - using type = table_reference; + std::tuple extract(sqlite3_value* value) const = delete; }; - template - struct column_result_t, void> { - using type = T; - }; + /** + * 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; - template - struct column_result_t, void> { - using type = R; - }; + O 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; + // 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 - struct column_result_t, void> { - using type = bool; - }; + 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{}); + } - template - struct column_result_t, void> { - using type = bool; - }; + // 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 - struct column_result_t, void> : column_result_t {}; + 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{}); + } + + O extract(sqlite3_value* value) const = delete; + }; } } -// #include "mapped_type_proxy.h" - -// #include "sync_schema_result.h" - -// #include "table_info.h" - -// #include "storage_impl.h" - -// #include "journal_mode.h" - -// #include "mapped_view.h" - -#include -#include // std::forward, std::move - -// #include "row_extractor.h" - // #include "mapped_iterator.h" #include @@ -13543,8 +13150,6 @@ namespace sqlite_orm { #include // std::system_error #include // std::bind -// #include "functional/cxx_universal.h" -// ::ptrdiff_t // #include "statement_finalizer.h" #include @@ -13917,8 +13522,6 @@ namespace sqlite_orm { #include // std::move, std::forward, std::pair #include // std::tuple -// #include "functional/cxx_universal.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "functional/cxx_functional_polyfill.h" @@ -14014,9 +13617,7 @@ namespace sqlite_orm { #include // std::vector #include // std::tuple -#include // std::forward - -// #include "functional/cxx_universal.h" +#include // std::forward, std::move // #include "functional/cxx_type_traits_polyfill.h" @@ -14053,7 +13654,6 @@ namespace sqlite_orm { internal::dynamic_values_t values(std::vector vector) { return {{std::move(vector)}}; } - } // #include "table_reference.h" @@ -15219,6 +14819,8 @@ namespace sqlite_orm { // #include "ast/match.h" +#include // std::move + namespace sqlite_orm { namespace internal { @@ -16023,8 +15625,6 @@ inline constexpr bool std::ranges::enable_borrowed_range // std::input_iterator_tag, std::default_sentinel_t #include // std::reference_wrapper -// #include "functional/cxx_universal.h" -// ::ptrdiff_t // #include "statement_finalizer.h" // #include "row_extractor.h" @@ -16187,10 +15787,12 @@ inline constexpr bool std::ranges::enable_borrowed_range +#include // atoi #include // std::allocator #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 @@ -16200,8 +15802,6 @@ inline constexpr bool std::ranges::enable_borrowed_range // std::is_same #include // std::find_if, std::ranges::find -// #include "functional/cxx_universal.h" -// ::size_t // #include "functional/cxx_tuple_polyfill.h" #include // std::apply; std::tuple_size @@ -16209,8 +15809,6 @@ inline constexpr bool std::ranges::enable_borrowed_range // std::forward, std::index_sequence, std::make_index_sequence #endif -// #include "../functional/cxx_universal.h" -// ::size_t // #include "../functional/cxx_functional_polyfill.h" // std::invoke @@ -16244,11 +15842,13 @@ namespace sqlite_orm { // #include "pragma.h" #include +#include // atoi #include // std::string #include // std::function #include // std::shared_ptr #include // std::vector #include +#include // std::flush // #include "error_code.h" @@ -16267,14 +15867,16 @@ 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 // #include "functional/cxx_type_traits_polyfill.h" +// #include "functional/cxx_functional_polyfill.h" + // #include "tuple_helper/tuple_iteration.h" +// #include "type_traits.h" + // #include "error_code.h" // #include "serializer_context.h" @@ -16283,6 +15885,8 @@ namespace sqlite_orm { // #include "util.h" +// #include "schema/column.h" + namespace sqlite_orm { namespace internal { template @@ -16831,14 +16435,14 @@ namespace sqlite_orm { auto& res = *(std::vector*)data; if(argc) { auto index = 0; - auto cid = std::atoi(argv[index++]); + auto cid = atoi(argv[index++]); std::string name = argv[index++]; std::string type = argv[index++]; - bool notnull = !!std::atoi(argv[index++]); + bool notnull = !!atoi(argv[index++]); std::string dflt_value = argv[index] ? argv[index] : ""; ++index; - auto pk = std::atoi(argv[index++]); - auto hidden = std::atoi(argv[index++]); + auto pk = atoi(argv[index++]); + auto hidden = atoi(argv[index++]); res.emplace_back(cid, std::move(name), std::move(type), @@ -16868,13 +16472,13 @@ namespace sqlite_orm { auto& res = *(std::vector*)data; if(argc) { auto index = 0; - auto cid = std::atoi(argv[index++]); + auto cid = atoi(argv[index++]); std::string name = argv[index++]; std::string type = argv[index++]; - bool notnull = !!std::atoi(argv[index++]); + bool notnull = !!atoi(argv[index++]); std::string dflt_value = argv[index] ? argv[index] : ""; ++index; - auto pk = std::atoi(argv[index++]); + auto pk = atoi(argv[index++]); res.emplace_back(cid, std::move(name), std::move(type), notnull, std::move(dflt_value), pk); } return 0; @@ -17234,8 +16838,6 @@ namespace sqlite_orm { #include // std::enable_if, std::is_same, std::index_sequence, std::make_index_sequence #include // std::tuple, std::tuple_size, std::tuple_element -// #include "functional/cxx_universal.h" -// ::size_t // #include "functional/cxx_functional_polyfill.h" // #include "type_traits.h" @@ -17431,7 +17033,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,6 +17264,8 @@ namespace sqlite_orm { // #include "serializing_util.h" +// #include "table_info.h" + namespace sqlite_orm { namespace internal { @@ -17799,7 +17403,7 @@ namespace sqlite_orm { [](void* data, int argc, char** argv, char** /*azColName*/) -> int { auto& res = *(bool*)data; if(argc) { - res = !!std::atoi(argv[0]); + res = !!atoi(argv[0]); } return 0; }, @@ -18777,9 +18381,9 @@ namespace sqlite_orm { // #include "statement_serializer.h" +#include // std::enable_if, std::remove_pointer #include // std::stringstream #include // std::string -#include // std::enable_if, std::remove_pointer #include // std::vector #ifndef SQLITE_ORM_OMITS_CODECVT #include // std::wstring_convert @@ -18792,8 +18396,6 @@ namespace sqlite_orm { // #include "functional/cxx_optional.h" -// #include "functional/cxx_universal.h" - // #include "functional/cxx_functional_polyfill.h" // #include "functional/mpl.h" @@ -18828,9 +18430,7 @@ namespace sqlite_orm { // #include "constraints.h" -// #include "conditions.h" - -// #include "schema/column.h" +// #include "conditions.h" // #include "indexed_column.h" @@ -18936,377 +18536,664 @@ namespace sqlite_orm { 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 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_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; + + // Compound statements are never passed in by db_objects_for_expression() + static_assert(!is_compound_operator_v); + + 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)}; + } + }; + + template + std::vector get_cte_column_names(const T& t, const Ctx& context) { + cte_column_names_collector collector; + return collector(t, context); + } + + template + struct cte_column_names_collector> { + using expression_type = As; + + template + std::vector operator()(const expression_type& /*expression*/, const Ctx& /*context*/) const { + return {alias_extractor>::extract()}; + } + }; + + template + struct cte_column_names_collector> { + using expression_type = Wrapper; + + template + std::vector operator()(const expression_type& expression, const Ctx& context) const { + return get_cte_column_names(expression.get(), context); + } + }; + + template + struct cte_column_names_collector> { + using expression_type = Asterisk; + using T = typename Asterisk::type; + + template + std::vector operator()(const expression_type&, const Ctx& context) const { + auto& table = pick_table(context.db_objects); + + std::vector columnNames; + columnNames.reserve(size_t(table.template count_of())); + + table.for_each_column([&columnNames](const column_identifier& column) { + columnNames.push_back(column.name); + }); + return columnNames; + } + }; + + // 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."); + }; + + // 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 + struct cte_column_names_collector> { + using expression_type = Columns; + + 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; + + 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 = 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); + + // 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}; + } + + size_t idx = 0; + iterate_tuple(explicitColRefs, [&idx, &columnNames, &context](auto& colRef) { + using ColRef = polyfill::remove_cvref_t; + + 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; + }); + } + + // 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); + } + } } - template - std::vector& operator()(const asterisk_t& expression, const Ctx& context) { - return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); - } + return columnNames; + } + } +} +#endif - template - std::vector& operator()(const object_t& expression, const Ctx& context) { - return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); - } +// #include "order_by_serializer.h" - 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; - } +#include // std::string +#include // std::stringstream - 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; - } +namespace sqlite_orm { - std::vector collectedExpressions; - }; + namespace internal { + + template + struct order_by_serializer; template - std::vector get_column_names(const T& t, const Ctx& context) { - column_names_getter serializer; + 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; + + 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(); + } + }; + + 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(); + } + }; + } } -// #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 "../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) +// It could be an interesting feature to bring to sqlite_orm that other libraries don't have ? -#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); + enum class trigger_timing { trigger_before, trigger_after, trigger_instead_of }; + enum class trigger_type { trigger_delete, trigger_insert, trigger_update }; - 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); - } - } - } + /** + * 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; - 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(); + /** + * 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; + + 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 - struct cte_column_names_collector { - using expression_type = T; + struct base_trigger { + /** + * Name of the trigger + */ + std::string name; + }; - // Compound statements are never passed in by db_objects_for_expression() - static_assert(!is_compound_operator_v); + /** + * 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& 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)}; - } + /** + * Base of the trigger (contains its type, timing and associated table) + */ + T base; + + /** + * 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 }; - template - std::vector get_cte_column_names(const T& t, const Ctx& context) { - cte_column_names_collector collector; - return collector(t, context); - } + /** + * 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; + + /** + * 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; - template - struct cte_column_names_collector> { - using expression_type = As; + trigger_base_t(trigger_type_base type_base_) : type_base(std::move(type_base_)) {} - template - std::vector operator()(const expression_type& /*expression*/, const Ctx& /*context*/) const { - return {alias_extractor>::extract()}; + trigger_base_t& for_each_row() { + this->do_for_each_row = true; + return *this; } - }; - template - struct cte_column_names_collector> { - using expression_type = Wrapper; + template + trigger_base_t when(WW expression) { + trigger_base_t res(this->type_base); + res.container_when.field = std::move(expression); + return res; + } - template - std::vector operator()(const expression_type& expression, const Ctx& context) const { - return get_cte_column_names(expression.get(), context); + template + partial_trigger_t, S...> begin(S... statements) { + return {*this, std::forward(statements)...}; } }; - template - struct cte_column_names_collector> { - using expression_type = Asterisk; - using T = typename Asterisk::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 + */ + 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; - std::vector columnNames; - columnNames.reserve(size_t(table.template count_of())); + trigger_type_base_t(trigger_timing timing, trigger_type type) : timing(timing), type(type) {} - table.for_each_column([&columnNames](const column_identifier& column) { - columnNames.push_back(column.name); - }); - return columnNames; + template + trigger_base_t on() { + 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."); - }; - - // 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."); - }; + /** + * 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; - template - struct cte_column_names_collector> { - using expression_type = Columns; + /** + * Contains the columns the trigger is watching. Will only + * trigger if one of theses columns is updated. + */ + columns_type columns; - 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_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.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); - - // 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}; - } - - size_t idx = 0; - iterate_tuple(explicitColRefs, [&idx, &columnNames, &context](auto& colRef) { - using ColRef = polyfill::remove_cvref_t; + struct trigger_timing_t { + trigger_timing timing; - 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; - }); + trigger_type_base_t delete_() { + return {timing, trigger_type::trigger_delete}; } - // 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); - } - } + trigger_type_base_t insert() { + return {timing, trigger_type::trigger_insert}; } - return columnNames; - } - } -} -#endif - -// #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)}; } -} -// #include "serializing_util.h" + /** + * 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 "serialize_result_type.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 "statement_binder.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 "values.h" + inline internal::trigger_timing_t before() { + return {internal::trigger_timing::trigger_before}; + } -// #include "schema/triggers.h" + inline internal::trigger_timing_t after() { + return {internal::trigger_timing::trigger_after}; + } -// #include "table_type_of.h" + inline internal::trigger_timing_t instead_of() { + return {internal::trigger_timing::trigger_instead_of}; + } +} + +// #include "schema/column.h" // #include "schema/index.h" // #include "schema/table.h" -// #include "util.h" - -// #include "error_code.h" - namespace sqlite_orm { namespace internal { @@ -21588,8 +21475,6 @@ namespace sqlite_orm { #include #endif -// #include "functional/cxx_universal.h" -// ::size_t // #include "tuple_helper/tuple_fy.h" // #include "table_type_of.h" @@ -22206,126 +22091,485 @@ namespace sqlite_orm { "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() 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) -#endif - result_set_view iterate(Select expression) { - expression.highest_level = true; - auto con = this->get_connection(); - return {this->db_objects, std::move(con), std::move(expression)}; + /** + * 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); } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template -#ifdef SQLITE_ORM_CONCEPTS_SUPPORTED - 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)}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto get(Ids... ids) { + return this->get>(std::forward(ids)...); } -#endif #endif /** - * Delete from routine. - * O is an object's type. Must be specified explicitly. - * @param args optional conditions: `where`, `join` etc - * @example: storage.remove_all(); - DELETE FROM users - * @example: storage.remove_all(where(in(&User::id, {5, 6, 7}))); - DELETE FROM users WHERE id IN (5, 6, 7) + * 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 - void remove_all(Args&&... args) { + template + std::unique_ptr get_pointer(Ids... ids) { this->assert_mapped_type(); - auto statement = this->prepare(sqlite_orm::remove_all(std::forward(args)...)); - this->execute(statement); + auto statement = this->prepare(sqlite_orm::get_pointer(std::forward(ids)...)); + return this->execute(statement); } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - void remove_all(Args&&... args) { - return this->remove_all>(std::forward(args)...); + template + auto get_pointer(Ids... ids) { + return this->get_pointer>(std::forward(ids)...); } #endif /** - * Delete routine. - * O is an object's type. Must be specified explicitly. - * @param ids ids of object to be removed. + * 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 - void remove(Ids... ids) { + std::shared_ptr get_no_throw(Ids... ids) { + return std::shared_ptr(this->get_pointer(std::forward(ids)...)); + } + +#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 statement = this->prepare(sqlite_orm::remove(std::forward(ids)...)); - this->execute(statement); + 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 - void remove(Ids... ids) { - return this->remove>(std::forward(ids)...); + auto get_optional(Ids... ids) { + return this->get_optional>(std::forward(ids)...); + } +#endif + + /** + * SELECT COUNT(*) https://www.sqlite.org/lang_aggfunc.html#count + * @return Number of O object in table. + */ + template + int count(Args&&... args) { + using R = mapped_type_proxy_t; + this->assert_mapped_type(); + auto rows = this->select(sqlite_orm::count(), std::forward(args)...); + if(!rows.empty()) { + return rows.front(); + } else { + return 0; + } + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + int count(Args&&... args) { + return this->count>(std::forward(args)...); + } +#endif + + /** + * SELECT COUNT(X) https://www.sqlite.org/lang_aggfunc.html#count + * @param m member pointer to class mapped to the storage. + * @return count of `m` values from database. + */ + template, is_column_pointer>::value, + bool> = true> + int count(F field, Args&&... args) { + this->assert_mapped_type>(); + auto rows = this->select(sqlite_orm::count(std::move(field)), std::forward(args)...); + if(!rows.empty()) { + return rows.front(); + } else { + return 0; + } + } + + /** + * AVG(X) query. https://www.sqlite.org/lang_aggfunc.html#avg + * @param m is a class member pointer (the same you passed into make_column). + * @return average value from database. + */ + template, is_column_pointer>::value, + bool> = true> + double avg(F field, Args&&... args) { + this->assert_mapped_type>(); + auto rows = this->select(sqlite_orm::avg(std::move(field)), std::forward(args)...); + if(!rows.empty()) { + return rows.front(); + } else { + return 0; + } + } + + template, is_column_pointer>::value, + bool> = true> + std::string group_concat(F field) { + return this->group_concat_internal(std::move(field), {}); + } + + /** + * GROUP_CONCAT(X) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat + * @param m is a class member pointer (the same you passed into make_column). + * @return group_concat query result. + */ + template, + std::enable_if_t::value >= 1, bool> = true, + std::enable_if_t, is_column_pointer>::value, + bool> = true> + std::string group_concat(F field, Args&&... args) { + return this->group_concat_internal(std::move(field), {}, std::forward(args)...); + } + + /** + * GROUP_CONCAT(X, Y) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat + * @param m is a class member pointer (the same you passed into make_column). + * @return group_concat query result. + */ + template, is_column_pointer>::value, + bool> = true> + std::string group_concat(F field, std::string y, Args&&... args) { + return this->group_concat_internal(std::move(field), + std::make_unique(std::move(y)), + std::forward(args)...); + } + + template, is_column_pointer>::value, + bool> = true> + std::string group_concat(F field, const char* y, Args&&... args) { + std::unique_ptr str; + if(y) { + str = std::make_unique(y); + } else { + str = std::make_unique(); + } + return this->group_concat_internal(std::move(field), std::move(str), std::forward(args)...); } -#endif /** - * Update routine. Sets all non primary key fields where primary key is equal. - * O is an object type. May be not specified explicitly cause it can be deduced by - * compiler from first parameter. - * @param o object to be updated. + * MAX(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with max value or null if sqlite engine returned null. */ - template - void update(const O& o) { - this->assert_mapped_type(); - auto statement = this->prepare(sqlite_orm::update(std::ref(o))); - this->execute(statement); + template, + std::enable_if_t, is_column_pointer>::value, + bool> = true> + std::unique_ptr max(F field, Args&&... args) { + this->assert_mapped_type>(); + auto rows = this->select(sqlite_orm::max(std::move(field)), std::forward(args)...); + if(!rows.empty()) { + return std::move(rows.front()); + } else { + return {}; + } } - template - void update_all(S set, Wargs... wh) { - static_assert(internal::is_set::value, - "first argument in update_all can be either set or dynamic_set"); - auto statement = this->prepare(sqlite_orm::update_all(std::move(set), std::forward(wh)...)); - this->execute(statement); + /** + * MIN(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with min value or null if sqlite engine returned null. + */ + template, + std::enable_if_t, is_column_pointer>::value, + bool> = true> + std::unique_ptr min(F field, Args&&... args) { + this->assert_mapped_type>(); + auto rows = this->select(sqlite_orm::min(std::move(field)), std::forward(args)...); + if(!rows.empty()) { + return std::move(rows.front()); + } else { + return {}; + } } - protected: - template - std::string group_concat_internal(F O::*m, std::unique_ptr y, Args&&... args) { - this->assert_mapped_type(); - std::vector rows; - if(y) { - rows = this->select(sqlite_orm::group_concat(m, std::move(*y)), std::forward(args)...); + /** + * SUM(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return std::unique_ptr with sum value or null if sqlite engine returned null. + */ + template, + std::enable_if_t, is_column_pointer>::value, + bool> = true> + std::unique_ptr sum(F field, Args&&... args) { + this->assert_mapped_type>(); + std::vector> rows = + this->select(sqlite_orm::sum(std::move(field)), std::forward(args)...); + if(!rows.empty()) { + if(rows.front()) { + return std::make_unique(std::move(*rows.front())); + } else { + return {}; + } } else { - rows = this->select(sqlite_orm::group_concat(m), std::forward(args)...); + return {}; } + } + + /** + * TOTAL(x) query. + * @param m is a class member pointer (the same you passed into make_column). + * @return total value (the same as SUM but not nullable. More details here + * https://www.sqlite.org/lang_aggfunc.html) + */ + template, is_column_pointer>::value, + bool> = true> + double total(F field, Args&&... args) { + this->assert_mapped_type>(); + auto rows = this->select(sqlite_orm::total(std::move(field)), std::forward(args)...); if(!rows.empty()) { return std::move(rows.front()); } else { @@ -22333,1362 +22577,1373 @@ namespace sqlite_orm { } } - public: /** - * SELECT * routine. - * T is an explicitly specified object mapped to a storage or a table alias. - * R is an explicit return type. This type must have `push_back(O &&)` function. Defaults to `std::vector` - * @return All objects of type O stored in database at the moment in `R`. - * @example: storage.get_all>(); - SELECT * FROM users - * @example: storage.get_all>(where(like(&User::name, "N%")), order_by(&User::id)); - SELECT * FROM users WHERE name LIKE 'N%' ORDER BY id - */ - template>, class... Args> - R get_all(Args&&... args) { - this->assert_mapped_type>(); - auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); + * Select a single column into std::vector or multiple columns into std::vector>. + * For a single column use `auto rows = storage.select(&User::id, where(...)); + * For multicolumns use `auto rows = storage.select(columns(&User::id, &User::name), where(...)); + */ + template + auto select(T m, Args... args) { + static_assert(!is_compound_operator_v || sizeof...(Args) == 0, + "Cannot use args with a compound operator"); + auto statement = this->prepare(sqlite_orm::select(std::move(m), std::forward(args)...)); return this->execute(statement); } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) /** - * SELECT * routine. - * `mapped` is an explicitly specified table reference or table alias of an object to be extracted. - * `R` is the container return type, which must have a `R::push_back(O&&)` method, and defaults to `std::vector` - * @return All objects stored in database. - * @example: storage.get_all>(); - SELECT sqlite_schema.* FROM sqlite_master AS sqlite_schema - */ - template>, - class... Args> - R get_all(Args&&... args) { - this->assert_mapped_type>(); - auto statement = this->prepare(sqlite_orm::get_all(std::forward(args)...)); + * Using a CTE, select a single column into std::vector or multiple columns into std::vector>. + */ + template + auto with(CTE cte, E expression) { + auto statement = this->prepare(sqlite_orm::with(std::move(cte), std::move(expression))); return this->execute(statement); } -#endif /** - * 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::unique_ptr stored in database at the moment. - * @example: storage.get_all_pointer>>(); - SELECT * FROM users - * @example: storage.get_all_pointer>>(where(length(&User::name) > 6)); - SELECT * FROM users WHERE LENGTH(name) > 6 - */ - template>, class... Args> - auto get_all_pointer(Args&&... args) { - this->assert_mapped_type(); - auto statement = this->prepare(sqlite_orm::get_all_pointer(std::forward(args)...)); + * Using a CTE, select a single column into std::vector or multiple columns into std::vector>. + */ + template + auto with(common_table_expressions cte, E expression) { + auto statement = this->prepare(sqlite_orm::with(std::move(cte), std::move(expression))); return this->execute(statement); } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template>>, - class... Args> - auto get_all_pointer(Args&&... args) { - return this->get_all_pointer>(std::forward(args)...); + /** + * Using a CTE, select a single column into std::vector or multiple columns into std::vector>. + */ + template + auto with_recursive(CTE cte, E expression) { + auto statement = this->prepare(sqlite_orm::with_recursive(std::move(cte), std::move(expression))); + return this->execute(statement); } -#endif -#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)...)); + * Using a CTE, select a single column into std::vector or multiple columns into std::vector>. + */ + template + auto with_recursive(common_table_expressions cte, E expression) { + auto statement = this->prepare(sqlite_orm::with_recursive(std::move(cte), std::move(expression))); return this->execute(statement); } #endif -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template>>, - class... Args> - auto get_all_optional(Args&&... conditions) { - return this->get_all_optional>(std::forward(conditions)...); + template = true> + std::string dump(const T& preparedStatement, bool parametrized = true) const { + return this->dump_highest_level(preparedStatement.expression, parametrized); } -#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::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"); -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto get(Ids... ids) { - return this->get>(std::forward(ids)...); + 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); } -#endif /** - * 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. + * Returns a string representation of object of a class mapped to the storage. + * Type of string has json-like style. */ - 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 = true> + std::string dump(const O& object) const { + auto& table = this->get_table(); + std::stringstream ss; + ss << "{ "; + table.for_each_column([&ss, &object, first = true](auto& column) mutable { + using column_type = std::decay_t; + using field_type = typename column_type::field_type; + constexpr std::array sep = {", ", ""}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto get_pointer(Ids... ids) { - return this->get_pointer>(std::forward(ids)...); + ss << sep[std::exchange(first, false)] << column.name << " : '" + << field_printer{}(polyfill::invoke(column.member_pointer, object)) << "'"; + }); + ss << " }"; + return ss.str(); } -#endif /** - * 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.) + * This is REPLACE (INSERT OR REPLACE) function. + * Also if you need to insert value with knows id you should + * also you this function instead of insert cause inserts ignores + * id and creates own one. */ - template - std::shared_ptr get_no_throw(Ids... ids) { - return std::shared_ptr(this->get_pointer(std::forward(ids)...)); + template + void replace(const O& o) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::replace(std::ref(o))); + this->execute(statement); } -#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) { + template + void replace_range(It from, It to, Projection project = {}) { + using O = std::decay_t; this->assert_mapped_type(); - auto statement = this->prepare(sqlite_orm::get_optional(std::forward(ids)...)); - return this->execute(statement); - } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + if(from == to) { + return; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto get_optional(Ids... ids) { - return this->get_optional>(std::forward(ids)...); + auto statement = + this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); } -#endif - /** - * SELECT COUNT(*) https://www.sqlite.org/lang_aggfunc.html#count - * @return Number of O object in table. - */ - template - int count(Args&&... args) { - using R = mapped_type_proxy_t; - this->assert_mapped_type(); - auto rows = this->select(sqlite_orm::count(), std::forward(args)...); - if(!rows.empty()) { - return rows.front(); - } else { - return 0; + template + void replace_range(It from, It to, Projection project = {}) { + this->assert_mapped_type(); + if(from == to) { + return; } - } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - int count(Args&&... args) { - return this->count>(std::forward(args)...); + auto statement = + this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); } -#endif - /** - * SELECT COUNT(X) https://www.sqlite.org/lang_aggfunc.html#count - * @param m member pointer to class mapped to the storage. - * @return count of `m` values from database. - */ - template, is_column_pointer>::value, - bool> = true> - int count(F field, Args&&... args) { - this->assert_mapped_type>(); - auto rows = this->select(sqlite_orm::count(std::move(field)), std::forward(args)...); - if(!rows.empty()) { - return rows.front(); - } else { - return 0; - } + template + int insert(const O& o, columns_t cols) { + static_assert(cols.count > 0, "Use insert or replace with 1 argument instead"); + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::insert(std::ref(o), std::move(cols))); + return int(this->execute(statement)); } /** - * AVG(X) query. https://www.sqlite.org/lang_aggfunc.html#avg - * @param m is a class member pointer (the same you passed into make_column). - * @return average value from database. + * Insert routine. Inserts object with all non primary key fields in passed object. Id of passed + * object doesn't matter. + * @return id of just created object. */ - template, is_column_pointer>::value, - bool> = true> - double avg(F field, Args&&... args) { - this->assert_mapped_type>(); - auto rows = this->select(sqlite_orm::avg(std::move(field)), std::forward(args)...); - if(!rows.empty()) { - return rows.front(); - } else { - return 0; - } - } - - template, is_column_pointer>::value, - bool> = true> - std::string group_concat(F field) { - return this->group_concat_internal(std::move(field), {}); + template + int insert(const O& o) { + this->assert_mapped_type(); + this->assert_insertable_type(); + auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); + return int(this->execute(statement)); } /** - * GROUP_CONCAT(X) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat - * @param m is a class member pointer (the same you passed into make_column). - * @return group_concat query result. + * Raw insert 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 + * ```sql + * INSERT INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * storage.insert(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix"))); + * ``` + * One more example: + * ```sql + * INSERT INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * storage.insert(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs"))); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * INSERT INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * storage.insert(into(), default_values()); + * ``` + * Also one can use `INSERT OR ABORT`/`INSERT OR FAIL`/`INSERT OR IGNORE`/`INSERT OR REPLACE`/`INSERT ROLLBACK`: + * ```c++ + * storage.insert(or_ignore(), into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs"))); + * storage.insert(or_rollback(), into(), default_values()); + * storage.insert(or_abort(), into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix"))); + * ``` */ - template, - std::enable_if_t::value >= 1, bool> = true, - std::enable_if_t, is_column_pointer>::value, - bool> = true> - std::string group_concat(F field, Args&&... args) { - return this->group_concat_internal(std::move(field), {}, std::forward(args)...); + template + void insert(Args... args) { + auto statement = this->prepare(sqlite_orm::insert(std::forward(args)...)); + this->execute(statement); } /** - * GROUP_CONCAT(X, Y) query. https://www.sqlite.org/lang_aggfunc.html#groupconcat - * @param m is a class member pointer (the same you passed into make_column). - * @return group_concat query result. + * 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 + * ```sql + * REPLACE INTO users (id, name) VALUES(5, 'Little Mix') + * ``` + * will be + * ```c++ + * storage.prepare(replace(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); + * ``` + * One more example: + * ```sql + * REPLACE INTO singers (name) VALUES ('Sofia Reyes')('Kungs') + * ``` + * will be + * ```c++ + * storage.prepare(replace(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); + * ``` + * One can use `default_values` to add `DEFAULT VALUES` modifier: + * ```sql + * REPLACE INTO users DEFAULT VALUES + * ``` + * will be + * ```c++ + * storage.prepare(replace(into(), default_values())); + * ``` */ - template, is_column_pointer>::value, - bool> = true> - std::string group_concat(F field, std::string y, Args&&... args) { - return this->group_concat_internal(std::move(field), - std::make_unique(std::move(y)), - std::forward(args)...); + template + void replace(Args... args) { + auto statement = this->prepare(sqlite_orm::replace(std::forward(args)...)); + this->execute(statement); } - template, is_column_pointer>::value, - bool> = true> - std::string group_concat(F field, const char* y, Args&&... args) { - std::unique_ptr str; - if(y) { - str = std::make_unique(y); - } else { - str = std::make_unique(); + template + void insert_range(It from, It to, Projection project = {}) { + using O = std::decay_t(), *std::declval()))>; + this->assert_mapped_type(); + this->assert_insertable_type(); + if(from == to) { + return; } - return this->group_concat_internal(std::move(field), std::move(str), std::forward(args)...); + auto statement = + this->prepare(sqlite_orm::insert_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); } - /** - * MAX(x) query. - * @param m is a class member pointer (the same you passed into make_column). - * @return std::unique_ptr with max value or null if sqlite engine returned null. - */ - template, - std::enable_if_t, is_column_pointer>::value, - bool> = true> - std::unique_ptr max(F field, Args&&... args) { - this->assert_mapped_type>(); - auto rows = this->select(sqlite_orm::max(std::move(field)), std::forward(args)...); - if(!rows.empty()) { - return std::move(rows.front()); - } else { - return {}; + template + void insert_range(It from, It to, Projection project = {}) { + this->assert_mapped_type(); + this->assert_insertable_type(); + if(from == to) { + return; } + auto statement = + this->prepare(sqlite_orm::insert_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); } /** - * MIN(x) query. - * @param m is a class member pointer (the same you passed into make_column). - * @return std::unique_ptr with min value or null if sqlite engine returned null. + * Change table name inside storage's schema info. This function does not + * affect database */ - template, - std::enable_if_t, is_column_pointer>::value, - bool> = true> - std::unique_ptr min(F field, Args&&... args) { - this->assert_mapped_type>(); - auto rows = this->select(sqlite_orm::min(std::move(field)), std::forward(args)...); - if(!rows.empty()) { - return std::move(rows.front()); - } else { - return {}; - } + template + void rename_table(std::string name) { + this->assert_mapped_type(); + auto& table = this->get_table(); + table.name = std::move(name); } + using storage_base::rename_table; + /** - * SUM(x) query. - * @param m is a class member pointer (the same you passed into make_column). - * @return std::unique_ptr with sum value or null if sqlite engine returned null. + * Get table's name stored in storage's schema info. This function does not call + * any SQLite queries */ - template, - std::enable_if_t, is_column_pointer>::value, - bool> = true> - std::unique_ptr sum(F field, Args&&... args) { - this->assert_mapped_type>(); - std::vector> rows = - this->select(sqlite_orm::sum(std::move(field)), std::forward(args)...); - if(!rows.empty()) { - if(rows.front()) { - return std::make_unique(std::move(*rows.front())); + template + const std::string& tablename() const { + this->assert_mapped_type(); + auto& table = this->get_table(); + return table.name; + } + + template + [[deprecated("Use the more accurately named function `find_column_name()`")]] const std::string* + column_name(F O::*memberPointer) const { + return internal::find_column_name(this->db_objects, memberPointer); + } + + template + const std::string* find_column_name(F O::*memberPointer) const { + return internal::find_column_name(this->db_objects, memberPointer); + } + + protected: + template + sync_schema_result schema_status(const virtual_table_t&, sqlite3*, bool, bool*) { + return sync_schema_result::already_in_sync; + } + + template + sync_schema_result schema_status(const trigger_t&, sqlite3*, bool, bool*) { + return sync_schema_result::already_in_sync; + } + + template + sync_schema_result schema_status(const index_t&, sqlite3*, bool, bool*) { + return sync_schema_result::already_in_sync; + } + + template + sync_schema_result schema_status(const table_t& table, + sqlite3* db, + bool preserve, + bool* attempt_to_preserve) { + if(attempt_to_preserve) { + *attempt_to_preserve = true; + } + + auto dbTableInfo = this->pragma.table_xinfo(table.name); + auto res = sync_schema_result::already_in_sync; + + // first let's see if table with such name exists.. + auto gottaCreateTable = !this->table_exists(db, table.name); + if(!gottaCreateTable) { + + // get table info provided in `make_table` call.. + auto storageTableInfo = table.get_table_info(); + + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; + + if(calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { + gottaCreateTable = true; + } + + if(!gottaCreateTable) { // if all storage columns are equal to actual db columns but there are + // excess columns at the db.. + if(!dbTableInfo.empty()) { + // extra table columns than storage columns + if(!preserve) { +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + res = sync_schema_result::old_columns_removed; +#else + gottaCreateTable = true; +#endif + } else { + res = sync_schema_result::old_columns_removed; + } + } + } + if(gottaCreateTable) { + res = sync_schema_result::dropped_and_recreated; } else { - return {}; + if(!columnsToAdd.empty()) { + // extra storage columns than table columns + for(const table_xinfo* colInfo: columnsToAdd) { + const basic_generated_always::storage_type* generatedStorageType = + table.find_column_generated_storage_type(colInfo->name); + if(generatedStorageType) { + if(*generatedStorageType == basic_generated_always::storage_type::stored) { + gottaCreateTable = true; + break; + } + // fallback cause VIRTUAL can be added + } else { + if(colInfo->notnull && colInfo->dflt_value.empty()) { + gottaCreateTable = true; + // no matter if preserve is true or false, there is no way to preserve data, so we wont try! + if(attempt_to_preserve) { + *attempt_to_preserve = false; + }; + break; + } + } + } + if(!gottaCreateTable) { + if(res == sync_schema_result::old_columns_removed) { + res = sync_schema_result::new_columns_added_and_old_columns_removed; + } else { + res = sync_schema_result::new_columns_added; + } + } else { + res = sync_schema_result::dropped_and_recreated; + } + } else { + if(res != sync_schema_result::old_columns_removed) { + res = sync_schema_result::already_in_sync; + } + } } } else { - return {}; + res = sync_schema_result::new_table_created; } + return res; } - /** - * TOTAL(x) query. - * @param m is a class member pointer (the same you passed into make_column). - * @return total value (the same as SUM but not nullable. More details here - * https://www.sqlite.org/lang_aggfunc.html) - */ - template, is_column_pointer>::value, - bool> = true> - double total(F field, Args&&... args) { - this->assert_mapped_type>(); - auto rows = this->select(sqlite_orm::total(std::move(field)), std::forward(args)...); - if(!rows.empty()) { - return std::move(rows.front()); - } else { - return {}; - } + 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; + context_t context{this->db_objects}; + auto query = serialize(virtualTable, context); + perform_void_exec(db, query); + return res; } - /** - * Select a single column into std::vector or multiple columns into std::vector>. - * For a single column use `auto rows = storage.select(&User::id, where(...)); - * For multicolumns use `auto rows = storage.select(columns(&User::id, &User::name), where(...)); - */ - template - auto select(T m, Args... args) { - static_assert(!is_compound_operator_v || sizeof...(Args) == 0, - "Cannot use args with a compound operator"); - auto statement = this->prepare(sqlite_orm::select(std::move(m), std::forward(args)...)); - return this->execute(statement); + 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; + context_t context{this->db_objects}; + auto query = serialize(index, context); + perform_void_exec(db, query); + return res; } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - /** - * Using a CTE, select a single column into std::vector or multiple columns into std::vector>. - */ - template - auto with(CTE cte, E expression) { - auto statement = this->prepare(sqlite_orm::with(std::move(cte), std::move(expression))); - return this->execute(statement); + 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; + context_t context{this->db_objects}; + auto query = serialize(trigger, context); + perform_void_exec(db, query); + return res; } - /** - * Using a CTE, select a single column into std::vector or multiple columns into std::vector>. - */ - template - auto with(common_table_expressions cte, E expression) { - auto statement = this->prepare(sqlite_orm::with(std::move(cte), std::move(expression))); - return this->execute(statement); - } + template = true> + sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); - /** - * Using a CTE, select a single column into std::vector or multiple columns into std::vector>. - */ - template - auto with_recursive(CTE cte, E expression) { - auto statement = this->prepare(sqlite_orm::with_recursive(std::move(cte), std::move(expression))); - return this->execute(statement); + template + void add_column(sqlite3* db, const std::string& tableName, const C& column) const { + 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()); } - /** - * Using a CTE, select a single column into std::vector or multiple columns into std::vector>. - */ - template - auto with_recursive(common_table_expressions cte, E expression) { - auto statement = this->prepare(sqlite_orm::with_recursive(std::move(cte), std::move(expression))); - return this->execute(statement); + template + auto execute_select(const S& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + + using R = decltype(make_row_extractor(this->db_objects).extract(nullptr, 0)); + std::vector res; + perform_steps( + stmt, + [rowExtractor = make_row_extractor(this->db_objects), &res](sqlite3_stmt* stmt) { + res.push_back(rowExtractor.extract(stmt, 0)); + }); + res.shrink_to_fit(); + return res; } -#endif - template = true> - std::string dump(const T& preparedStatement, bool parametrized = true) const { - return this->dump_highest_level(preparedStatement.expression, parametrized); + 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() + context.skip_table_name = false; + return serialize(expression, context); } - template, - 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"); + 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; - 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); + 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}; } + public: /** - * Returns a string representation of object of a class mapped to the storage. - * Type of string has json-like style. + * This is a cute function used to replace migration up/down functionality. + * It performs check storage schema with actual db schema and: + * * if there are excess tables exist in db they are ignored (not dropped) + * * every table from storage is compared with it's db analog and + * * if table doesn't exist it is being created + * * if table exists its colums are being compared with table_info from db and + * * if there are columns in db that do not exist in storage (excess) table will be dropped and + * recreated + * * if there are columns in storage that do not exist in db they will be added using `ALTER TABLE + * ... ADD COLUMN ...' command + * * if there is any column existing in both db and storage but differs by any of + * properties/constraints (pk, notnull, dflt_value) table will be dropped and recreated. Be aware that + * `sync_schema` doesn't guarantee that data will not be dropped. It guarantees only that it will make db + * schema the same as you specified in `make_storage` function call. A good point is that if you have no db + * file at all it will be created and all tables also will be created with exact tables and columns you + * specified in `make_storage`, `make_table` and `make_column` calls. The best practice is to call this + * function right after storage creation. + * @param preserve affects function's behaviour in case it is needed to remove a column. If it is `false` + * so table will be dropped if there is column to remove if SQLite version is < 3.35.0 and remove column if SQLite version >= 3.35.0, + * if `true` - table is being copied into another table, dropped and copied table is renamed with source table name. + * Warning: sync_schema doesn't check foreign keys cause it is unable to do so in sqlite3. If you know how to get foreign key info please + * submit an issue https://github.com/fnc12/sqlite_orm/issues + * @return std::map with std::string key equal table name and `sync_schema_result` as value. + * `sync_schema_result` is a enum value that stores table state after syncing a schema. `sync_schema_result` + * can be printed out on std::ostream with `operator<<`. */ - template = true> - std::string dump(const O& object) const { - auto& table = this->get_table(); - std::stringstream ss; - ss << "{ "; - table.for_each_column([&ss, &object, first = true](auto& column) mutable { - using column_type = std::decay_t; - using field_type = typename column_type::field_type; - constexpr std::array sep = {", ", ""}; - - ss << sep[std::exchange(first, false)] << column.name << " : '" - << field_printer{}(polyfill::invoke(column.member_pointer, object)) << "'"; + std::map sync_schema(bool preserve = false) { + auto con = this->get_connection(); + std::map result; + iterate_tuple(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { + sync_schema_result status = this->sync_table(schemaObject, db, preserve); + result.emplace(schemaObject.name, status); }); - ss << " }"; - return ss.str(); + return result; } /** - * This is REPLACE (INSERT OR REPLACE) function. - * Also if you need to insert value with knows id you should - * also you this function instead of insert cause inserts ignores - * id and creates own one. + * This function returns the same map that `sync_schema` returns but it + * doesn't perform `sync_schema` actually - just simulates it in case you want to know + * what will happen if you sync your schema. */ - template - void replace(const O& o) { - this->assert_mapped_type(); - auto statement = this->prepare(sqlite_orm::replace(std::ref(o))); - this->execute(statement); + std::map sync_schema_simulate(bool preserve = false) { + auto con = this->get_connection(); + std::map result; + iterate_tuple(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { + sync_schema_result status = this->schema_status(schemaObject, db, preserve, nullptr); + result.emplace(schemaObject.name, status); + }); + return result; } - template - void replace_range(It from, It to, Projection project = {}) { - using O = std::decay_t; - this->assert_mapped_type(); - if(from == to) { - return; - } + using storage_base::table_exists; // now that it is in storage_base make it into overload set - auto statement = - this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); - this->execute(statement); +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template, is_insert_raw>, bool> = true> + prepared_statement_t> prepare(with_t sel) { + return this->prepare_impl>(std::move(sel)); } +#endif - template - void replace_range(It from, It to, Projection project = {}) { - this->assert_mapped_type(); - if(from == to) { - return; - } - - auto statement = - this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); - this->execute(statement); + template + prepared_statement_t> prepare(select_t statement) { + statement.highest_level = true; + return this->prepare_impl(std::move(statement)); } - template - int insert(const O& o, columns_t cols) { - static_assert(cols.count > 0, "Use insert or replace with 1 argument instead"); - this->assert_mapped_type(); - auto statement = this->prepare(sqlite_orm::insert(std::ref(o), std::move(cols))); - return int(this->execute(statement)); + template + prepared_statement_t> prepare(get_all_t statement) { + return this->prepare_impl(std::move(statement)); } - /** - * Insert routine. Inserts object with all non primary key fields in passed object. Id of passed - * object doesn't matter. - * @return id of just created object. - */ - template - int insert(const O& o) { - this->assert_mapped_type(); - this->assert_insertable_type(); - auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); - return int(this->execute(statement)); + template + prepared_statement_t> prepare(get_all_pointer_t statement) { + return this->prepare_impl(std::move(statement)); } - /** - * Raw insert 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 - * ```sql - * INSERT INTO users (id, name) VALUES(5, 'Little Mix') - * ``` - * will be - * ```c++ - * storage.insert(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix"))); - * ``` - * One more example: - * ```sql - * INSERT INTO singers (name) VALUES ('Sofia Reyes')('Kungs') - * ``` - * will be - * ```c++ - * storage.insert(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs"))); - * ``` - * One can use `default_values` to add `DEFAULT VALUES` modifier: - * ```sql - * INSERT INTO users DEFAULT VALUES - * ``` - * will be - * ```c++ - * storage.insert(into(), default_values()); - * ``` - * Also one can use `INSERT OR ABORT`/`INSERT OR FAIL`/`INSERT OR IGNORE`/`INSERT OR REPLACE`/`INSERT ROLLBACK`: - * ```c++ - * storage.insert(or_ignore(), into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs"))); - * storage.insert(or_rollback(), into(), default_values()); - * storage.insert(or_abort(), into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix"))); - * ``` - */ template - void insert(Args... args) { - auto statement = this->prepare(sqlite_orm::insert(std::forward(args)...)); - this->execute(statement); + prepared_statement_t> prepare(replace_raw_t statement) { + return this->prepare_impl(std::move(statement)); } - /** - * 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 - * ```sql - * REPLACE INTO users (id, name) VALUES(5, 'Little Mix') - * ``` - * will be - * ```c++ - * storage.prepare(replace(into, columns(&User::id, &User::name), values(std::make_tuple(5, "Little Mix")))); - * ``` - * One more example: - * ```sql - * REPLACE INTO singers (name) VALUES ('Sofia Reyes')('Kungs') - * ``` - * will be - * ```c++ - * storage.prepare(replace(into(), columns(&Singer::name), values(std::make_tuple("Sofia Reyes"), std::make_tuple("Kungs")))); - * ``` - * One can use `default_values` to add `DEFAULT VALUES` modifier: - * ```sql - * REPLACE INTO users DEFAULT VALUES - * ``` - * will be - * ```c++ - * storage.prepare(replace(into(), default_values())); - * ``` - */ template - void replace(Args... args) { - auto statement = this->prepare(sqlite_orm::replace(std::forward(args)...)); - this->execute(statement); + prepared_statement_t> prepare(insert_raw_t statement) { + return this->prepare_impl(std::move(statement)); } - template - void insert_range(It from, It to, Projection project = {}) { - using O = std::decay_t(), *std::declval()))>; - this->assert_mapped_type(); - this->assert_insertable_type(); - if(from == to) { - return; - } - auto statement = - this->prepare(sqlite_orm::insert_range(std::move(from), std::move(to), std::move(project))); - this->execute(statement); +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + prepared_statement_t> + prepare(get_all_optional_t statement) { + return this->prepare_impl(std::move(statement)); } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - void insert_range(It from, It to, Projection project = {}) { - this->assert_mapped_type(); - this->assert_insertable_type(); - if(from == to) { - return; - } - auto statement = - this->prepare(sqlite_orm::insert_range(std::move(from), std::move(to), std::move(project))); - this->execute(statement); + template + prepared_statement_t> prepare(update_all_t statement) { + return this->prepare_impl(std::move(statement)); } - /** - * Change table name inside storage's schema info. This function does not - * affect database - */ - template - void rename_table(std::string name) { - this->assert_mapped_type(); - auto& table = this->get_table(); - table.name = std::move(name); + template + prepared_statement_t> prepare(remove_all_t statement) { + return this->prepare_impl(std::move(statement)); } - using storage_base::rename_table; + template + prepared_statement_t> prepare(get_t statement) { + return this->prepare_impl(std::move(statement)); + } - /** - * Get table's name stored in storage's schema info. This function does not call - * any SQLite queries - */ - template - const std::string& tablename() const { - this->assert_mapped_type(); - auto& table = this->get_table(); - return table.name; + template + prepared_statement_t> prepare(get_pointer_t statement) { + return this->prepare_impl(std::move(statement)); } - template - [[deprecated("Use the more accurately named function `find_column_name()`")]] const std::string* - column_name(F O::*memberPointer) const { - return internal::find_column_name(this->db_objects, memberPointer); +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + prepared_statement_t> prepare(get_optional_t statement) { + return this->prepare_impl(std::move(statement)); } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - const std::string* find_column_name(F O::*memberPointer) const { - return internal::find_column_name(this->db_objects, memberPointer); + template + prepared_statement_t> prepare(update_t statement) { + using object_type = expression_object_type_t; + this->assert_mapped_type(); + this->assert_updatable_type(); + return this->prepare_impl(std::move(statement)); } - protected: - template - sync_schema_result schema_status(const virtual_table_t&, sqlite3*, bool, bool*) { - return sync_schema_result::already_in_sync; + template + prepared_statement_t> prepare(remove_t statement) { + using object_type = expression_object_type_t; + this->assert_mapped_type(); + return this->prepare_impl(std::move(statement)); } - template - sync_schema_result schema_status(const trigger_t&, sqlite3*, bool, bool*) { - return sync_schema_result::already_in_sync; + template + prepared_statement_t> prepare(insert_t statement) { + using object_type = expression_object_type_t; + this->assert_mapped_type(); + this->assert_insertable_type(); + return this->prepare_impl(std::move(statement)); } - template - sync_schema_result schema_status(const index_t&, sqlite3*, bool, bool*) { - return sync_schema_result::already_in_sync; + template + prepared_statement_t> prepare(replace_t statement) { + using object_type = expression_object_type_t; + this->assert_mapped_type(); + return this->prepare_impl(std::move(statement)); } - template - sync_schema_result schema_status(const table_t& table, - sqlite3* db, - bool preserve, - bool* attempt_to_preserve) { - if(attempt_to_preserve) { - *attempt_to_preserve = true; - } + template = true> + prepared_statement_t prepare(E statement) { + using object_type = expression_object_type_t; + this->assert_mapped_type(); + this->assert_insertable_type(); + return this->prepare_impl(std::move(statement)); + } - auto dbTableInfo = this->pragma.table_xinfo(table.name); - auto res = sync_schema_result::already_in_sync; + template = true> + prepared_statement_t prepare(E statement) { + using object_type = expression_object_type_t; + this->assert_mapped_type(); + return this->prepare_impl(std::move(statement)); + } - // first let's see if table with such name exists.. - auto gottaCreateTable = !this->table_exists(db, table.name); - if(!gottaCreateTable) { + template + prepared_statement_t> prepare(insert_explicit statement) { + using object_type = expression_object_type_t; + this->assert_mapped_type(); + return this->prepare_impl(std::move(statement)); + } - // get table info provided in `make_table` call.. - auto storageTableInfo = table.get_table_info(); + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression, conditional_binder{stmt}); + perform_step(stmt); + } - // this vector will contain pointers to columns that gotta be added.. - std::vector columnsToAdd; +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template = true> + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression, conditional_binder{stmt}); + perform_step(stmt); + } +#endif - if(calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { - gottaCreateTable = true; - } + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression, conditional_binder{stmt}); + perform_step(stmt); + } - if(!gottaCreateTable) { // if all storage columns are equal to actual db columns but there are - // excess columns at the db.. - if(!dbTableInfo.empty()) { - // extra table columns than storage columns - if(!preserve) { -#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) - res = sync_schema_result::old_columns_removed; -#else - gottaCreateTable = true; -#endif - } else { - res = sync_schema_result::old_columns_removed; - } - } - } - if(gottaCreateTable) { - res = sync_schema_result::dropped_and_recreated; - } else { - if(!columnsToAdd.empty()) { - // extra storage columns than table columns - for(const table_xinfo* colInfo: columnsToAdd) { - const basic_generated_always::storage_type* generatedStorageType = - table.find_column_generated_storage_type(colInfo->name); - if(generatedStorageType) { - if(*generatedStorageType == basic_generated_always::storage_type::stored) { - gottaCreateTable = true; - break; - } - // fallback cause VIRTUAL can be added - } else { - if(colInfo->notnull && colInfo->dflt_value.empty()) { - gottaCreateTable = true; - // no matter if preserve is true or false, there is no way to preserve data, so we wont try! - if(attempt_to_preserve) { - *attempt_to_preserve = false; - }; - break; - } - } - } - if(!gottaCreateTable) { - if(res == sync_schema_result::old_columns_removed) { - res = sync_schema_result::new_columns_added_and_old_columns_removed; - } else { - res = sync_schema_result::new_columns_added; - } - } else { - res = sync_schema_result::dropped_and_recreated; - } - } else { - if(res != sync_schema_result::old_columns_removed) { - res = sync_schema_result::already_in_sync; - } - } - } - } else { - res = sync_schema_result::new_table_created; - } - return res; + template + int64 execute(const prepared_statement_t>& statement) { + using object_type = statement_object_type_t; + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + tuple_value_binder{stmt}( + statement.expression.columns.columns, + [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { + return table.object_field_value(object, memberPointer); + }); + perform_step(stmt); + return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } - 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; - context_t context{this->db_objects}; - auto query = serialize(virtualTable, context); - perform_void_exec(db, query); - return res; + template, is_replace_range>::value, bool> = true> + void execute(const prepared_statement_t& statement) { + using object_type = statement_object_type_t; + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + auto processObject = [&table = this->get_table(), + bindValue = field_value_binder{stmt}](auto& object) mutable { + table.template for_each_column_excluding( + call_as_template_base([&bindValue, &object](auto& column) { + bindValue(polyfill::invoke(column.member_pointer, object)); + })); + }; + + static_if::value>( + [&processObject](auto& expression) { +#if __cpp_lib_ranges >= 201911L + std::ranges::for_each(expression.range.first, + expression.range.second, + std::ref(processObject), + std::ref(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); + }); +#endif + }, + [&processObject](auto& expression) { + const object_type& o = get_object(expression); + processObject(o); + })(statement.expression); + + perform_step(stmt); } - 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; - context_t context{this->db_objects}; - auto query = serialize(index, context); - perform_void_exec(db, query); - return res; + template, is_insert_range>::value, bool> = true> + int64 execute(const prepared_statement_t& statement) { + using object_type = statement_object_type_t; + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + auto processObject = [&table = this->get_table(), + bindValue = field_value_binder{stmt}](auto& object) mutable { + using is_without_rowid = typename std::decay_t::is_without_rowid; + table.template for_each_column_excluding< + mpl::conjunction>, + mpl::disjunction_fn>>( + call_as_template_base([&table, &bindValue, &object](auto& column) { + if(!exists_in_composite_primary_key(table, column)) { + bindValue(polyfill::invoke(column.member_pointer, object)); + } + })); + }; + + static_if::value>( + [&processObject](auto& expression) { +#if __cpp_lib_ranges >= 201911L + std::ranges::for_each(expression.range.first, + expression.range.second, + std::ref(processObject), + std::ref(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); + }); +#endif + }, + [&processObject](auto& expression) { + const object_type& o = get_object(expression); + processObject(o); + })(statement.expression); + + perform_step(stmt); + return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } - 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; - context_t context{this->db_objects}; - auto query = serialize(trigger, context); - perform_void_exec(db, query); - return res; + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + perform_step(stmt); } - template = true> - sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); + template + void execute(const prepared_statement_t>& statement) { + using object_type = statement_object_type_t; - template - void add_column(sqlite3* db, const std::string& tableName, const C& column) const { - using context_t = serializer_context; + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + auto& table = this->get_table(); - 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()); + field_value_binder bindValue{stmt}; + auto& object = get_object(statement.expression); + table.template for_each_column_excluding>( + call_as_template_base([&table, &bindValue, &object](auto& column) { + if(!exists_in_composite_primary_key(table, column)) { + bindValue(polyfill::invoke(column.member_pointer, object)); + } + })); + table.for_each_column([&table, &bindValue, &object](auto& column) { + if(column.template is() || exists_in_composite_primary_key(table, column)) { + bindValue(polyfill::invoke(column.member_pointer, object)); + } + }); + perform_step(stmt); } - template - auto execute_select(const S& statement) { + template + std::unique_ptr execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); - iterate_ast(statement.expression, conditional_binder{stmt}); + iterate_ast(statement.expression.ids, conditional_binder{stmt}); - using R = decltype(make_row_extractor(this->db_objects).extract(nullptr, 0)); - std::vector res; - perform_steps( - stmt, - [rowExtractor = make_row_extractor(this->db_objects), &res](sqlite3_stmt* stmt) { - res.push_back(rowExtractor.extract(stmt, 0)); - }); - res.shrink_to_fit(); + std::unique_ptr res; + 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); + }); return res; } - 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() - context.skip_table_name = false; - return serialize(expression, context); - } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + std::optional execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); - 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; + iterate_ast(statement.expression.ids, conditional_binder{stmt}); - 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}; + std::optional res; + perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + object_from_column_builder builder{res.emplace(), stmt}; + table.for_each_column(builder); + }); + return res; } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - public: - /** - * This is a cute function used to replace migration up/down functionality. - * It performs check storage schema with actual db schema and: - * * if there are excess tables exist in db they are ignored (not dropped) - * * every table from storage is compared with it's db analog and - * * if table doesn't exist it is being created - * * if table exists its colums are being compared with table_info from db and - * * if there are columns in db that do not exist in storage (excess) table will be dropped and - * recreated - * * if there are columns in storage that do not exist in db they will be added using `ALTER TABLE - * ... ADD COLUMN ...' command - * * if there is any column existing in both db and storage but differs by any of - * properties/constraints (pk, notnull, dflt_value) table will be dropped and recreated. Be aware that - * `sync_schema` doesn't guarantee that data will not be dropped. It guarantees only that it will make db - * schema the same as you specified in `make_storage` function call. A good point is that if you have no db - * file at all it will be created and all tables also will be created with exact tables and columns you - * specified in `make_storage`, `make_table` and `make_column` calls. The best practice is to call this - * function right after storage creation. - * @param preserve affects function's behaviour in case it is needed to remove a column. If it is `false` - * so table will be dropped if there is column to remove if SQLite version is < 3.35.0 and remove column if SQLite version >= 3.35.0, - * if `true` - table is being copied into another table, dropped and copied table is renamed with source table name. - * Warning: sync_schema doesn't check foreign keys cause it is unable to do so in sqlite3. If you know how to get foreign key info please - * submit an issue https://github.com/fnc12/sqlite_orm/issues - * @return std::map with std::string key equal table name and `sync_schema_result` as value. - * `sync_schema_result` is a enum value that stores table state after syncing a schema. `sync_schema_result` - * can be printed out on std::ostream with `operator<<`. - */ - std::map sync_schema(bool preserve = false) { - auto con = this->get_connection(); - std::map result; - iterate_tuple(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { - sync_schema_result status = this->sync_table(schemaObject, db, preserve); - result.emplace(schemaObject.name, status); + template + T execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + std::optional res; + perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + object_from_column_builder builder{res.emplace(), stmt}; + table.for_each_column(builder); }); - return result; + 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); + switch(stepRes) { + case SQLITE_ROW: { + T res; + object_from_column_builder builder{res, stmt}; + table.for_each_column(builder); + return res; + } break; + case SQLITE_DONE: { + throw std::system_error{orm_error_code::not_found}; + } break; + default: { + throw_translated_sqlite_error(stmt); + } + } +#endif } - /** - * This function returns the same map that `sync_schema` returns but it - * doesn't perform `sync_schema` actually - just simulates it in case you want to know - * what will happen if you sync your schema. - */ - std::map sync_schema_simulate(bool preserve = false) { - auto con = this->get_connection(); - std::map result; - iterate_tuple(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { - sync_schema_result status = this->schema_status(schemaObject, db, preserve, nullptr); - result.emplace(schemaObject.name, status); - }); - return result; + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.conditions, conditional_binder{stmt}); + perform_step(stmt); } - using storage_base::table_exists; // now that it is in storage_base make it into overload set + template + void execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + conditional_binder bindNode{stmt}; + iterate_ast(statement.expression.set, bindNode); + iterate_ast(statement.expression.conditions, bindNode); + perform_step(stmt); + } #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template, is_insert_raw>, bool> = true> - prepared_statement_t> prepare(with_t sel) { - return this->prepare_impl>(std::move(sel)); + template + auto execute(const prepared_statement_t, CTEs...>>& statement) { + using ExprDBOs = decltype(db_objects_for_expression(this->db_objects, statement.expression)); + // note: it is enough to only use the 'expression DBOs' at compile-time to determine the column results; + // because we cannot select objects/structs from a CTE, passing the permanently defined DBOs are enough. + using ColResult = column_result_of_t; + return this->execute_select(statement); } #endif template - prepared_statement_t> prepare(select_t statement) { - statement.highest_level = true; - return this->prepare_impl(std::move(statement)); + auto execute(const prepared_statement_t>& statement) { + using ColResult = column_result_of_t; + return this->execute_select(statement); } - template - prepared_statement_t> prepare(get_all_t statement) { - return this->prepare_impl(std::move(statement)); - } + template> + R execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); - template - prepared_statement_t> prepare(get_all_pointer_t statement) { - return this->prepare_impl(std::move(statement)); - } + iterate_ast(statement.expression, conditional_binder{stmt}); - template - prepared_statement_t> prepare(replace_raw_t statement) { - return this->prepare_impl(std::move(statement)); + R res; + 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); + 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; } - template - prepared_statement_t> prepare(insert_raw_t statement) { - return this->prepare_impl(std::move(statement)); + template + R execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + + R res; + 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); + 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; } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template - prepared_statement_t> - prepare(get_all_optional_t statement) { - return this->prepare_impl(std::move(statement)); + R execute(const prepared_statement_t>& statement) { + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + + R res; + 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); + 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 + }; // struct storage_t + } - template - prepared_statement_t> prepare(update_all_t statement) { - return this->prepare_impl(std::move(statement)); - } + /* + * Factory function for a storage, 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)...}}; + } - template - prepared_statement_t> prepare(remove_all_t statement) { - return this->prepare_impl(std::move(statement)); - } + /** + * sqlite3_threadsafe() interface. + */ + inline int threadsafe() { + return sqlite3_threadsafe(); + } +} +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ +#pragma once - template - prepared_statement_t> prepare(get_t statement) { - return this->prepare_impl(std::move(statement)); - } +// #include "implementations/column_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ - template - prepared_statement_t> prepare(get_pointer_t statement) { - return this->prepare_impl(std::move(statement)); - } +#include // std::make_unique -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - prepared_statement_t> prepare(get_optional_t statement) { - return this->prepare_impl(std::move(statement)); - } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "../functional/static_magic.h" - template - prepared_statement_t> prepare(update_t statement) { - using object_type = expression_object_type_t; - this->assert_mapped_type(); - this->assert_updatable_type(); - return this->prepare_impl(std::move(statement)); - } +// #include "../tuple_helper/tuple_traits.h" - template - prepared_statement_t> prepare(remove_t statement) { - using object_type = expression_object_type_t; - this->assert_mapped_type(); - return this->prepare_impl(std::move(statement)); - } +// #include "../default_value_extractor.h" - template - prepared_statement_t> prepare(insert_t statement) { - using object_type = expression_object_type_t; - this->assert_mapped_type(); - this->assert_insertable_type(); - return this->prepare_impl(std::move(statement)); - } +#include // std::string - template - prepared_statement_t> prepare(replace_t statement) { - using object_type = expression_object_type_t; - this->assert_mapped_type(); - return this->prepare_impl(std::move(statement)); - } +// #include "constraints.h" - template = true> - prepared_statement_t prepare(E statement) { - using object_type = expression_object_type_t; - this->assert_mapped_type(); - this->assert_insertable_type(); - return this->prepare_impl(std::move(statement)); +// #include "serializer_context.h" + +// #include "storage_lookup.h" + +namespace sqlite_orm { + + namespace internal { + + template + auto serialize(const T& t, const C& context); + + /** + * Serialize default value of a column's default valu + */ + template + std::string serialize_default_value(const default_t& dft) { + db_objects_tuple<> dbObjects; + serializer_context> context{dbObjects}; + return serialize(dft.value, context); + } + + } + +} + +// #include "../schema/column.h" + +namespace sqlite_orm { + namespace internal { + + template + std::unique_ptr column_constraints::default_value() const { + 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); + return value; + } + + } +} + +// #include "implementations/table_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ + +#include // std::decay_t +#include // std::move +#include // std::find_if, std::ranges::find + +// #include "../type_printer.h" + +// #include "../schema/column.h" + +// #include "../schema/table.h" + +namespace sqlite_orm { + namespace internal { + + template + std::vector table_t::get_table_info() const { + std::vector res; + res.reserve(filter_tuple_sequence_t::size()); + this->for_each_column([&res](auto& column) { + using field_type = field_type_t>; + std::string dft; + if(auto d = column.default_value()) { + dft = std::move(*d); + } + res.emplace_back(-1, + column.name, + type_printer().print(), + column.is_not_null(), + std::move(dft), + column.template is(), + column.template is()); + }); + auto compositeKeyColumnNames = this->composite_key_columns_names(); + 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); + } } + return res; + } - template = true> - prepared_statement_t prepare(E statement) { - using object_type = expression_object_type_t; - this->assert_mapped_type(); - return this->prepare_impl(std::move(statement)); - } + } +} - template - prepared_statement_t> prepare(insert_explicit statement) { - using object_type = expression_object_type_t; - this->assert_mapped_type(); - return this->prepare_impl(std::move(statement)); - } +// #include "implementations/storage_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * this file is also used to separate implementation details from the main header file, + * e.g. usage of the dbstat table. + */ - template - void execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); - iterate_ast(statement.expression, conditional_binder{stmt}); - perform_step(stmt); - } +#include // std::is_same +#include // std::stringstream +#include // std::flush +#include // std::reference_wrapper, std::cref +#include // std::find_if, std::ranges::find -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template = true> - void execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); - iterate_ast(statement.expression, conditional_binder{stmt}); - perform_step(stmt); - } -#endif +// #include "../type_traits.h" - template - void execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); - iterate_ast(statement.expression, conditional_binder{stmt}); - perform_step(stmt); - } +// #include "../sqlite_schema_table.h" - template - int64 execute(const prepared_statement_t>& statement) { - using object_type = statement_object_type_t; +#include // std::string - sqlite3_stmt* stmt = reset_stmt(statement.stmt); +// #include "schema/column.h" - tuple_value_binder{stmt}( - statement.expression.columns.columns, - [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { - return table.object_field_value(object, memberPointer); - }); - perform_step(stmt); - return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); - } +// #include "schema/table.h" - template, is_replace_range>::value, bool> = true> - void execute(const prepared_statement_t& statement) { - using object_type = statement_object_type_t; +// #include "column_pointer.h" - sqlite3_stmt* stmt = reset_stmt(statement.stmt); +// #include "alias.h" - auto processObject = [&table = this->get_table(), - bindValue = field_value_binder{stmt}](auto& object) mutable { - table.template for_each_column_excluding( - call_as_template_base([&bindValue, &object](auto& column) { - bindValue(polyfill::invoke(column.member_pointer, object)); - })); - }; +namespace sqlite_orm { + /** + * SQLite's "schema table" that stores the schema for a database. + * + * @note Despite the fact that the schema table was renamed from "sqlite_master" to "sqlite_schema" in SQLite 3.33.0 + * the renaming process was more like keeping the previous name "sqlite_master" and attaching an internal alias "sqlite_schema". + * One can infer this fact from the following SQL statement: + * It qualifies the set of columns, but bails out with error "no such table: sqlite_schema": `SELECT sqlite_schema.* from sqlite_schema`. + * Hence we keep its previous table name `sqlite_master`, and provide `sqlite_schema` as a table alias in sqlite_orm. + */ + struct sqlite_master { + std::string type; + std::string name; + std::string tbl_name; + int rootpage = 0; + std::string sql; - static_if::value>( - [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L - std::ranges::for_each(expression.range.first, - expression.range.second, - std::ref(processObject), - std::ref(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); - }); +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator==(const sqlite_master&, const sqlite_master&) = default; #endif - }, - [&processObject](auto& expression) { - const object_type& o = get_object(expression); - processObject(o); - })(statement.expression); - - perform_step(stmt); - } + }; - template, is_insert_range>::value, bool> = true> - int64 execute(const prepared_statement_t& statement) { - using object_type = statement_object_type_t; + inline auto make_sqlite_schema_table() { + return make_table("sqlite_master", + make_column("type", &sqlite_master::type), + make_column("name", &sqlite_master::name), + make_column("tbl_name", &sqlite_master::tbl_name), + make_column("rootpage", &sqlite_master::rootpage), + make_column("sql", &sqlite_master::sql)); + } - sqlite3_stmt* stmt = reset_stmt(statement.stmt); +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + inline constexpr orm_table_reference auto sqlite_master_table = c(); + inline constexpr orm_table_alias auto sqlite_schema = "sqlite_schema"_alias.for_(); +#endif +} - auto processObject = [&table = this->get_table(), - bindValue = field_value_binder{stmt}](auto& object) mutable { - using is_without_rowid = typename std::decay_t::is_without_rowid; - table.template for_each_column_excluding< - mpl::conjunction>, - mpl::disjunction_fn>>( - call_as_template_base([&table, &bindValue, &object](auto& column) { - if(!exists_in_composite_primary_key(table, column)) { - bindValue(polyfill::invoke(column.member_pointer, object)); - } - })); - }; +// #include "../eponymous_vtabs/dbstat.h" - static_if::value>( - [&processObject](auto& expression) { -#if __cpp_lib_ranges >= 201911L - std::ranges::for_each(expression.range.first, - expression.range.second, - std::ref(processObject), - std::ref(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); - }); +#ifdef SQLITE_ENABLE_DBSTAT_VTAB +#include // std::string #endif - }, - [&processObject](auto& expression) { - const object_type& o = get_object(expression); - processObject(o); - })(statement.expression); - - perform_step(stmt); - return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); - } - template - void execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); - iterate_ast(statement.expression.ids, conditional_binder{stmt}); - perform_step(stmt); - } +// #include "../schema/column.h" - template - void execute(const prepared_statement_t>& statement) { - using object_type = statement_object_type_t; +// #include "../schema/table.h" - sqlite3_stmt* stmt = reset_stmt(statement.stmt); - auto& table = this->get_table(); +namespace sqlite_orm { +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + struct dbstat { + std::string name; + std::string path; + int pageno = 0; + std::string pagetype; + int ncell = 0; + int payload = 0; + int unused = 0; + int mx_payload = 0; + int pgoffset = 0; + int pgsize = 0; + }; - field_value_binder bindValue{stmt}; - auto& object = get_object(statement.expression); - table.template for_each_column_excluding>( - call_as_template_base([&table, &bindValue, &object](auto& column) { - if(!exists_in_composite_primary_key(table, column)) { - bindValue(polyfill::invoke(column.member_pointer, object)); - } - })); - table.for_each_column([&table, &bindValue, &object](auto& column) { - if(column.template is() || exists_in_composite_primary_key(table, column)) { - bindValue(polyfill::invoke(column.member_pointer, object)); - } - }); - perform_step(stmt); - } + inline auto make_dbstat_table() { + return make_table("dbstat", + make_column("name", &dbstat::name), + make_column("path", &dbstat::path), + make_column("pageno", &dbstat::pageno), + make_column("pagetype", &dbstat::pagetype), + make_column("ncell", &dbstat::ncell), + make_column("payload", &dbstat::payload), + make_column("unused", &dbstat::unused), + make_column("mx_payload", &dbstat::mx_payload), + make_column("pgoffset", &dbstat::pgoffset), + make_column("pgsize", &dbstat::pgsize)); + } +#endif // SQLITE_ENABLE_DBSTAT_VTAB +} - template - std::unique_ptr execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); +// #include "../type_traits.h" - iterate_ast(statement.expression.ids, conditional_binder{stmt}); +// #include "../util.h" - std::unique_ptr res; - 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); - }); - return res; - } +// #include "../serializing_util.h" -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - std::optional execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); +// #include "../storage.h" - iterate_ast(statement.expression.ids, conditional_binder{stmt}); +namespace sqlite_orm { + namespace internal { - std::optional res; - perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { - object_from_column_builder builder{res.emplace(), stmt}; - table.for_each_column(builder); - }); - return res; + 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; } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + if(std::is_same, dbstat>::value) { + return sync_schema_result::already_in_sync; + } +#endif // SQLITE_ENABLE_DBSTAT_VTAB + auto res = sync_schema_result::already_in_sync; + bool attempt_to_preserve = true; - template - T execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); + auto schema_stat = this->schema_status(table, db, preserve, &attempt_to_preserve); + if(schema_stat != sync_schema_result::already_in_sync) { + if(schema_stat == sync_schema_result::new_table_created) { + this->create_table(db, table.name, table); + res = sync_schema_result::new_table_created; + } else { + if(schema_stat == sync_schema_result::old_columns_removed || + schema_stat == sync_schema_result::new_columns_added || + schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { - iterate_ast(statement.expression.ids, conditional_binder{stmt}); + // get table info provided in `make_table` call.. + auto storageTableInfo = table.get_table_info(); -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - std::optional res; - perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { - object_from_column_builder builder{res.emplace(), stmt}; - table.for_each_column(builder); - }); - 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); - switch(stepRes) { - case SQLITE_ROW: { - T res; - object_from_column_builder builder{res, stmt}; - table.for_each_column(builder); - return res; - } break; - case SQLITE_DONE: { - throw std::system_error{orm_error_code::not_found}; - } break; - default: { - throw_translated_sqlite_error(stmt); - } - } -#endif - } + // now get current table info from db using `PRAGMA table_xinfo` query.. + auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns - template - void execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); - iterate_ast(statement.expression.conditions, conditional_binder{stmt}); - perform_step(stmt); - } + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; - template - void execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); - conditional_binder bindNode{stmt}; - iterate_ast(statement.expression.set, bindNode); - iterate_ast(statement.expression.conditions, bindNode); - perform_step(stmt); - } + this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template - auto execute(const prepared_statement_t, CTEs...>>& statement) { - using ExprDBOs = decltype(db_objects_for_expression(this->db_objects, statement.expression)); - // note: it is enough to only use the 'expression DBOs' at compile-time to determine the column results; - // because we cannot select objects/structs from a CTE, passing the permanently defined DBOs are enough. - using ColResult = column_result_of_t; - return this->execute_select(statement); - } + if(schema_stat == sync_schema_result::old_columns_removed) { +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + for(auto& tableInfo: dbTableInfo) { + this->drop_column(db, table.name, tableInfo.name); + } + res = sync_schema_result::old_columns_removed; +#else + // extra table columns than storage columns + this->backup_table(db, table, {}); + res = sync_schema_result::old_columns_removed; #endif + } - template - auto execute(const prepared_statement_t>& statement) { - using ColResult = column_result_of_t; - return this->execute_select(statement); - } - - template> - R execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); - - iterate_ast(statement.expression, conditional_binder{stmt}); + if(schema_stat == sync_schema_result::new_columns_added) { + for(const table_xinfo* colInfo: columnsToAdd) { + table.for_each_column([this, colInfo, &tableName = table.name, db](auto& column) { + if(column.name != colInfo->name) { + return; + } + this->add_column(db, tableName, column); + }); + } + res = sync_schema_result::new_columns_added; + } - R res; - 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); - 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; - } + if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { - template - R execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); + auto storageTableInfo = table.get_table_info(); + this->add_generated_cols(columnsToAdd, storageTableInfo); - iterate_ast(statement.expression, conditional_binder{stmt}); + // remove extra columns and generated columns + this->backup_table(db, table, columnsToAdd); + res = sync_schema_result::new_columns_added_and_old_columns_removed; + } + } else if(schema_stat == sync_schema_result::dropped_and_recreated) { + // now get current table info from db using `PRAGMA table_xinfo` query.. + auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns + auto storageTableInfo = table.get_table_info(); - R res; - 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); - 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; - } + // this vector will contain pointers to columns that gotta be added.. + std::vector columnsToAdd; -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - R execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); + this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); - iterate_ast(statement.expression, conditional_binder{stmt}); + this->add_generated_cols(columnsToAdd, storageTableInfo); - R res; - 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); - res.push_back(std::move(obj)); - }); -#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED - if constexpr(polyfill::is_specialization_of_v) { - res.shrink_to_fit(); + if(preserve && attempt_to_preserve) { + this->backup_table(db, table, columnsToAdd); + } else { + this->drop_create_with_loss(db, table); + } + res = schema_stat; + } } -#endif - return res; } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - }; // struct storage_t - } + return res; + } - /* - * Factory function for a storage, 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)...}}; - } + template + template + void storage_t::copy_table( + sqlite3* db, + const std::string& sourceTableName, + const std::string& destinationTableName, + const Table& table, + const std::vector& columnsToIgnore) const { // must ignore generated columns + std::vector> columnNames; + 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 + auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); +#else + auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), + columnsToIgnore.end(), + [&columnName](const table_xinfo* tableInfo) { + return columnName == tableInfo->name; + }); +#endif + if(columnToIgnoreIt == columnsToIgnore.end()) { + columnNames.push_back(cref(columnName)); + } + }); - /** - * sqlite3_threadsafe() interface. - */ - inline int threadsafe() { - return sqlite3_threadsafe(); + 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()); + } } } + #pragma once #include // std::is_same, std::remove_reference, std::remove_cvref #include // std::get -// #include "functional/cxx_universal.h" -// ::size_t // #include "functional/cxx_type_traits_polyfill.h" // #include "functional/static_magic.h" @@ -23863,765 +24118,421 @@ namespace sqlite_orm { struct node_tuple, void> : node_tuple_for {}; template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple_for {}; - -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct node_tuple, void> : node_tuple_for {}; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - - template - struct node_tuple, Wargs...>, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple {}; - - // note: not strictly necessary as there's no binding support for USING; - // we provide it nevertheless, in line with on_t. - template - struct node_tuple, void> : node_tuple> {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple {}; - - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple_for {}; - } -} - -// #include "expression_object_type.h" - -namespace sqlite_orm { - - template - auto& get(internal::prepared_statement_t>& statement) { - return std::get(statement.expression.range); - } - - template - const auto& get(const internal::prepared_statement_t>& statement) { - return std::get(statement.expression.range); - } - - template - auto& get(internal::prepared_statement_t>& statement) { - return std::get(statement.expression.range); - } - - template - const auto& get(const internal::prepared_statement_t>& statement) { - return std::get(statement.expression.range); - } - - template - auto& get(internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.expression.ids)); - } - - template - const auto& get(const internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.expression.ids)); - } - - template - auto& get(internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.expression.ids)); - } - - template - const auto& get(const internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.expression.ids)); - } - -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - auto& get(internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.expression.ids)); - } - - template - const auto& get(const internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.expression.ids)); - } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - - template - auto& get(internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.expression.ids)); - } - - template - const auto& get(const internal::prepared_statement_t>& statement) { - return internal::get_ref(std::get(statement.expression.ids)); - } - - template - 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); - } - - template - const auto& get(const 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); - } - - template - 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); - } - - template - const auto& get(const 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); - } - - template - 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); - } - - template - const auto& get(const 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); - } - - template - 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); - } - - template - const auto& get(const 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); - } - - template - const auto& get(const internal::prepared_statement_t& statement) { - using statement_type = polyfill::remove_cvref_t; - using expression_type = internal::expression_type_t; - using node_tuple = internal::node_tuple_t; - using bind_tuple = internal::bindable_filter_t; - using result_type = std::tuple_element_t(N), bind_tuple>; - 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) { - ++index; - } - if(index == N) { - internal::call_if_constexpr::value>( - [](auto& r, auto& n) { - r = &n; - }, - result, - node); - } - }); - return internal::get_ref(*result); - } + struct node_tuple, void> : node_tuple {}; - template - 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; - using bind_tuple = internal::bindable_filter_t; - using result_type = std::tuple_element_t(N), bind_tuple>; - result_type* result = nullptr; + template + struct node_tuple, void> : node_tuple_for {}; - internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { - using node_type = polyfill::remove_cvref_t; - if(internal::is_bindable::value) { - ++index; - } - if(index == N) { - internal::call_if_constexpr::value>( - [](auto& r, auto& n) { - r = const_cast>(&n); - }, - result, - node); - } - }); - return internal::get_ref(*result); - } -} -#pragma once + template + struct node_tuple, void> : node_tuple_for {}; -/* - * Note: This feature needs constexpr variables with external linkage. - * which can be achieved before C++17's inline variables, but differs from compiler to compiler. - * Hence we make it only available for compilers supporting inline variables. - */ + template + struct node_tuple, void> : node_tuple_for {}; -#if SQLITE_VERSION_NUMBER >= 3020000 -#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED -#include // std::move -#ifndef SQLITE_ORM_WITH_CPP20_ALIASES -#include // std::integral_constant -#endif -#endif -#endif +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct node_tuple, void> : node_tuple_for {}; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED -// #include "pointer_value.h" + template + struct node_tuple, Wargs...>, void> : node_tuple_for {}; -#if SQLITE_VERSION_NUMBER >= 3020000 -#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED -namespace sqlite_orm { + template + struct node_tuple, void> : node_tuple_for {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - inline constexpr orm_pointer_type auto carray_pointer_tag = "carray"_pointer_type; - // [Deprecation notice] This type is deprecated and will be removed in v1.10. Use the inline variable `carray_pointer_tag` instead. - using carray_pvt [[deprecated]] = decltype("carray"_pointer_type); + template + struct node_tuple, void> : node_tuple {}; - template - using carray_pointer_arg = pointer_arg_t; - template - using carray_pointer_binding = pointer_binding_t; - template - using static_carray_pointer_binding = static_pointer_binding_t; + template + struct node_tuple, void> : node_tuple {}; - /** - * Wrap a pointer of type 'carray' 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 - carray_pointer_binding bind_carray_pointer(P* p, D d) noexcept { - return bind_pointer(p, std::move(d)); - } + template + struct node_tuple, void> : node_tuple {}; - template - static_carray_pointer_binding

bind_carray_pointer_statically(P* p) noexcept { - return bind_pointer_statically(p); - } + template + struct node_tuple, void> : node_tuple_for {}; - /** - * Wrap a pointer of type 'carray' 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 - [[deprecated("Use the better named function `bind_carray_pointer(...)`")]] carray_pointer_binding - bindable_carray_pointer(P* p, D d) noexcept { - return bind_pointer(p, std::move(d)); - } + template + struct node_tuple, void> : node_tuple_for {}; - template - [[deprecated( - "Use the better named function `bind_carray_pointer_statically(...)` ")]] static_carray_pointer_binding

- statically_bindable_carray_pointer(P* p) noexcept { - return bind_pointer_statically(p); - } -#else - inline constexpr const char carray_pointer_name[] = "carray"; - using carray_pointer_type = std::integral_constant; - // [Deprecation notice] This type is deprecated and will be removed in v1.10. Use the alias type `carray_pointer_type` instead. - using carray_pvt [[deprecated]] = carray_pointer_type; + template + struct node_tuple, void> : node_tuple_for {}; - template - using carray_pointer_arg = pointer_arg; - template - using carray_pointer_binding = pointer_binding; - template - using static_carray_pointer_binding = static_pointer_binding; + template + struct node_tuple, void> : node_tuple {}; - /** - * Wrap a pointer of type 'carray' 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 - carray_pointer_binding bind_carray_pointer(P* p, D d) noexcept { - return bind_pointer(p, std::move(d)); - } + template + struct node_tuple, void> : node_tuple {}; - /** - * Wrap a pointer of type 'carray' 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 - static_carray_pointer_binding

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

- statically_bindable_carray_pointer(P* p) noexcept { - return bind_carray_pointer_statically(p); - } -#endif + template + struct node_tuple, void> : node_tuple_for {}; - /** - * Base for a generalized form of the 'remember' SQL function that is a pass-through for values - * (it returns its argument unchanged using move semantics) but also saves the - * value that is passed through into a bound variable. - */ - template - struct note_value_fn { - P operator()(P&& value, carray_pointer_arg

pv) const { - if(P* observer = pv) { - *observer = value; - } - return std::move(value); - } - }; + template + struct node_tuple, void> : node_tuple_for {}; - /** - * remember(V, $PTR) extension function https://sqlite.org/src/file/ext/misc/remember.c - */ - struct remember_fn : note_value_fn { - static constexpr const char* name() { - return "remember"; - } - }; -} -#endif -#endif -#pragma once + template + struct node_tuple, void> : node_tuple_for {}; -#include // std::string + template + struct node_tuple, void> : node_tuple_for {}; -// #include "schema/column.h" + template + struct node_tuple, void> : node_tuple {}; -// #include "schema/table.h" + template + struct node_tuple, void> : node_tuple {}; -// #include "column_pointer.h" + // note: not strictly necessary as there's no binding support for USING; + // we provide it nevertheless, in line with on_t. + template + struct node_tuple, void> : node_tuple> {}; -// #include "alias.h" + template + struct node_tuple, void> : node_tuple {}; -namespace sqlite_orm { - /** - * SQLite's "schema table" that stores the schema for a database. - * - * @note Despite the fact that the schema table was renamed from "sqlite_master" to "sqlite_schema" in SQLite 3.33.0 - * the renaming process was more like keeping the previous name "sqlite_master" and attaching an internal alias "sqlite_schema". - * One can infer this fact from the following SQL statement: - * It qualifies the set of columns, but bails out with error "no such table: sqlite_schema": `SELECT sqlite_schema.* from sqlite_schema`. - * Hence we keep its previous table name `sqlite_master`, and provide `sqlite_schema` as a table alias in sqlite_orm. - */ - struct sqlite_master { - std::string type; - std::string name; - std::string tbl_name; - int rootpage = 0; - std::string sql; + template + struct node_tuple, void> : node_tuple {}; -#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED - friend bool operator==(const sqlite_master&, const sqlite_master&) = default; -#endif - }; + template + struct node_tuple, void> : node_tuple {}; - inline auto make_sqlite_schema_table() { - return make_table("sqlite_master", - make_column("type", &sqlite_master::type), - make_column("name", &sqlite_master::name), - make_column("tbl_name", &sqlite_master::tbl_name), - make_column("rootpage", &sqlite_master::rootpage), - make_column("sql", &sqlite_master::sql)); - } + template + struct node_tuple, void> : node_tuple_for {}; + + template + struct node_tuple, void> : node_tuple_for {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - inline constexpr orm_table_reference auto sqlite_master_table = c(); - inline constexpr orm_table_alias auto sqlite_schema = "sqlite_schema"_alias.for_(); -#endif + template + struct node_tuple, void> : node_tuple_for {}; + + template + struct node_tuple, void> : node_tuple_for {}; + } } -#pragma once -#ifdef SQLITE_ENABLE_DBSTAT_VTAB -#include // std::string -#endif +// #include "expression_object_type.h" -// #include "../schema/column.h" +namespace sqlite_orm { -// #include "../schema/table.h" + template + auto& get(internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); + } -namespace sqlite_orm { -#ifdef SQLITE_ENABLE_DBSTAT_VTAB - struct dbstat { - std::string name; - std::string path; - int pageno = 0; - std::string pagetype; - int ncell = 0; - int payload = 0; - int unused = 0; - int mx_payload = 0; - int pgoffset = 0; - int pgsize = 0; - }; + template + const auto& get(const internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); + } - inline auto make_dbstat_table() { - return make_table("dbstat", - make_column("name", &dbstat::name), - make_column("path", &dbstat::path), - make_column("pageno", &dbstat::pageno), - make_column("pagetype", &dbstat::pagetype), - make_column("ncell", &dbstat::ncell), - make_column("payload", &dbstat::payload), - make_column("unused", &dbstat::unused), - make_column("mx_payload", &dbstat::mx_payload), - make_column("pgoffset", &dbstat::pgoffset), - make_column("pgsize", &dbstat::pgsize)); + template + auto& get(internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); } -#endif // SQLITE_ENABLE_DBSTAT_VTAB -} -/** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) - * this file is also used to provide definitions of interface methods 'hitting the database'. - */ -#pragma once -// #include "implementations/column_definitions.h" -/** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) - * this file is also used to provide definitions of interface methods 'hitting the database'. - */ + template + const auto& get(const internal::prepared_statement_t>& statement) { + return std::get(statement.expression.range); + } -#include // std::make_unique + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } -// #include "../functional/static_magic.h" + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } -// #include "../tuple_helper/tuple_traits.h" + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } -// #include "../default_value_extractor.h" + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } -// #include "../schema/column.h" +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } -namespace sqlite_orm { - namespace internal { + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - std::unique_ptr column_constraints::default_value() const { - static constexpr size_t default_op_index = find_tuple_template::value; + template + auto& get(internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } - std::unique_ptr value; - call_if_constexpr::value>( - [&value](auto& constraints) { - value = - std::make_unique(serialize_default_value(std::get(constraints))); - }, - this->constraints); - return value; - } + template + const auto& get(const internal::prepared_statement_t>& statement) { + return internal::get_ref(std::get(statement.expression.ids)); + } + + template + 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); + } + template + const auto& get(const 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); } -} -// #include "implementations/table_definitions.h" -/** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) - * this file is also used to provide definitions of interface methods 'hitting the database'. - */ + template + 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); + } -#include // std::decay_t -#include // std::move -#include // std::find_if, std::ranges::find + template + const auto& get(const 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); + } -// #include "../functional/cxx_universal.h" -// ::size_t -// #include "../type_printer.h" + template + 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); + } -// #include "../schema/column.h" + template + const auto& get(const 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); + } -// #include "../schema/table.h" + template + 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); + } -namespace sqlite_orm { - namespace internal { + template + const auto& get(const 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); + } - template - std::vector table_t::get_table_info() const { - std::vector res; - res.reserve(filter_tuple_sequence_t::size()); - this->for_each_column([&res](auto& column) { - using field_type = field_type_t>; - std::string dft; - if(auto d = column.default_value()) { - dft = std::move(*d); - } - res.emplace_back(-1, - column.name, - type_printer().print(), - column.is_not_null(), - std::move(dft), - column.template is(), - column.template is()); - }); - auto compositeKeyColumnNames = this->composite_key_columns_names(); - 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); - } + template + const auto& get(const internal::prepared_statement_t& statement) { + using statement_type = polyfill::remove_cvref_t; + using expression_type = internal::expression_type_t; + using node_tuple = internal::node_tuple_t; + using bind_tuple = internal::bindable_filter_t; + using result_type = std::tuple_element_t(N), bind_tuple>; + 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) { + ++index; } - return res; - } + if(index == N) { + internal::call_if_constexpr::value>( + [](auto& r, auto& n) { + r = &n; + }, + result, + node); + } + }); + return internal::get_ref(*result); + } + + template + 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; + using bind_tuple = internal::bindable_filter_t; + using result_type = std::tuple_element_t(N), bind_tuple>; + 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) { + ++index; + } + if(index == N) { + internal::call_if_constexpr::value>( + [](auto& r, auto& n) { + r = const_cast>(&n); + }, + result, + node); + } + }); + return internal::get_ref(*result); } } +#pragma once -// #include "implementations/storage_definitions.h" -/** @file Mainly existing to disentangle implementation details from circular and cross dependencies - * this file is also used to separate implementation details from the main header file, - * e.g. usage of the dbstat table. +/* + * Note: This feature needs constexpr variables with external linkage. + * which can be achieved before C++17's inline variables, but differs from compiler to compiler. + * Hence we make it only available for compilers supporting inline variables. */ -#include // std::is_same -#include -#include // std::reference_wrapper, std::cref -#include // std::find_if, std::ranges::find - -// #include "../sqlite_schema_table.h" - -// #include "../eponymous_vtabs/dbstat.h" - -// #include "../type_traits.h" - -// #include "../util.h" - -// #include "../serializing_util.h" +#if SQLITE_VERSION_NUMBER >= 3020000 +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#include // std::move +#ifndef SQLITE_ORM_WITH_CPP20_ALIASES +#include // std::integral_constant +#endif +#endif +#endif -// #include "../storage.h" +// #include "pointer_value.h" +#if SQLITE_VERSION_NUMBER >= 3020000 +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED 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; - } -#ifdef SQLITE_ENABLE_DBSTAT_VTAB - if(std::is_same, dbstat>::value) { - return sync_schema_result::already_in_sync; - } -#endif // SQLITE_ENABLE_DBSTAT_VTAB - auto res = sync_schema_result::already_in_sync; - bool attempt_to_preserve = true; - auto schema_stat = this->schema_status(table, db, preserve, &attempt_to_preserve); - if(schema_stat != sync_schema_result::already_in_sync) { - if(schema_stat == sync_schema_result::new_table_created) { - this->create_table(db, table.name, table); - res = sync_schema_result::new_table_created; - } else { - if(schema_stat == sync_schema_result::old_columns_removed || - schema_stat == sync_schema_result::new_columns_added || - schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + inline constexpr orm_pointer_type auto carray_pointer_tag = "carray"_pointer_type; + // [Deprecation notice] This type is deprecated and will be removed in v1.10. Use the inline variable `carray_pointer_tag` instead. + using carray_pvt [[deprecated]] = decltype("carray"_pointer_type); - // get table info provided in `make_table` call.. - auto storageTableInfo = table.get_table_info(); + template + using carray_pointer_arg = pointer_arg_t; + template + using carray_pointer_binding = pointer_binding_t; + template + using static_carray_pointer_binding = static_pointer_binding_t; - // now get current table info from db using `PRAGMA table_xinfo` query.. - auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns + /** + * Wrap a pointer of type 'carray' 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 + carray_pointer_binding bind_carray_pointer(P* p, D d) noexcept { + return bind_pointer(p, std::move(d)); + } - // this vector will contain pointers to columns that gotta be added.. - std::vector columnsToAdd; + template + static_carray_pointer_binding

bind_carray_pointer_statically(P* p) noexcept { + return bind_pointer_statically(p); + } - this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + /** + * Wrap a pointer of type 'carray' 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 + [[deprecated("Use the better named function `bind_carray_pointer(...)`")]] carray_pointer_binding + bindable_carray_pointer(P* p, D d) noexcept { + return bind_pointer(p, std::move(d)); + } - if(schema_stat == sync_schema_result::old_columns_removed) { -#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) - for(auto& tableInfo: dbTableInfo) { - this->drop_column(db, table.name, tableInfo.name); - } - res = sync_schema_result::old_columns_removed; + template + [[deprecated( + "Use the better named function `bind_carray_pointer_statically(...)` ")]] static_carray_pointer_binding

+ statically_bindable_carray_pointer(P* p) noexcept { + return bind_pointer_statically(p); + } #else - // extra table columns than storage columns - this->backup_table(db, table, {}); - res = sync_schema_result::old_columns_removed; -#endif - } - - if(schema_stat == sync_schema_result::new_columns_added) { - for(const table_xinfo* colInfo: columnsToAdd) { - table.for_each_column([this, colInfo, &tableName = table.name, db](auto& column) { - if(column.name != colInfo->name) { - return; - } - this->add_column(db, tableName, column); - }); - } - res = sync_schema_result::new_columns_added; - } - - if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + inline constexpr const char carray_pointer_name[] = "carray"; + using carray_pointer_type = std::integral_constant; + // [Deprecation notice] This type is deprecated and will be removed in v1.10. Use the alias type `carray_pointer_type` instead. + using carray_pvt [[deprecated]] = carray_pointer_type; - auto storageTableInfo = table.get_table_info(); - this->add_generated_cols(columnsToAdd, storageTableInfo); + template + using carray_pointer_arg = pointer_arg; + template + using carray_pointer_binding = pointer_binding; + template + using static_carray_pointer_binding = static_pointer_binding; - // remove extra columns and generated columns - this->backup_table(db, table, columnsToAdd); - res = sync_schema_result::new_columns_added_and_old_columns_removed; - } - } else if(schema_stat == sync_schema_result::dropped_and_recreated) { - // now get current table info from db using `PRAGMA table_xinfo` query.. - auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns - auto storageTableInfo = table.get_table_info(); + /** + * Wrap a pointer of type 'carray' 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 + carray_pointer_binding bind_carray_pointer(P* p, D d) noexcept { + return bind_pointer(p, std::move(d)); + } - // this vector will contain pointers to columns that gotta be added.. - std::vector columnsToAdd; + /** + * Wrap a pointer of type 'carray' 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 + static_carray_pointer_binding

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

+ statically_bindable_carray_pointer(P* p) noexcept { + return bind_carray_pointer_statically(p); + } +#endif - if(preserve && attempt_to_preserve) { - this->backup_table(db, table, columnsToAdd); - } else { - this->drop_create_with_loss(db, table); - } - res = schema_stat; - } - } + /** + * Base for a generalized form of the 'remember' SQL function that is a pass-through for values + * (it returns its argument unchanged using move semantics) but also saves the + * value that is passed through into a bound variable. + */ + template + struct note_value_fn { + P operator()(P&& value, carray_pointer_arg

pv) const { + if(P* observer = pv) { + *observer = value; } - return res; + return std::move(value); } + }; - template - template - void storage_t::copy_table( - sqlite3* db, - const std::string& sourceTableName, - const std::string& destinationTableName, - const Table& table, - const std::vector& columnsToIgnore) const { // must ignore generated columns - std::vector> columnNames; - 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 - auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); -#else - auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), - columnsToIgnore.end(), - [&columnName](const table_xinfo* tableInfo) { - return columnName == tableInfo->name; - }); -#endif - if(columnToIgnoreIt == columnsToIgnore.end()) { - columnNames.push_back(cref(columnName)); - } - }); - - 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()); + /** + * remember(V, $PTR) extension function https://sqlite.org/src/file/ext/misc/remember.c + */ + struct remember_fn : note_value_fn { + static constexpr const char* name() { + return "remember"; } - } + }; } - +#endif +#endif #pragma once #if defined(_MSC_VER) diff --git a/not_single_header_include/sqlite_orm/sqlite_orm.h b/not_single_header_include/sqlite_orm/sqlite_orm.h index 55595b291..b217801ed 100644 --- a/not_single_header_include/sqlite_orm/sqlite_orm.h +++ b/not_single_header_include/sqlite_orm/sqlite_orm.h @@ -5,30 +5,8 @@ // though each header is required to include everything it needs // we include the configuration and all underlying c++ core features in order to make it universally available #include "../../dev/functional/config.h" -#include "../../dev/type_traits.h" -#include "../../dev/collate_argument.h" -#include "../../dev/constraints.h" -#include "../../dev/type_is_nullable.h" -#include "../../dev/operators.h" -#include "../../dev/schema/column.h" -#include "../../dev/conditions.h" -#include "../../dev/alias.h" -#include "../../dev/core_functions.h" -#include "../../dev/select_constraints.h" -#include "../../dev/table_info.h" -#include "../../dev/schema/triggers.h" -#include "../../dev/statement_binder.h" -#include "../../dev/row_extractor.h" -#include "../../dev/sync_schema_result.h" -#include "../../dev/schema/index.h" -#include "../../dev/rowid.h" -#include "../../dev/schema/table.h" -#include "../../dev/storage_impl.h" -#include "../../dev/default_value_extractor.h" #include "../../dev/storage.h" +#include "../../dev/interface_definitions.h" #include "../../dev/get_prepared_statement.h" #include "../../dev/carray.h" -#include "../../dev/sqlite_schema_table.h" -#include "../../dev/eponymous_vtabs/dbstat.h" -#include "../../dev/interface_definitions.h" #include "../../dev/functional/finish_macros.h" diff --git a/tests/row_extractor.cpp b/tests/row_extractor.cpp index 141541da2..f0b989876 100644 --- a/tests/row_extractor.cpp +++ b/tests/row_extractor.cpp @@ -1,3 +1,4 @@ +#include #include #include diff --git a/tests/static_tests/row_extractor.cpp b/tests/static_tests/row_extractor.cpp index a7fc39fd2..5279c21b7 100644 --- a/tests/static_tests/row_extractor.cpp +++ b/tests/static_tests/row_extractor.cpp @@ -1,3 +1,4 @@ +#include #include #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED #define ENABLE_THIS_UT diff --git a/tests/storage_non_crud_tests.cpp b/tests/storage_non_crud_tests.cpp index f4167dce2..a4cfe0b47 100644 --- a/tests/storage_non_crud_tests.cpp +++ b/tests/storage_non_crud_tests.cpp @@ -1,3 +1,4 @@ +#include #include #include diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index e084b57d0..e9e2244a9 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -490,3 +491,112 @@ TEST_CASE("insert with generated column") { REQUIRE(allProducts == expectedProducts); } #endif + +TEST_CASE("last insert rowid") { + struct Object { + int id; + }; + + auto storage = make_storage("", make_table("objects", make_column("id", &Object::id, primary_key()))); + + storage.sync_schema(); + + SECTION("ordinary insert") { + int id = storage.insert({0}); + REQUIRE(id == storage.last_insert_rowid()); + REQUIRE_NOTHROW(storage.get(id)); + } + SECTION("explicit insert") { + int id = storage.insert({2}, columns(&Object::id)); + REQUIRE(id == 2); + REQUIRE(id == storage.last_insert_rowid()); + } + SECTION("range, prepared") { + std::vector rng{{0}}; + auto stmt = storage.prepare(insert_range(rng.begin(), rng.end())); + int64 id = storage.execute(stmt); + REQUIRE(id == storage.last_insert_rowid()); + REQUIRE_NOTHROW(storage.get(id)); + } +} + +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +TEST_CASE("With clause") { + using Catch::Matchers::Equals; + + SECTION("select") { + using cnt = decltype(1_ctealias); + auto storage = make_storage(""); + SECTION("with ordinary") { + auto rows = storage.with(cte().as(select(1)), select(column(1_colalias))); + REQUIRE_THAT(rows, Equals(std::vector{1})); + } + SECTION("with ordinary, compound") { + auto rows = storage.with(cte().as(select(1)), + union_all(select(column(1_colalias)), select(column(1_colalias)))); + REQUIRE_THAT(rows, Equals(std::vector{1, 1})); + } + SECTION("with not enforced recursive") { + auto rows = storage.with_recursive(cte().as(select(1)), select(column(1_colalias))); + REQUIRE_THAT(rows, Equals(std::vector{1})); + } + SECTION("with not enforced recursive, compound") { + auto rows = + storage.with_recursive(cte().as(select(1)), + union_all(select(column(1_colalias)), select(column(1_colalias)))); + REQUIRE_THAT(rows, Equals(std::vector{1, 1})); + } + SECTION("with ordinary, multiple") { + auto rows = storage.with(std::make_tuple(cte().as(select(1))), select(column(1_colalias))); + REQUIRE_THAT(rows, Equals(std::vector{1})); + } + SECTION("with ordinary, multiple, compound") { + auto rows = storage.with(std::make_tuple(cte().as(select(1))), + union_all(select(column(1_colalias)), select(column(1_colalias)))); + REQUIRE_THAT(rows, Equals(std::vector{1, 1})); + } + SECTION("with not enforced recursive, multiple") { + auto rows = + storage.with_recursive(std::make_tuple(cte().as(select(1))), select(column(1_colalias))); + REQUIRE_THAT(rows, Equals(std::vector{1})); + } + SECTION("with not enforced recursive, multiple, compound") { + auto rows = + storage.with_recursive(std::make_tuple(cte().as(select(1))), + union_all(select(column(1_colalias)), select(column(1_colalias)))); + REQUIRE_THAT(rows, Equals(std::vector{1, 1})); + } + SECTION("with optional recursive") { + auto rows = storage.with( + cte().as( + union_all(select(1), select(column(1_colalias) + 1, where(column(1_colalias) < 2)))), + select(column(1_colalias))); + REQUIRE_THAT(rows, Equals(std::vector{1, 2})); + } + SECTION("with recursive") { + auto rows = storage.with_recursive( + cte().as( + union_all(select(1), select(column(1_colalias) + 1, where(column(1_colalias) < 2)))), + select(column(1_colalias))); + REQUIRE_THAT(rows, Equals(std::vector{1, 2})); + } + } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("insert") { + struct Object { + int id; + }; + + auto storage = make_storage("", make_table("objects", make_column("id", &Object::id, primary_key()))); + + storage.sync_schema(); + + constexpr auto data = "data"_cte; + storage.with(cte().as(select(2)), + insert(into(), columns(&Object::id), select(data->*1_colalias))); + REQUIRE(2 == storage.last_insert_rowid()); + } +#endif +} +#endif diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index eb245a520..65d48945c 100644 --- a/tests/sync_schema_tests.cpp +++ b/tests/sync_schema_tests.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -138,7 +139,7 @@ TEST_CASE("issue521") { struct MockDatabasePoco { int id{-1}; std::string name; - uint32_t alpha{0}; + std::uint32_t alpha{0}; float beta{0.0}; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED diff --git a/tests/tests3.cpp b/tests/tests3.cpp index 366ccc238..234a0eff9 100644 --- a/tests/tests3.cpp +++ b/tests/tests3.cpp @@ -385,62 +385,3 @@ TEST_CASE("Escape chars") { storage.update(selena); storage.remove(10); } - -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -TEST_CASE("With select") { - using Catch::Matchers::Equals; - - using cnt = decltype(1_ctealias); - auto storage = make_storage(""); - SECTION("with ordinary") { - auto rows = storage.with(cte().as(select(1)), select(column(1_colalias))); - REQUIRE_THAT(rows, Equals(std::vector{1})); - } - SECTION("with ordinary, compound") { - auto rows = storage.with(cte().as(select(1)), - union_all(select(column(1_colalias)), select(column(1_colalias)))); - REQUIRE_THAT(rows, Equals(std::vector{1, 1})); - } - SECTION("with not enforced recursive") { - auto rows = storage.with_recursive(cte().as(select(1)), select(column(1_colalias))); - REQUIRE_THAT(rows, Equals(std::vector{1})); - } - SECTION("with not enforced recursive, compound") { - auto rows = storage.with_recursive(cte().as(select(1)), - union_all(select(column(1_colalias)), select(column(1_colalias)))); - REQUIRE_THAT(rows, Equals(std::vector{1, 1})); - } - SECTION("with ordinary, multiple") { - auto rows = storage.with(std::make_tuple(cte().as(select(1))), select(column(1_colalias))); - REQUIRE_THAT(rows, Equals(std::vector{1})); - } - SECTION("with ordinary, multiple, compound") { - auto rows = storage.with(std::make_tuple(cte().as(select(1))), - union_all(select(column(1_colalias)), select(column(1_colalias)))); - REQUIRE_THAT(rows, Equals(std::vector{1, 1})); - } - SECTION("with not enforced recursive, multiple") { - auto rows = storage.with_recursive(std::make_tuple(cte().as(select(1))), select(column(1_colalias))); - REQUIRE_THAT(rows, Equals(std::vector{1})); - } - SECTION("with not enforced recursive, multiple, compound") { - auto rows = storage.with_recursive(std::make_tuple(cte().as(select(1))), - union_all(select(column(1_colalias)), select(column(1_colalias)))); - REQUIRE_THAT(rows, Equals(std::vector{1, 1})); - } - SECTION("with optional recursive") { - auto rows = storage.with( - cte().as( - union_all(select(1), select(column(1_colalias) + 1, where(column(1_colalias) < 2)))), - select(column(1_colalias))); - REQUIRE_THAT(rows, Equals(std::vector{1, 2})); - } - SECTION("with recursive") { - auto rows = storage.with_recursive( - cte().as( - union_all(select(1), select(column(1_colalias) + 1, where(column(1_colalias) < 2)))), - select(column(1_colalias))); - REQUIRE_THAT(rows, Equals(std::vector{1, 2})); - } -} -#endif diff --git a/third_party/amalgamate/config.json b/third_party/amalgamate/config.json index cdea2f87a..ccf329ef8 100755 --- a/third_party/amalgamate/config.json +++ b/third_party/amalgamate/config.json @@ -5,32 +5,10 @@ "dev/functional/start_macros.h", "dev/functional/sqlite3_config.h", "dev/functional/config.h", - "dev/type_traits.h", - "dev/collate_argument.h", - "dev/constraints.h", - "dev/type_is_nullable.h", - "dev/operators.h", - "dev/schema/column.h", - "dev/conditions.h", - "dev/alias.h", - "dev/core_functions.h", - "dev/select_constraints.h", - "dev/table_info.h", - "dev/schema/triggers.h", - "dev/statement_binder.h", - "dev/row_extractor.h", - "dev/sync_schema_result.h", - "dev/schema/index.h", - "dev/rowid.h", - "dev/schema/table.h", - "dev/storage_impl.h", - "dev/default_value_extractor.h", "dev/storage.h", + "dev/interface_definitions.h", "dev/get_prepared_statement.h", "dev/carray.h", - "dev/sqlite_schema_table.h", - "dev/eponymous_vtabs/dbstat.h", - "dev/interface_definitions.h", "dev/functional/finish_macros.h" ], "include_paths": [ "dev" ]