From 8fadf9e4da2bc3d1494c033f26d0325c07abbaa5 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sat, 13 Jul 2024 16:24:09 +0500 Subject: [PATCH 01/38] added if_exists with tests --- dev/storage.h | 4 +- dev/storage_base.h | 90 ++++++++++++++++++++++++++----- include/sqlite_orm/sqlite_orm.h | 94 +++++++++++++++++++++++++++------ tests/storage_tests.cpp | 39 ++++++++++++++ 4 files changed, 197 insertions(+), 30 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index a7bf160d..27f3f7c0 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -149,7 +149,7 @@ namespace sqlite_orm { template void drop_create_with_loss(sqlite3* db, const Table& table) { // eliminated all transaction handling - this->drop_table_internal(db, table.name); + this->drop_table_internal(db, table.name, false); this->create_table(db, table.name, table); } @@ -176,7 +176,7 @@ namespace sqlite_orm { this->copy_table(db, table.name, backupTableName, table, columnsToIgnore); - this->drop_table_internal(db, table.name); + this->drop_table_internal(db, table.name, false); this->rename_table(db, backupTableName, table.name); } diff --git a/dev/storage_base.h b/dev/storage_base.h index ddad5559..4f76208a 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -70,27 +70,66 @@ namespace sqlite_orm { std::bind(&storage_base::rollback, this)}; } + /** + * Drops index with given name. + * Calls `DROP INDEX indexName`. + * More info: https://www.sqlite.org/lang_dropindex.html + */ void drop_index(const std::string& indexName) { - std::stringstream ss; - ss << "DROP INDEX " << quote_identifier(indexName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + this->drop_index_internal(indexName, false); } + /** + * Drops trigger with given name if trigger exists. + * Calls `DROP INDEX IF EXISTS indexName`. + * More info: https://www.sqlite.org/lang_dropindex.html + */ + void drop_index_if_exists(const std::string& indexName) { + this->drop_index_internal(indexName, true); + } + + /** + * Drops trigger with given name. + * Calls `DROP TRIGGER triggerName`. + * More info: https://www.sqlite.org/lang_droptrigger.html + */ void drop_trigger(const std::string& triggerName) { - std::stringstream ss; - ss << "DROP TRIGGER " << quote_identifier(triggerName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + this->drop_trigger_internal(triggerName, false); + } + + /** + * Drops trigger with given name if trigger exists. + * Calls `DROP TRIGGER IF EXISTS triggerName`. + * More info: https://www.sqlite.org/lang_droptrigger.html + */ + void drop_trigger_if_exists(const std::string& triggerName) { + this->drop_trigger_internal(triggerName, true); } + /** + * `VACUUM` query. + * More info: https://www.sqlite.org/lang_vacuum.html + */ void vacuum() { perform_void_exec(this->get_connection().get(), "VACUUM"); } /** - * Drops table with given name. + * Drops table with given name. + * Calls `DROP TABLE tableName`. + * More info: https://www.sqlite.org/lang_droptable.html */ void drop_table(const std::string& tableName) { - this->drop_table_internal(this->get_connection().get(), tableName); + this->drop_table_internal(this->get_connection().get(), tableName, false); + } + + /** + * Drops table with given name if table exists. + * Calls `DROP TABLE IF EXISTS tableName`. + * More info: https://www.sqlite.org/lang_droptable.html + */ + void drop_table_if_exists(const std::string& tableName) { + this->drop_table_internal(this->get_connection().get(), tableName, true); } /** @@ -885,15 +924,40 @@ namespace sqlite_orm { return result; } - void drop_table_internal(sqlite3* db, const std::string& tableName) { + void drop_table_internal(sqlite3* db, const std::string& tableName, bool ifExists) { std::stringstream ss; - ss << "DROP TABLE " << streaming_identifier(tableName) << std::flush; + ss << "DROP TABLE"; + if(ifExists) { + ss << " IF EXISTS"; + } + ss << streaming_identifier(tableName) << std::flush; perform_void_exec(db, ss.str()); } - static int collate_callback(void* arg, int leftLen, const void* lhs, int rightLen, const void* rhs) { - auto& f = *(collating_function*)arg; - return f(leftLen, lhs, rightLen, rhs); + void drop_index_internal(const std::string& indexName, bool ifExists) { + std::stringstream ss; + ss << "DROP INDEX"; + if(ifExists) { + ss << " IF EXISTS"; + } + ss << quote_identifier(indexName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); + } + + void drop_trigger_internal(const std::string& triggerName, bool ifExists) { + std::stringstream ss; + ss << "DROP TRIGGER"; + if(ifExists) { + ss << " IF EXISTS"; + } + ss << quote_identifier(triggerName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); + } + + static int + collate_callback(void* argument, int leftLength, const void* lhs, int rightLength, const void* rhs) { + auto& function = *(collating_function*)argument; + return function(leftLength, lhs, rightLength, rhs); } static int busy_handler_callback(void* selfPointer, int triesCount) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index fc45ba9f..35cec13a 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -17701,27 +17701,66 @@ namespace sqlite_orm { std::bind(&storage_base::rollback, this)}; } + /** + * Drops index with given name. + * Calls `DROP INDEX indexName`. + * More info: https://www.sqlite.org/lang_dropindex.html + */ void drop_index(const std::string& indexName) { - std::stringstream ss; - ss << "DROP INDEX " << quote_identifier(indexName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + this->drop_index_internal(indexName, false); } + /** + * Drops trigger with given name if trigger exists. + * Calls `DROP INDEX IF EXISTS indexName`. + * More info: https://www.sqlite.org/lang_dropindex.html + */ + void drop_index_if_exists(const std::string& indexName) { + this->drop_index_internal(indexName, true); + } + + /** + * Drops trigger with given name. + * Calls `DROP TRIGGER triggerName`. + * More info: https://www.sqlite.org/lang_droptrigger.html + */ void drop_trigger(const std::string& triggerName) { - std::stringstream ss; - ss << "DROP TRIGGER " << quote_identifier(triggerName) << std::flush; - perform_void_exec(this->get_connection().get(), ss.str()); + this->drop_trigger_internal(triggerName, false); + } + + /** + * Drops trigger with given name if trigger exists. + * Calls `DROP TRIGGER IF EXISTS triggerName`. + * More info: https://www.sqlite.org/lang_droptrigger.html + */ + void drop_trigger_if_exists(const std::string& triggerName) { + this->drop_trigger_internal(triggerName, true); } + /** + * `VACUUM` query. + * More info: https://www.sqlite.org/lang_vacuum.html + */ void vacuum() { perform_void_exec(this->get_connection().get(), "VACUUM"); } /** - * Drops table with given name. + * Drops table with given name. + * Calls `DROP TABLE tableName`. + * More info: https://www.sqlite.org/lang_droptable.html */ void drop_table(const std::string& tableName) { - this->drop_table_internal(this->get_connection().get(), tableName); + this->drop_table_internal(this->get_connection().get(), tableName, false); + } + + /** + * Drops table with given name if table exists. + * Calls `DROP TABLE IF EXISTS tableName`. + * More info: https://www.sqlite.org/lang_droptable.html + */ + void drop_table_if_exists(const std::string& tableName) { + this->drop_table_internal(this->get_connection().get(), tableName, true); } /** @@ -18516,15 +18555,40 @@ namespace sqlite_orm { return result; } - void drop_table_internal(sqlite3* db, const std::string& tableName) { + void drop_table_internal(sqlite3* db, const std::string& tableName, bool ifExists) { std::stringstream ss; - ss << "DROP TABLE " << streaming_identifier(tableName) << std::flush; + ss << "DROP TABLE"; + if(ifExists) { + ss << " IF EXISTS"; + } + ss << streaming_identifier(tableName) << std::flush; perform_void_exec(db, ss.str()); } - static int collate_callback(void* arg, int leftLen, const void* lhs, int rightLen, const void* rhs) { - auto& f = *(collating_function*)arg; - return f(leftLen, lhs, rightLen, rhs); + void drop_index_internal(const std::string& indexName, bool ifExists) { + std::stringstream ss; + ss << "DROP INDEX"; + if(ifExists) { + ss << " IF EXISTS"; + } + ss << quote_identifier(indexName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); + } + + void drop_trigger_internal(const std::string& triggerName, bool ifExists) { + std::stringstream ss; + ss << "DROP TRIGGER"; + if(ifExists) { + ss << " IF EXISTS"; + } + ss << quote_identifier(triggerName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); + } + + static int + collate_callback(void* argument, int leftLength, const void* lhs, int rightLength, const void* rhs) { + auto& function = *(collating_function*)argument; + return function(leftLength, lhs, rightLength, rhs); } static int busy_handler_callback(void* selfPointer, int triesCount) { @@ -22059,7 +22123,7 @@ namespace sqlite_orm { template void drop_create_with_loss(sqlite3* db, const Table& table) { // eliminated all transaction handling - this->drop_table_internal(db, table.name); + this->drop_table_internal(db, table.name, false); this->create_table(db, table.name, table); } @@ -22086,7 +22150,7 @@ namespace sqlite_orm { this->copy_table(db, table.name, backupTableName, table, columnsToIgnore); - this->drop_table_internal(db, table.name); + this->drop_table_internal(db, table.name, false); this->rename_table(db, backupTableName, table.name); } diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index 40e1104c..e084b57d 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -91,6 +91,45 @@ TEST_CASE("drop table") { storage.drop_table(visitsTableName); REQUIRE_FALSE(storage.table_exists(usersTableName)); REQUIRE_FALSE(storage.table_exists(visitsTableName)); + + REQUIRE_THROWS(storage.drop_table(usersTableName)); + REQUIRE_THROWS(storage.drop_table(visitsTableName)); + + REQUIRE_NOTHROW(storage.drop_table_if_exists(usersTableName)); + REQUIRE_NOTHROW(storage.drop_table_if_exists(visitsTableName)); +} + +TEST_CASE("drop index") { + struct User { + int id = 0; + std::string name; + }; + const std::string indexName = "user_id_index"; + auto storage = make_storage( + {}, + make_index("user_id_index", &User::id), + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))); + storage.sync_schema(); + + REQUIRE_NOTHROW(storage.drop_index(indexName)); + REQUIRE_THROWS(storage.drop_index(indexName)); + REQUIRE_NOTHROW(storage.drop_index_if_exists(indexName)); +} + +TEST_CASE("drop trigger") { + struct User { + int id = 0; + std::string name; + }; + const std::string triggerName = "table_insert_InsertTest"; + auto storage = make_storage( + {}, + make_trigger(triggerName, after().insert().on().begin(update_all(set(c(&User::id) = 5))).end()), + make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))); + storage.sync_schema(); + REQUIRE_NOTHROW(storage.drop_trigger(triggerName)); + REQUIRE_THROWS(storage.drop_trigger(triggerName)); + REQUIRE_NOTHROW(storage.drop_trigger_if_exists(triggerName)); } TEST_CASE("rename table") { From d2f40144f5f19e62432fb5f42ee5843fd6e124ca Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sun, 14 Jul 2024 22:19:30 +0500 Subject: [PATCH 02/38] fixed whitespaces --- dev/storage_base.h | 6 +++--- include/sqlite_orm/sqlite_orm.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/storage_base.h b/dev/storage_base.h index 4f76208a..20a8d93f 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -930,7 +930,7 @@ namespace sqlite_orm { if(ifExists) { ss << " IF EXISTS"; } - ss << streaming_identifier(tableName) << std::flush; + ss << ' ' << streaming_identifier(tableName) << std::flush; perform_void_exec(db, ss.str()); } @@ -940,7 +940,7 @@ namespace sqlite_orm { if(ifExists) { ss << " IF EXISTS"; } - ss << quote_identifier(indexName) << std::flush; + ss << ' ' << quote_identifier(indexName) << std::flush; perform_void_exec(this->get_connection().get(), ss.str()); } @@ -950,7 +950,7 @@ namespace sqlite_orm { if(ifExists) { ss << " IF EXISTS"; } - ss << quote_identifier(triggerName) << std::flush; + ss << ' ' << quote_identifier(triggerName) << std::flush; perform_void_exec(this->get_connection().get(), ss.str()); } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 35cec13a..adf7767b 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -18561,7 +18561,7 @@ namespace sqlite_orm { if(ifExists) { ss << " IF EXISTS"; } - ss << streaming_identifier(tableName) << std::flush; + ss << ' ' << streaming_identifier(tableName) << std::flush; perform_void_exec(db, ss.str()); } @@ -18571,7 +18571,7 @@ namespace sqlite_orm { if(ifExists) { ss << " IF EXISTS"; } - ss << quote_identifier(indexName) << std::flush; + ss << ' ' << quote_identifier(indexName) << std::flush; perform_void_exec(this->get_connection().get(), ss.str()); } @@ -18581,7 +18581,7 @@ namespace sqlite_orm { if(ifExists) { ss << " IF EXISTS"; } - ss << quote_identifier(triggerName) << std::flush; + ss << ' ' << quote_identifier(triggerName) << std::flush; perform_void_exec(this->get_connection().get(), ss.str()); } From 26c0b76f8e6f557f7a50585331ebed6e25e08faf Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 17 Jul 2024 21:43:28 +0200 Subject: [PATCH 03/38] Added includes of dependent headers * Added missing includes of dependent internal headers * Cleaned up amalgamation files to only include the public facing high-level headers --- dev/alias.h | 2 +- dev/arithmetic_tag.h | 1 + dev/ast/match.h | 1 + dev/collate_argument.h | 1 - dev/conditions.h | 1 + dev/constraints.h | 2 +- dev/error_code.h | 1 - dev/expression.h | 3 +- dev/field_printer.h | 2 +- dev/functional/cxx_functional_polyfill.h | 1 + dev/implementations/storage_definitions.h | 5 +- dev/implementations/table_definitions.h | 1 + dev/indexed_column.h | 1 - dev/is_std_ptr.h | 1 + dev/operators.h | 1 - dev/pragma.h | 1 + dev/row_extractor.h | 14 +- dev/schema/table.h | 1 + dev/select_constraints.h | 3 +- dev/serializing_util.h | 2 +- dev/statement_binder.h | 6 +- dev/statement_serializer.h | 10 +- dev/storage.h | 1 + dev/storage_base.h | 2 + dev/storage_impl.h | 2 + dev/table_type_of.h | 1 + dev/tuple_helper/same_or_void.h | 1 + dev/tuple_helper/tuple_filter.h | 2 +- dev/udf_proxy.h | 2 +- dev/values.h | 1 - examples/iteration.cpp | 6 +- include/sqlite_orm/sqlite_orm.h | 24315 ++++++++-------- .../sqlite_orm/sqlite_orm.h | 24 +- third_party/amalgamate/config.json | 24 +- 34 files changed, 12204 insertions(+), 12238 deletions(-) diff --git a/dev/alias.h b/dev/alias.h index 822b41be..5611415c 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -1,6 +1,6 @@ #pragma once -#include // std::enable_if, std::is_same, std::conditional +#include // std::enable_if, std::is_same #include // std::make_index_sequence, std::move #include // std::string #include // std::stringstream diff --git a/dev/arithmetic_tag.h b/dev/arithmetic_tag.h index 26570fff..a89e7a2d 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 5500e282..57b499fa 100644 --- a/dev/ast/match.h +++ b/dev/ast/match.h @@ -1,4 +1,5 @@ #pragma once +#include namespace sqlite_orm { namespace internal { diff --git a/dev/collate_argument.h b/dev/collate_argument.h index 72088662..8de5e997 100644 --- a/dev/collate_argument.h +++ b/dev/collate_argument.h @@ -10,5 +10,4 @@ namespace sqlite_orm { rtrim, }; } - } diff --git a/dev/conditions.h b/dev/conditions.h index e1a1fccd..b5ee0fac 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -6,6 +6,7 @@ #include // std::tuple #include // std::move, std::forward #include // std::stringstream +#include // std::flush #include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" diff --git a/dev/constraints.h b/dev/constraints.h index eed1db45..94df7f69 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -1,10 +1,10 @@ #pragma once +#include // std::is_base_of, std::false_type, std::true_type #include // std::system_error #include // std::ostream #include // std::string #include // std::tuple -#include // std::is_base_of, std::false_type, std::true_type #include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" diff --git a/dev/error_code.h b/dev/error_code.h index 7410c268..662de285 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 657bf56e..a38faebf 100644 --- a/dev/expression.h +++ b/dev/expression.h @@ -5,9 +5,10 @@ #include // std::move, std::forward, std::declval #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" +#include "functional/cxx_universal.h" // ::nullptr_t #include "functional/cxx_type_traits_polyfill.h" #include "tags.h" +#include "operators.h" namespace sqlite_orm { diff --git a/dev/field_printer.h b/dev/field_printer.h index 6bd99034..61ba6894 100644 --- a/dev/field_printer.h +++ b/dev/field_printer.h @@ -10,7 +10,7 @@ #endif #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" +#include "functional/cxx_universal.h" // ::nullptr_t #include "functional/cxx_type_traits_polyfill.h" #include "is_std_ptr.h" #include "type_traits.h" diff --git a/dev/functional/cxx_functional_polyfill.h b/dev/functional/cxx_functional_polyfill.h index 72392363..474ab2f3 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/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index 30d498b1..80d777a2 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 a869d227..eb983961 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -3,6 +3,7 @@ * this file is also used to provide definitions of interface methods 'hitting the database'. */ #pragma once + #include // std::decay_t #include // std::move #include // std::find_if, std::ranges::find diff --git a/dev/indexed_column.h b/dev/indexed_column.h index e8bd3f1d..9426a00e 100644 --- a/dev/indexed_column.h +++ b/dev/indexed_column.h @@ -66,5 +66,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 dc48b1dc..3570df78 100644 --- a/dev/is_std_ptr.h +++ b/dev/is_std_ptr.h @@ -1,4 +1,5 @@ #pragma once + #include #include diff --git a/dev/operators.h b/dev/operators.h index d6cd2b22..180747db 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 c30008cb..53c798d9 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -6,6 +6,7 @@ #include // std::shared_ptr #include // std::vector #include +#include // std::flush #include "error_code.h" #include "row_extractor.h" diff --git a/dev/row_extractor.h b/dev/row_extractor.h index 42becde1..1516d56a 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -2,7 +2,8 @@ #include #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if -#include // atof, atoi, atoll +#include // std::atof, std::atoi, std::atoll +#include // std::strlen #include // std::system_error #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT @@ -10,7 +11,6 @@ #include // std::codecvt_utf8_utf16 #endif #include // std::vector -#include // strlen #include // std::copy #include // std::back_inserter #include // std::tuple, std::tuple_size, std::tuple_element @@ -18,7 +18,7 @@ #include #endif -#include "functional/cxx_universal.h" +#include "functional/cxx_universal.h" // ::nullptr_t, ::size_t #include "functional/cxx_functional_polyfill.h" #include "functional/static_magic.h" #include "tuple_helper/tuple_transformer.h" @@ -175,7 +175,7 @@ namespace sqlite_orm { using tag = arithmetic_tag_t; V extract(const char* columnText, const int_or_smaller_tag&) const { - return static_cast(atoi(columnText)); + return static_cast(std::atoi(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { @@ -187,7 +187,7 @@ namespace sqlite_orm { } V extract(const char* columnText, const bigint_tag&) const { - return static_cast(atoll(columnText)); + return static_cast(std::atoll(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { @@ -199,7 +199,7 @@ namespace sqlite_orm { } V extract(const char* columnText, const real_tag&) const { - return static_cast(atof(columnText)); + return static_cast(std::atof(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { @@ -389,7 +389,7 @@ namespace sqlite_orm { template<> struct row_extractor, void> { std::vector extract(const char* columnText) const { - return {columnText, columnText + (columnText ? ::strlen(columnText) : 0)}; + return {columnText, columnText + (columnText ? std::strlen(columnText) : 0)}; } std::vector extract(sqlite3_stmt* stmt, int columnIndex) const { diff --git a/dev/schema/table.h b/dev/schema/table.h index 055e70be..88d4fc02 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -22,6 +22,7 @@ #include "../alias_traits.h" #include "../constraints.h" #include "../table_info.h" +#include "index.h" #include "column.h" namespace sqlite_orm { diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 4b7226b1..74ad5cac 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -3,7 +3,7 @@ #ifdef SQLITE_ORM_WITH_CPP20_ALIASES #include #endif -#include // std::remove_const +#include // std::remove_cvref, std::is_convertible, std::is_same, std::is_member_pointer #include // std::string #include // std::move #include // std::tuple, std::get, std::tuple_size @@ -21,6 +21,7 @@ #include "core_functions.h" #include "alias_traits.h" #include "cte_moniker.h" +#include "schema/column.h" namespace sqlite_orm { diff --git a/dev/serializing_util.h b/dev/serializing_util.h index abfcfe0d..00dfc209 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -5,7 +5,7 @@ #include #include #include -#include // std::exchange, std::tuple_size +#include // std::exchange, std::tuple_size, std::make_index_sequence #include "functional/cxx_universal.h" // ::size_t #include "functional/cxx_type_traits_polyfill.h" diff --git a/dev/statement_binder.h b/dev/statement_binder.h index 0473f794..23ad894c 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -5,7 +5,7 @@ #include // std::default_delete #include // std::string, std::wstring #include // std::vector -#include // ::strncpy, ::strlen +#include // std::strncpy, std::strlen #include "functional/cxx_string_view.h" #ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED #include // ::wcsncpy, ::wcslen @@ -140,7 +140,7 @@ namespace sqlite_orm { auto stringData = this->string_data(value); auto dataCopy = new char[stringData.second + 1]; constexpr auto deleter = std::default_delete{}; - ::strncpy(dataCopy, stringData.first, stringData.second + 1); + std::strncpy(dataCopy, stringData.first, stringData.second + 1); sqlite3_result_text(context, dataCopy, stringData.second, obtain_xdestroy_for(deleter, dataCopy)); } @@ -155,7 +155,7 @@ namespace sqlite_orm { } std::pair string_data(const char* s) const { - return {s, int(::strlen(s))}; + return {s, int(std::strlen(s))}; } #endif }; diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 0e724925..c1f0f2c2 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -14,7 +14,7 @@ #include "functional/cxx_string_view.h" #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" +#include "functional/cxx_universal.h" // ::nullptr_t, ::size_t #include "functional/cxx_functional_polyfill.h" #include "functional/mpl.h" #include "tuple_helper/tuple_filter.h" @@ -28,7 +28,6 @@ #include "core_functions.h" #include "constraints.h" #include "conditions.h" -#include "schema/column.h" #include "indexed_column.h" #include "function.h" #include "prepared_statement.h" @@ -45,12 +44,13 @@ #include "serialize_result_type.h" #include "statement_binder.h" #include "values.h" -#include "schema/triggers.h" #include "table_type_of.h" -#include "schema/index.h" -#include "schema/table.h" #include "util.h" #include "error_code.h" +#include "schema/triggers.h" +#include "schema/column.h" +#include "schema/index.h" +#include "schema/table.h" namespace sqlite_orm { diff --git a/dev/storage.h b/dev/storage.h index a7bf160d..2d6fab2d 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -7,6 +7,7 @@ #include // std::remove_reference, std::is_base_of, std::decay, std::false_type, std::true_type #include // std::identity #include // std::stringstream +#include // std::flush #include // std::map #include // std::vector #include // std::tuple_size, std::tuple, std::make_tuple, std::tie diff --git a/dev/storage_base.h b/dev/storage_base.h index ddad5559..e071b464 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -5,6 +5,7 @@ #include // std::function, std::bind, std::bind_front #include // std::string #include // std::stringstream +#include // std::flush #include // std::move #include // std::system_error #include // std::vector @@ -30,6 +31,7 @@ #include "xdestroy_handling.h" #include "udf_proxy.h" #include "serializing_util.h" +#include "table_info.h" namespace sqlite_orm { diff --git a/dev/storage_impl.h b/dev/storage_impl.h index be629ced..d364daba 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -11,6 +11,8 @@ #include "type_traits.h" #include "select_constraints.h" #include "cte_types.h" +#include "schema/column.h" +#include "schema/table.h" #include "storage_lookup.h" // interface functions diff --git a/dev/table_type_of.h b/dev/table_type_of.h index 8dacf846..52604d78 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 ce272608..c0bd482b 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 785c9a9a..159a50b0 100644 --- a/dev/tuple_helper/tuple_filter.h +++ b/dev/tuple_helper/tuple_filter.h @@ -1,7 +1,7 @@ #pragma once #include // std::integral_constant, std::index_sequence, std::conditional, std::declval -#include // std::tuple +#include // std::tuple, std::tuple_cat, std::tuple_element #include "../functional/cxx_universal.h" // ::size_t #include "../functional/mpl/conditional.h" diff --git a/dev/udf_proxy.h b/dev/udf_proxy.h index a750fe60..cf152de9 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 68ce9639..25098cad 100644 --- a/dev/values.h +++ b/dev/values.h @@ -40,5 +40,4 @@ namespace sqlite_orm { internal::dynamic_values_t values(std::vector vector) { return {{std::move(vector)}}; } - } diff --git a/examples/iteration.cpp b/examples/iteration.cpp index 1ca54b8d..cd183ce4 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/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index fc45ba9f..8e9af631 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -278,16 +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_universal.h" + +// #include "functional/cxx_functional_polyfill.h" + +#include +#if __cpp_lib_invoke < 201411L +#include // std::enable_if, std::is_member_object_pointer, std::is_member_function_pointer +#endif +#include // std::forward + +// #include "cxx_type_traits_polyfill.h" #include @@ -476,163 +502,251 @@ namespace sqlite_orm { namespace polyfill = internal::polyfill; } -namespace sqlite_orm { - // C++ generic traits used throughout the library - namespace internal { - template - using is_any_of = polyfill::disjunction...>; +// #include "../member_traits/member_traits.h" - template - struct value_unref_type : polyfill::remove_cvref {}; +#include // std::enable_if, std::is_function, std::true_type, std::false_type - template - struct value_unref_type> : std::remove_const {}; +// #include "../functional/cxx_universal.h" + +// #include "../functional/cxx_type_traits_polyfill.h" +namespace sqlite_orm { + namespace internal { + // SFINAE friendly trait to get a member object pointer's field type template - using value_unref_type_t = typename value_unref_type::type; + struct object_field_type {}; template - using is_eval_order_garanteed = -#if __cpp_lib_is_aggregate >= 201703L - std::is_aggregate; -#else - std::is_pod; -#endif + using object_field_type_t = typename object_field_type::type; - // enable_if for types - template class Op, class... Args> - using match_if = std::enable_if_t::value>; + template + struct object_field_type : std::enable_if::value, F> {}; - // enable_if for types - template class Op, class... Args> - using match_if_not = std::enable_if_t>::value>; + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified return type) + template + struct getter_field_type {}; - // enable_if for types - template class Primary> - using match_specialization_of = std::enable_if_t::value>; + template + using getter_field_type_t = typename getter_field_type::type; - // enable_if for functions - template class Op, class... Args> - using satisfies = std::enable_if_t::value, bool>; + template + struct getter_field_type : getter_field_type {}; - // enable_if for functions - template class Op, class... Args> - using satisfies_not = std::enable_if_t>::value, bool>; + template + struct getter_field_type : polyfill::remove_cvref {}; - // enable_if for functions - template class Primary> - using satisfies_is_specialization_of = - std::enable_if_t::value, bool>; - } + template + struct getter_field_type : polyfill::remove_cvref {}; - // type name template aliases for syntactic sugar - namespace internal { - template - using type_t = typename T::type; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct getter_field_type : polyfill::remove_cvref {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - using auto_type_t = typename decltype(a)::type; + template + struct getter_field_type : polyfill::remove_cvref {}; #endif - template - using value_type_t = typename T::value_type; - - template - using field_type_t = typename T::field_type; - - template - using constraints_type_t = typename T::constraints_type; - - template - using columns_tuple_t = typename T::columns_tuple; - - template - using object_type_t = typename T::object_type; - - template - using elements_type_t = typename T::elements_type; - - template - using table_type_t = typename T::table_type; - - template - using target_type_t = typename T::target_type; + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) + template + struct setter_field_type {}; - template - using left_type_t = typename T::left_type; + template + using setter_field_type_t = typename setter_field_type::type; - template - using right_type_t = typename T::right_type; + template + struct setter_field_type : setter_field_type {}; - template - using on_type_t = typename T::on_type; + template + struct setter_field_type : polyfill::remove_cvref {}; - template - using expression_type_t = typename T::expression_type; +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct setter_field_type : polyfill::remove_cvref {}; +#endif - template - using alias_type_t = typename As::alias_type; + template + struct is_getter : std::false_type {}; + template + struct is_getter>> : std::true_type {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES template - using udf_type_t = typename T::udf_type; + SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter::value; - template - using auto_udf_type_t = typename decltype(a)::udf_type; -#endif + template + struct is_setter : std::false_type {}; + template + struct is_setter>> : std::true_type {}; -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template - using cte_moniker_type_t = typename T::cte_moniker_type; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter::value; - template - using cte_mapper_type_t = typename T::cte_mapper_type; + template + struct member_field_type : object_field_type, getter_field_type, setter_field_type {}; - // T::alias_type or nonesuch template - using alias_holder_type_or_none = polyfill::detected; + using member_field_type_t = typename member_field_type::type; template - using alias_holder_type_or_none_t = typename alias_holder_type_or_none::type; -#endif + struct member_object_type {}; -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - template - concept stateless = std::is_empty_v; -#endif - } + template + struct member_object_type : polyfill::type_identity {}; -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - template - concept orm_names_type = requires { typename T::type; }; -#endif + template + using member_object_type_t = typename member_object_type::type; + } } -#pragma once namespace sqlite_orm { - namespace internal { + namespace polyfill { + // C++20 or later (unfortunately there's no feature test macro). + // Stupidly, clang says C++20, but `std::identity` was only implemented in libc++ 13 and libstd++-v3 10 + // (the latter is used on Linux). + // gcc got it right and reports C++20 only starting with v10. + // The check here doesn't care and checks the library versions in use. + // + // Another way of detection would be the constrained algorithms feature-test macro __cpp_lib_ranges +#if(__cplusplus >= 202002L) && \ + ((!_LIBCPP_VERSION || _LIBCPP_VERSION >= 13000) && (!_GLIBCXX_RELEASE || _GLIBCXX_RELEASE >= 10)) + using std::identity; +#else + struct identity { + template + constexpr T&& operator()(T&& v) const noexcept { + return std::forward(v); + } - enum class collate_argument { - binary, - nocase, - rtrim, - }; - } + using is_transparent = int; + }; +#endif -} -#pragma once +#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; + } -#include // std::system_error -#include // std::ostream -#include // std::string -#include // std::tuple -#include // std::is_base_of, std::false_type, std::true_type + // 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)...); + } -// #include "functional/cxx_universal.h" + // 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)...); + } -// #include "functional/cxx_type_traits_polyfill.h" + // functor + template + decltype(auto) invoke(Callable&& callable, Args&&... args) { + return std::forward(callable)(std::forward(args)...); + } +#endif + } + } + + namespace polyfill = internal::polyfill; +} + +// #include "functional/static_magic.h" + +#ifndef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED +#include // std::false_type, std::true_type, std::integral_constant +#endif +#include // std::forward + +namespace sqlite_orm { + + // got from here + // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co + namespace internal { + + // note: this is a class template accompanied with a variable template because older compilers (e.g. VC 2017) + // cannot handle a static lambda variable inside a template function + template + struct empty_callable_t { + template + R operator()(Args&&...) const { + return R(); + } + }; + template + constexpr empty_callable_t empty_callable{}; + +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + template + decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { + if constexpr(B) { + return std::forward(trueFn); + } else { + return std::forward(falseFn); + } + } + + template + decltype(auto) static_if([[maybe_unused]] T&& trueFn) { + if constexpr(B) { + return std::forward(trueFn); + } else { + return empty_callable<>; + } + } + + template + void call_if_constexpr([[maybe_unused]] L&& lambda, [[maybe_unused]] Args&&... args) { + if constexpr(B) { + lambda(std::forward(args)...); + } + } +#else + template + decltype(auto) static_if(std::true_type, T&& trueFn, const F&) { + return std::forward(trueFn); + } + + template + decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { + return std::forward(falseFn); + } + + template + decltype(auto) static_if(T&& trueFn, F&& falseFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), std::forward(falseFn)); + } + + template + decltype(auto) static_if(T&& trueFn) { + return static_if(std::integral_constant{}, std::forward(trueFn), empty_callable<>); + } + + template + void call_if_constexpr(L&& lambda, Args&&... args) { + static_if(std::forward(lambda))(std::forward(args)...); + } +#endif + } + +} // #include "functional/mpl.h" @@ -1180,53 +1294,6 @@ namespace sqlite_orm { } } -// #include "tuple_helper/same_or_void.h" - -#include // std::common_type - -namespace sqlite_orm { - namespace internal { - - /** - * Accepts any number of arguments and evaluates a nested `type` typename as `T` if all arguments are the same, otherwise `void`. - */ - template - struct same_or_void { - using type = void; - }; - - template - struct same_or_void { - using type = A; - }; - - template - struct same_or_void { - using type = A; - }; - - template - using same_or_void_t = typename same_or_void::type; - - template - struct same_or_void : same_or_void {}; - - template - struct common_type_of; - - template class Pack, class... Types> - struct common_type_of> : std::common_type {}; - - /** - * Accepts a pack of types and defines a nested `type` typename to a common type if possible, otherwise nonexistent. - * - * @note: SFINAE friendly like `std::common_type`. - */ - template - using common_type_of_t = typename common_type_of::type; - } -} - // #include "tuple_helper/tuple_traits.h" // #include "../functional/cxx_type_traits_polyfill.h" @@ -1297,7 +1364,7 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_filter.h" #include // std::integral_constant, std::index_sequence, std::conditional, std::declval -#include // std::tuple +#include // std::tuple, std::tuple_cat, std::tuple_element // #include "../functional/cxx_universal.h" // ::size_t @@ -1458,12081 +1525,11685 @@ namespace sqlite_orm { } } -// #include "type_traits.h" - -// #include "collate_argument.h" - -// #include "error_code.h" - -#include -#include // std::error_code, std::system_error -#include // std::string -#include -#include // std::ostringstream -#include +// #include "tuple_helper/tuple_transformer.h" -namespace sqlite_orm { +#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 - /** @short Enables classifying sqlite error codes. +// #include "../functional/cxx_universal.h" +// ::size_t +// #include "../functional/cxx_type_traits_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/cxx_functional_polyfill.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, - }; +// #include "../functional/mpl.h" -} +namespace sqlite_orm { + namespace internal { -namespace std { - template<> - struct is_error_code_enum<::sqlite_orm::sqlite_errc> : true_type {}; + template class Op> + struct tuple_transformer; - template<> - struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; -} + template class Pack, class... Types, template class Op> + struct tuple_transformer, Op> { + using type = Pack...>; + }; -namespace sqlite_orm { + /* + * Transform specified tuple. + * + * `Op` is a metafunction. + */ + template class Op> + using transform_tuple_t = typename tuple_transformer::type; - class orm_error_category : public std::error_category { - public: - const char* name() const noexcept override final { - return "ORM error"; + // 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))...); } - 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"; - } + /* + * 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)); } - }; - class sqlite_error_category : public std::error_category { - public: - const char* name() const noexcept override final { - return "SQLite 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{}; + } + }; - std::string message(int c) const override final { - return sqlite3_errstr(c); - } - }; + /* + * Function object that takes a type, applies a projection on it, and returns the tuple size of the projected type (as an integral constant). + * The projection is applied on the argument type, not the argument value/object. + */ + template class NestedProject> + struct project_nested_tuple_size { + template + constexpr auto operator()(const T&) const { + return typename std::tuple_size>::type{}; + } + }; - inline const orm_error_category& get_orm_error_category() { - static orm_error_category res; - return res; - } + template class NestedProject, class Tpl, class IdxSeq> + using nested_tuple_size_for_t = decltype(recombine_tuple(plus_fold_integrals{}, + std::declval(), + IdxSeq{}, + project_nested_tuple_size{}, + std::integral_constant{})); +#endif - inline const sqlite_error_category& get_sqlite_error_category() { - static sqlite_error_category res; - return res; - } + template + constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; + } - inline std::error_code make_error_code(sqlite_errc ev) noexcept { - return {static_cast(ev), get_sqlite_error_category()}; + /* + * Like `std::make_from_tuple`, but using a projection on the tuple elements. + */ + template + constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple( + std::forward(tpl), + std::make_index_sequence>::value>{}, + std::forward(project)); + } } +} - inline std::error_code make_error_code(orm_error_code ev) noexcept { - return {static_cast(ev), get_orm_error_category()}; - } +// #include "tuple_helper/tuple_iteration.h" - template - std::string get_error_message(sqlite3* db, T&&... args) { - std::ostringstream stream; - using unpack = int[]; - (void)unpack{0, (stream << args, 0)...}; - stream << sqlite3_errmsg(db); - return stream.str(); - } +#include // std::get, std::tuple_element, std::tuple_size +#include // std::index_sequence, std::make_index_sequence +#include // std::forward, std::move - template - [[noreturn]] void throw_error(sqlite3* db, T&&... args) { - throw std::system_error{sqlite_errc(sqlite3_errcode(db)), get_error_message(db, std::forward(args)...)}; - } +// #include "../functional/cxx_universal.h" +// ::size_t - inline std::system_error sqlite_to_system_error(int ev) { - return {sqlite_errc(ev)}; - } +namespace sqlite_orm { + namespace internal { +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + template + void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { + if constexpr(reversed) { + // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= + int sink; + // note: `(void)` cast silences warning 'expression result unused' + (void)((lambda(std::get(tpl)), sink) = ... = 0); + } else { + (lambda(std::get(tpl)), ...); + } + } +#else + template + void iterate_tuple(Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} - inline std::system_error sqlite_to_system_error(sqlite3* db) { - return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(db)}; - } + template + void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { + if SQLITE_ORM_CONSTEXPR_IF(reversed) { + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + lambda(std::get(tpl)); + } else { + lambda(std::get(tpl)); + iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + } + } +#endif + template + void iterate_tuple(Tpl&& tpl, L&& lambda) { + iterate_tuple(tpl, + std::make_index_sequence>::value>{}, + std::forward(lambda)); + } - [[noreturn]] inline void throw_translated_sqlite_error(int ev) { - throw sqlite_to_system_error(ev); - } +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template + void iterate_tuple(std::index_sequence, L&& lambda) { + (lambda((std::tuple_element_t*)nullptr), ...); + } +#else + template + void iterate_tuple(std::index_sequence, L&& lambda) { + using Sink = int[sizeof...(Idx)]; + (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; + } +#endif + template + void iterate_tuple(L&& lambda) { + iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); + } - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { - throw sqlite_to_system_error(db); - } + template class Base, class L> + struct lambda_as_template_base : L { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} +#endif + template + decltype(auto) operator()(const Base& object) { + return L::operator()(object); + } + }; - [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { - throw sqlite_to_system_error(sqlite3_db_handle(stmt)); + /* + * This method wraps the specified callable in another function object, + * which in turn implicitly casts its single argument to the specified template base class, + * then passes the converted argument to the lambda. + * + * Note: This method is useful for reducing combinatorial instantiation of template lambdas, + * as long as this library supports compilers that do not implement + * explicit template parameters in generic lambdas [SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED]. + * Unfortunately it doesn't work with user-defined conversion operators in order to extract + * parts of a class. In other words, the destination type must be a direct template base class. + */ + template class Base, class L> + lambda_as_template_base call_as_template_base(L lambda) { + return {std::move(lambda)}; + } } } -// #include "table_type_of.h" +// #include "type_traits.h" -#include // std::enable_if, std::is_convertible +#include // std::enable_if, std::is_same, std::is_empty, std::is_aggregate +#if __cpp_lib_unwrap_ref >= 201811L +#include // std::reference_wrapper +#else +#include // std::reference_wrapper +#endif -namespace sqlite_orm { +// #include "functional/cxx_core_features.h" - namespace internal { +// #include "functional/cxx_type_traits_polyfill.h" - template - struct column_pointer; +namespace sqlite_orm { + // C++ generic traits used throughout the library + namespace internal { + template + using is_any_of = polyfill::disjunction...>; - template - struct indexed_column_t; + template + struct value_unref_type : polyfill::remove_cvref {}; - /** - * 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; - - template - struct table_type_of { - using type = O; - }; - - template - struct table_type_of> { - using type = T; - }; + struct value_unref_type> : std::remove_const {}; - template - struct table_type_of> : table_type_of {}; + template + using value_unref_type_t = typename value_unref_type::type; template - using table_type_of_t = typename table_type_of::type; + using is_eval_order_garanteed = +#if __cpp_lib_is_aggregate >= 201703L + std::is_aggregate; +#else + std::is_pod; +#endif - /* - * 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 types + template class Op, class... Args> + using match_if = std::enable_if_t::value>; - /* - * `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 types + template class Op, class... Args> + using match_if_not = std::enable_if_t>::value>; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; - } -} + // enable_if for types + template class Primary> + using match_specialization_of = std::enable_if_t::value>; -// #include "type_printer.h" + // enable_if for functions + template class Op, class... Args> + using satisfies = std::enable_if_t::value, bool>; -#include // std::string -#include // std::shared_ptr, std::unique_ptr -#include // std::vector -// #include "functional/cxx_optional.h" + // enable_if for functions + template class Op, class... Args> + using satisfies_not = std::enable_if_t>::value, bool>; -// #include "cxx_core_features.h" + // enable_if for functions + template class Primary> + using satisfies_is_specialization_of = + std::enable_if_t::value, bool>; + } -#if SQLITE_ORM_HAS_INCLUDE() -#include -#endif + // type name template aliases for syntactic sugar + namespace internal { + template + using type_t = typename T::type; -#if __cpp_lib_optional >= 201606L -#define SQLITE_ORM_OPTIONAL_SUPPORTED +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using auto_type_t = typename decltype(a)::type; #endif -// #include "functional/cxx_type_traits_polyfill.h" + template + using value_type_t = typename T::value_type; -// #include "type_traits.h" + template + using field_type_t = typename T::field_type; -// #include "is_std_ptr.h" + template + using constraints_type_t = typename T::constraints_type; -#include -#include + template + using columns_tuple_t = typename T::columns_tuple; -namespace sqlite_orm { + template + using object_type_t = typename T::object_type; - /** - * Specialization for optional type (std::shared_ptr / std::unique_ptr). - */ - template - struct is_std_ptr : std::false_type {}; + template + using elements_type_t = typename T::elements_type; - template - struct is_std_ptr> : std::true_type { - using element_type = typename std::shared_ptr::element_type; + template + using table_type_t = typename T::table_type; - static std::shared_ptr make(std::remove_cv_t&& v) { - return std::make_shared(std::move(v)); - } - }; + template + using target_type_t = typename T::target_type; - template - struct is_std_ptr> : std::true_type { - using element_type = typename std::unique_ptr::element_type; + template + using left_type_t = typename T::left_type; - static auto make(std::remove_cv_t&& v) { - return std::make_unique(std::move(v)); - } - }; -} + template + using right_type_t = typename T::right_type; -namespace sqlite_orm { + template + using on_type_t = typename T::on_type; - /** - * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) - */ - template - struct type_printer {}; + template + using expression_type_t = typename T::expression_type; - struct integer_printer { - const std::string& print() const { - static const std::string res = "INTEGER"; - return res; - } - }; + template + using alias_type_t = typename As::alias_type; - struct text_printer { - const std::string& print() const { - static const std::string res = "TEXT"; - return res; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using udf_type_t = typename T::udf_type; - struct real_printer { - const std::string& print() const { - static const std::string res = "REAL"; - return res; - } - }; + template + using auto_udf_type_t = typename decltype(a)::udf_type; +#endif - struct blob_printer { - const std::string& print() const { - static const std::string res = "BLOB"; - return res; - } - }; +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template + using cte_moniker_type_t = typename T::cte_moniker_type; - // 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 + using cte_mapper_type_t = typename T::cte_mapper_type; - template - struct type_printer::value>> : real_printer {}; + // T::alias_type or nonesuch + template + using alias_holder_type_or_none = polyfill::detected; - template - struct type_printer, - std::is_base_of, - std::is_base_of>::value>> - : text_printer {}; + template + using alias_holder_type_or_none_t = typename alias_holder_type_or_none::type; +#endif - template - struct type_printer::value>> : type_printer {}; +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept stateless = std::is_empty_v; +#endif + } -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED template - struct type_printer>> - : type_printer {}; + concept orm_names_type = requires { typename T::type; }; #endif - - template<> - struct type_printer, void> : blob_printer {}; } -namespace sqlite_orm { +// #include "alias.h" - namespace internal { +#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 - enum class conflict_clause_t { - rollback, - abort, - fail, - ignore, - replace, - }; +// #include "functional/cxx_type_traits_polyfill.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/mpl/conditional.h" - template - struct primary_key_with_autoincrement : T { - using primary_key_type = T; +// #include "functional/cstring_literal.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 - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include // std::index_sequence +#include // std::copy_n +#endif - /** - * 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 "cxx_universal.h" +// ::size_t - columns_tuple columns; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +namespace sqlite_orm::internal { + /* + * Wraps a C string of fixed size. + * Its main purpose is to enable the user-defined string literal operator template. + */ + template + struct cstring_literal { + static constexpr size_t size() { + return N - 1; + } - primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} + constexpr cstring_literal(const char (&cstr)[N]) { + std::copy_n(cstr, N, this->cstr); + } - self asc() const { - auto res = *this; - res.options.asc_option = order_by::ascending; - return res; - } + char cstr[N]; + }; - self desc() const { - auto res = *this; - res.options.asc_option = order_by::descending; - return res; - } + template class Template, cstring_literal literal, size_t... Idx> + consteval auto explode_into(std::index_sequence) { + return Template{}; + } +} +#endif - primary_key_with_autoincrement autoincrement() const { - return {*this}; - } +// #include "type_traits.h" - self on_conflict_rollback() const { - auto res = *this; - res.options.conflict_clause_is_on = true; - res.options.conflict_clause = conflict_clause_t::rollback; - return res; - } +// #include "alias_traits.h" - self on_conflict_abort() const { - auto res = *this; - res.options.conflict_clause_is_on = true; - res.options.conflict_clause = conflict_clause_t::abort; - return res; - } +#include // std::is_base_of, std::is_same +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include +#endif - 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 "functional/cxx_universal.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 "functional/cxx_type_traits_polyfill.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 "type_traits.h" - struct unique_base { - operator std::string() const { - return "UNIQUE"; - } - }; +// #include "table_reference.h" - /** - * UNIQUE constraint class. +#include // std::remove_const, std::type_identity +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include +#endif + +// #include "functional/cxx_type_traits_polyfill.h" + +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, - }; + template + struct is_recordset_alias : polyfill::bool_constant> {}; - inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { - switch(action) { - case foreign_key_action::no_action: - os << "NO ACTION"; - break; - case foreign_key_action::restrict_: - os << "RESTRICT"; - break; - case foreign_key_action::set_null: - os << "SET NULL"; - break; - case foreign_key_action::set_default: - os << "SET DEFAULT"; - break; - case foreign_key_action::cascade: - os << "CASCADE"; - break; - case foreign_key_action::none: - break; - } - return os; - } + /** @short Alias of a concrete table, see `orm_table_alias`. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction< + is_recordset_alias, + polyfill::negation, std::remove_const_t>>>::value; - struct on_update_delete_base { - const bool update; // true if update and false if delete + template + struct is_table_alias : polyfill::bool_constant> {}; - operator std::string() const { - if(this->update) { - return "ON UPDATE"; - } else { - return "ON DELETE"; - } - } - }; - - /** - * F - foreign key class + /** @short Moniker of a CTE, see `orm_cte_moniker`. */ - template - struct on_update_delete_t : on_update_delete_base { - using foreign_key_type = F; - - const foreign_key_type& fk; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_cte_moniker_v = +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + polyfill::conjunction_v, + std::is_same, std::remove_const_t>>; +#else + false; +#endif - on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : - on_update_delete_base{update_}, fk(fk_), _action(action_) {} + template + using is_cte_moniker = polyfill::bool_constant>; + } - foreign_key_action _action = foreign_key_action::none; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + concept orm_alias = std::derived_from; - foreign_key_type no_action() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::no_action; - } else { - res.on_delete._action = foreign_key_action::no_action; - } - return res; - } + /** @short Specifies that a type is an alias of a column in a record set. + * + * A column alias has the following traits: + * - is derived from `alias_tag` + * - must not have a nested `type` typename + */ + template + concept orm_column_alias = (orm_alias && !orm_names_type); - foreign_key_type restrict_() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::restrict_; - } else { - res.on_delete._action = foreign_key_action::restrict_; - } - return res; - } + /** @short Specifies that a type is an alias of any type of record set. + * + * A record set alias has the following traits: + * - is derived from `alias_tag`. + * - has a nested `type` typename, which refers to a mapped object. + */ + template + concept orm_recordset_alias = (orm_alias && orm_names_type); - foreign_key_type set_null() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::set_null; - } else { - res.on_delete._action = foreign_key_action::set_null; - } - return res; - } + /** @short Specifies that a type is an alias of a concrete table. + * + * A concrete table alias has the following traits: + * - is derived from `alias_tag`. + * - has a `type` typename, which refers to another mapped object (i.e. doesn't refer to itself). + */ + template + concept orm_table_alias = (orm_recordset_alias && !std::same_as>); - foreign_key_type set_default() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::set_default; - } else { - res.on_delete._action = foreign_key_action::set_default; - } - return res; - } + /** @short Moniker of a CTE. + * + * A CTE moniker has the following traits: + * - is derived from `alias_tag`. + * - has a `type` typename, which refers to itself. + */ + template + concept orm_cte_moniker = (orm_recordset_alias && std::same_as>); - foreign_key_type cascade() const { - auto res = this->fk; - if(update) { - res.on_update._action = foreign_key_action::cascade; - } else { - res.on_delete._action = foreign_key_action::cascade; - } - return res; - } + /** @short Specifies that a type refers to a mapped table (possibly aliased). + */ + template + concept orm_refers_to_table = (orm_table_reference || orm_table_alias); - operator bool() const { - return this->_action != foreign_key_action::none; - } - }; + /** @short Specifies that a type refers to a recordset. + */ + template + concept orm_refers_to_recordset = (orm_table_reference || orm_recordset_alias); - template - bool operator==(const on_update_delete_t& lhs, const on_update_delete_t& rhs) { - return lhs._action == rhs._action; - } + /** @short Specifies that a type is a mapped recordset (table reference). + */ + template + concept orm_mapped_recordset = (orm_table_reference || orm_cte_moniker); +#endif +} - template - struct foreign_key_t, std::tuple> { - using columns_type = std::tuple; - using references_type = std::tuple; - using self = foreign_key_t; +// #include "table_type_of.h" - /** - * Holds obect type of all referenced columns. - */ - using target_type = same_or_void_t...>; +#include // std::enable_if, std::is_convertible - /** - * Holds obect type of all source columns. - */ - using source_type = same_or_void_t...>; +namespace sqlite_orm { - columns_type columns; - references_type references; + namespace internal { - on_update_delete_t on_update; - on_update_delete_t on_delete; + template + struct column_pointer; - static_assert(std::tuple_size::value == std::tuple_size::value, - "Columns size must be equal to references tuple"); - static_assert(!std::is_same::value, "All references must have the same type"); + template + struct indexed_column_t; - foreign_key_t(columns_type columns_, references_type references_) : - columns(std::move(columns_)), references(std::move(references_)), - on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} + /** + * Trait class used to define table mapped type by setter/getter/member + * T - member pointer + * `type` is a type which is mapped. + * E.g. + * - `table_type_of::type` is `User` + * - `table_type_of::type` is `User` + * - `table_type_of::type` is `User` + * - `table_type_of(&User::id))>::type` is `User` + * - `table_type_of*&User::id)>::type` is `User` + */ + template + struct table_type_of; - foreign_key_t(const self& other) : - columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), - on_delete(*this, false, other.on_delete._action) {} + template + struct table_type_of { + using type = O; + }; - self& operator=(const self& other) { - this->columns = other.columns; - this->references = other.references; - this->on_update = {*this, true, other.on_update._action}; - this->on_delete = {*this, false, other.on_delete._action}; - return *this; - } + template + struct table_type_of> { + using type = T; }; - template - bool operator==(const foreign_key_t& lhs, const foreign_key_t& rhs) { - return lhs.columns == rhs.columns && lhs.references == rhs.references && lhs.on_update == rhs.on_update && - lhs.on_delete == rhs.on_delete; - } + template + struct table_type_of> : table_type_of {}; - /** - * Cs can be a class member pointer, a getter function member pointer or setter - * func member pointer - * Available in SQLite 3.6.19 or higher + template + using table_type_of_t = typename table_type_of::type; + + /* + * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. + * + * One use case is the ability to create column reference to an aliased table column of a derived object field without explicitly using a column pointer. + * E.g. + * regular: `alias_column>(column(&Base::field))` + * short: `alias_column>(&Base::field)` */ - template - struct foreign_key_intermediate_t { - using tuple_type = std::tuple; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; - tuple_type columns; + /* + * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_field_of_v::value>> = true; - template - foreign_key_t, std::tuple> references(Rs... refs) { - return {std::move(this->columns), {std::forward(refs)...}}; - } - }; -#endif + template + SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; + } +} - struct collate_constraint_t { - collate_argument argument = collate_argument::binary; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - collate_constraint_t(collate_argument argument) : argument{argument} {} -#endif +// #include "tags.h" - operator std::string() const { - return "COLLATE " + this->string_from_collate_argument(this->argument); - } +// #include "functional/cxx_functional_polyfill.h" - static std::string string_from_collate_argument(collate_argument argument) { - switch(argument) { - case collate_argument::binary: - return "BINARY"; - case collate_argument::nocase: - return "NOCASE"; - case collate_argument::rtrim: - return "RTRIM"; - } - throw std::system_error{orm_error_code::invalid_collate_argument_enum}; - } - }; +namespace sqlite_orm { + namespace internal { + struct negatable_t {}; - template - struct check_t { - using expression_type = T; + /** + * Inherit from this class to support arithmetic types overloading + */ + struct arithmetic_t {}; - expression_type expression; - }; + /** + * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + */ + struct condition_t {}; - struct basic_generated_always { - enum class storage_type { - not_specified, - virtual_, - stored, - }; + /** + * Specialize if a type participates as an argument to overloaded operators (arithmetic, conditional, negation, chaining) + */ + template + SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v = false; -#if SQLITE_VERSION_NUMBER >= 3031000 - bool full = true; - storage_type storage = storage_type::not_specified; -#endif + template + using is_operator_argument = polyfill::bool_constant>; + } +} -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} -#endif - }; +// #include "column_pointer.h" -#if SQLITE_VERSION_NUMBER >= 3031000 - template - struct generated_always_t : basic_generated_always { - using expression_type = T; +#include // std::enable_if, std::is_convertible +#include // std::move - expression_type expression; +// #include "functional/cxx_core_features.h" - generated_always_t(expression_type expression_, bool full, storage_type storage) : - basic_generated_always{full, storage}, expression(std::move(expression_)) {} +// #include "functional/cxx_type_traits_polyfill.h" - generated_always_t virtual_() { - return {std::move(this->expression), this->full, storage_type::virtual_}; - } +// #include "type_traits.h" - generated_always_t stored() { - return {std::move(this->expression), this->full, storage_type::stored}; - } - }; -#endif +// #include "table_reference.h" - struct null_t {}; +// #include "alias_traits.h" - struct not_null_t {}; - } +// #include "tags.h" +namespace sqlite_orm { namespace internal { + /** + * This class is used to store explicit mapped type T and its column descriptor (member pointer/getter/setter). + * Is useful when mapped type is derived from other type and base class has members mapped to a storage. + */ + template + struct column_pointer { + using type = T; + using field_type = F; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = -#if SQLITE_VERSION_NUMBER >= 3006019 - polyfill::is_specialization_of::value; -#else - false; -#endif + field_type field; + }; template - struct is_foreign_key : polyfill::bool_constant> {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_column_pointer_v = + polyfill::is_specialization_of::value; template - SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = std::is_base_of::value; + struct is_column_pointer : polyfill::bool_constant> {}; template - struct is_primary_key : polyfill::bool_constant> {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v::value>> = + true; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = -#if SQLITE_VERSION_NUMBER >= 3031000 - polyfill::is_specialization_of::value; -#else - false; +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + template + struct alias_holder; #endif - - template - struct is_generated_always : polyfill::bool_constant> {}; - - /** - * PRIMARY KEY INSERTABLE traits. - */ - template - struct is_primary_key_insertable - : polyfill::disjunction< - mpl::invoke_t, - check_if_has_template>, - constraints_type_t>, - std::is_base_of>>> { - - static_assert(tuple_has, is_primary_key>::value, - "an unexpected type was passed"); - }; - - template - using is_column_constraint = mpl::invoke_t>, - check_if_is_type, - check_if_is_type, - check_if_is_type>, - check_if_is_template, - check_if_is_template, - check_if_is_type, - check_if, - check_if_is_type>, - T>; } -#if SQLITE_VERSION_NUMBER >= 3031000 - template - internal::generated_always_t generated_always_as(T expression) { - return {std::move(expression), true, internal::basic_generated_always::storage_type::not_specified}; - } - - template - internal::generated_always_t as(T expression) { - return {std::move(expression), false, internal::basic_generated_always::storage_type::not_specified}; - } -#endif - -#if SQLITE_VERSION_NUMBER >= 3006019 /** - * FOREIGN KEY constraint construction function that takes member pointer as argument - * Available in SQLite 3.6.19 or higher + * Explicitly refer to a column, used in contexts + * where the automatic object mapping deduction needs to be overridden. + * + * Example: + * struct BaseType : { int64 id; }; + * struct MyType : BaseType { ... }; + * storage.select(column(&BaseType::id)); */ - template - internal::foreign_key_intermediate_t foreign_key(Cs... columns) { - return {{std::forward(columns)...}}; + template = true> + constexpr internal::column_pointer column(F Base::*field) { + static_assert(std::is_convertible::value, "Field must be from derived class"); + return {field}; } -#endif +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * UNIQUE table constraint builder function. + * Explicitly refer to a column. */ - template - internal::unique_t unique(Args... args) { - return {{std::forward(args)...}}; + template + constexpr auto column(F O::*field) { + return column>(field); } - /** - * UNIQUE column constraint builder function. - */ - inline internal::unique_t<> unique() { - return {{}}; + // Intentionally place pointer-to-member operator for table references in the internal namespace + // to facilitate ADL (Argument Dependent Lookup) + namespace internal { + /** + * Explicitly refer to a column. + */ + template + constexpr auto operator->*(const R& /*table*/, F O::*field) { + return column(field); + } } -#if SQLITE_VERSION_NUMBER >= 3009000 /** - * UNINDEXED column constraint builder function. Used in FTS virtual tables. - * - * https://www.sqlite.org/fts5.html#the_unindexed_column_option + * Make a table reference. */ - inline internal::unindexed_t unindexed() { + template + requires(!orm_recordset_alias) + consteval internal::table_reference column() { return {}; } /** - * prefix=N table constraint builder function. Used in FTS virtual tables. - * - * https://www.sqlite.org/fts5.html#prefix_indexes + * Make a table reference. */ - template - internal::prefix_t prefix(T value) { - return {std::move(value)}; + template + requires(!orm_recordset_alias) + consteval internal::table_reference c() { + return {}; } +#endif +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) /** - * tokenize='...'' table constraint builder function. Used in FTS virtual tables. - * - * https://www.sqlite.org/fts5.html#tokenizers + * Explicitly refer to a column alias mapped into a CTE or subquery. + * + * Example: + * struct Object { ... }; + * using cte_1 = decltype(1_ctealias); + * storage.with(cte()(select(&Object::id)), select(column(&Object::id))); + * storage.with(cte()(select(&Object::id)), select(column(1_colalias))); + * storage.with(cte()(select(as(&Object::id))), select(column(colalias_a{}))); + * storage.with(cte(colalias_a{})(select(&Object::id)), select(column(colalias_a{}))); + * storage.with(cte()(select(as(&Object::id))), select(column(get()))); */ - template - internal::tokenize_t tokenize(T value) { - return {std::move(value)}; + template = true> + constexpr auto column(F field) { + using namespace ::sqlite_orm::internal; + + static_assert(is_cte_moniker_v, "`Moniker' must be a CTE moniker"); + + if constexpr(polyfill::is_specialization_of_v) { + static_assert(is_column_alias_v>); + return column_pointer{{}}; + } else if constexpr(is_column_alias_v) { + return column_pointer>{{}}; + } else { + return column_pointer{std::move(field)}; + } + (void)field; } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * content='' table constraint builder function. Used in FTS virtual tables. - * - * https://www.sqlite.org/fts5.html#contentless_tables + * Explicitly refer to a column mapped into a CTE or subquery. + * + * Example: + * struct Object { ... }; + * storage.with(cte<"z"_cte>()(select(&Object::id)), select(column<"z"_cte>(&Object::id))); + * storage.with(cte<"z"_cte>()(select(&Object::id)), select(column<"z"_cte>(1_colalias))); */ - template - internal::content_t content(T value) { - return {std::move(value)}; + template + constexpr auto column(F field) { + using Moniker = std::remove_const_t; + return column(std::forward(field)); } /** - * content='table' table constraint builder function. Used in FTS virtual tables. - * - * https://www.sqlite.org/fts5.html#external_content_tables - */ - template - internal::table_content_t content() { - return {}; - } -#endif - - /** - * PRIMARY KEY table constraint builder function. - */ - template - internal::primary_key_t primary_key(Cs... cs) { - return {{std::forward(cs)...}}; - } - - /** - * PRIMARY KEY column constraint builder function. + * 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 - 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 {}; + template + constexpr auto operator->*(const Moniker& /*moniker*/, F field) { + return column(std::forward(field)); } +#endif +#endif } -#pragma once - -#include // std::false_type, std::true_type, std::enable_if -#include // std::shared_ptr, std::unique_ptr -// #include "functional/cxx_optional.h" - -// #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { - /** - * This is class that tells `sqlite_orm` that type is nullable. Nullable types - * are mapped to sqlite database as `NULL` and not-nullable are mapped as `NOT NULL`. - * Default nullability status for all types is `NOT NULL`. So if you want to map - * custom type as `NULL` (for example: boost::optional) you have to create a specialization - * of `type_is_nullable` for your type and derive from `std::true_type`. - */ - template - struct type_is_nullable : std::false_type { - bool operator()(const T&) const { - return true; - } - }; - - /** - * 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 {}; + 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 + } -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct getter_field_type : polyfill::remove_cvref {}; + /** + * 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 getter_field_type : polyfill::remove_cvref {}; -#endif + return alias_column_t{std::move(field)}; + } - // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) - template - struct setter_field_type {}; + /** + * Using an object member field, create a column reference to an aliased table column. + * + * @note The object member pointer can be from a derived class without explicitly forming a column pointer. + * + * Example: + * using als = alias_u; + * select(alias_column(&User::id)) + */ + template, + polyfill::negation>>>::value, + bool> = true> + constexpr auto alias_column(F O::*field) { + using namespace ::sqlite_orm::internal; + using aliased_type = type_t; + static_assert(is_field_of_v, "Column must be from aliased table"); - template - using setter_field_type_t = typename setter_field_type::type; + using C1 = + mpl::conditional_t::value, F O::*, column_pointer>; + return alias_column_t{C1{field}}; + } - template - struct setter_field_type : setter_field_type {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Create a column reference to an aliased table column. + * + * @note An object member pointer can be from a derived class without explicitly forming a column pointer. + * + * Example: + * constexpr orm_table_alias auto als = "u"_alias.for_(); + * select(alias_column(&User::id)) + */ + template + requires(!orm_cte_moniker>) + constexpr auto alias_column(C field) { + using namespace ::sqlite_orm::internal; + using A = decltype(als); + using aliased_type = type_t; + static_assert(is_field_of_v, "Column must be from aliased table"); - template - struct setter_field_type : polyfill::remove_cvref {}; + 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}}; + } + } -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template - struct setter_field_type : polyfill::remove_cvref {}; + /** + * Create a column reference to an aliased table column. + * + * @note An object member pointer can be from a derived class without explicitly forming a column pointer. + * + * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) + * because recordset aliases are derived from `sqlite_orm::alias_tag` + * + * Example: + * constexpr auto als = "u"_alias.for_(); + * select(als->*&User::id) + */ + template + requires(!orm_cte_moniker>) + constexpr auto operator->*(const A& /*tableAlias*/, F field) { + return alias_column(std::move(field)); + } #endif - template - struct is_getter : std::false_type {}; - template - struct is_getter>> : std::true_type {}; - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter::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 internal; + using cte_moniker_t = type_t; - template - struct is_setter : std::false_type {}; - template - struct is_setter>> : std::true_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 - SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter::value; +#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_field_type : object_field_type, getter_field_type, setter_field_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 - using member_field_type_t = typename member_field_type::type; + /** + * Alias a column expression. + */ + template = true> + internal::as_t as(E expression) { + return {std::move(expression)}; + } - template - struct member_object_type {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Alias a column expression. + */ + template + auto as(E expression) { + return internal::as_t{std::move(expression)}; + } - template - struct member_object_type : polyfill::type_identity {}; + /** + * Alias a column expression. + */ + template + internal::as_t operator>>=(E expression, const A&) { + return {std::move(expression)}; + } +#else + /** + * Alias a column expression. + */ + template = true> + internal::as_t operator>>=(E expression, const A&) { + return {std::move(expression)}; + } +#endif - template - using member_object_type_t = typename member_object_type::type; + /** + * Wrap a column alias in an alias holder. + */ + template + internal::alias_holder get() { + static_assert(internal::is_column_alias_v, ""); + return {}; } -} -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); - } - - using is_transparent = int; - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto get() { + return internal::alias_holder{}; + } #endif -#if __cpp_lib_invoke >= 201411L - using std::invoke; -#else - // pointer-to-data-member+object - template, - std::enable_if_t::value, bool> = true> - decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { - return std::forward(object).*callable; - } - - // pointer-to-member-function+object - template, - std::enable_if_t::value, bool> = true> - decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { - return (std::forward(object).*callable)(std::forward(args)...); - } - - // 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 - } - } + 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 polyfill = internal::polyfill; -} + 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'>; -namespace sqlite_orm { - namespace internal { - struct negatable_t {}; +#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{}; - /** - * Inherit from this class to support arithmetic types overloading + inline namespace literals { + /** @short Create a table alias. + * + * Examples: + * constexpr orm_table_alias auto z_alias = "z"_alias.for_(); */ - struct arithmetic_t {}; + template + [[nodiscard]] consteval auto operator"" _alias() { + return internal::explode_into( + std::make_index_sequence{}); + } - /** - * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + /** @short Create a column alias. + * column_alias<'a'[, ...]> from a string literal. + * E.g. "a"_col, "b"_col */ - struct condition_t {}; + template + [[nodiscard]] consteval auto operator"" _col() { + return internal::explode_into(std::make_index_sequence{}); + } + } +#endif +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + inline namespace literals { /** - * Specialize if a type participates as an argument to overloaded operators (arithmetic, conditional, negation, chaining) + * column_alias<'1'[, ...]> from a numeric literal. + * E.g. 1_colalias, 2_colalias */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v = false; - - template - using is_operator_argument = polyfill::bool_constant>; + template + [[nodiscard]] SQLITE_ORM_CONSTEVAL auto operator"" _colalias() { + // numeric identifiers are used for automatically assigning implicit aliases to unaliased column expressions, + // which start at "1". + static_assert(std::array{Chars...}[0] > '0'); + return internal::column_alias{}; + } } +#endif } -// #include "serialize_result_type.h" +// #include "error_code.h" -// #include "functional/cxx_string_view.h" +#include +#include // std::error_code, std::system_error +#include // std::string +#include +#include // std::ostringstream +#include -// #include "cxx_core_features.h" +namespace sqlite_orm { -#if SQLITE_ORM_HAS_INCLUDE() -#include -#endif + /** @short Enables classifying sqlite error codes. -#if __cpp_lib_string_view >= 201606L -#define SQLITE_ORM_STRING_VIEW_SUPPORTED -#endif + @note We don't bother listing all possible values; + this also allows for compatibility with + 'Construction rules for enum class values (P0138R2)' + */ + enum class sqlite_errc {}; -#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED -#include // std::string -#endif + enum class orm_error_code { + not_found = 1, + type_is_not_mapped_to_storage, + trying_to_dereference_null_iterator, + too_many_tables_specified, + incorrect_set_fields_specified, + column_not_found, + table_has_no_primary_key_column, + cannot_start_a_transaction_within_a_transaction, + no_active_transaction, + incorrect_journal_mode_string, + invalid_collate_argument_enum, + failed_to_init_a_backup, + unknown_member_value, + incorrect_order, + cannot_use_default_value, + arguments_count_does_not_match, + function_not_found, + index_is_out_of_bounds, + value_is_null, + no_tables_specified, + }; +} -namespace sqlite_orm { - namespace 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 std { + template<> + struct is_error_code_enum<::sqlite_orm::sqlite_errc> : true_type {}; + + template<> + struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; } namespace sqlite_orm { - namespace internal { - - template - struct binary_operator : Ds... { - using left_type = L; - using right_type = R; + class orm_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "ORM error"; + } - left_type lhs; - right_type rhs; + std::string message(int c) const override final { + switch(static_cast(c)) { + case orm_error_code::not_found: + return "Not found"; + case orm_error_code::type_is_not_mapped_to_storage: + return "Type is not mapped to storage"; + case orm_error_code::trying_to_dereference_null_iterator: + return "Trying to dereference null iterator"; + case orm_error_code::too_many_tables_specified: + return "Too many tables specified"; + case orm_error_code::incorrect_set_fields_specified: + return "Incorrect set fields specified"; + case orm_error_code::column_not_found: + return "Column not found"; + case orm_error_code::table_has_no_primary_key_column: + return "Table has no primary key column"; + case orm_error_code::cannot_start_a_transaction_within_a_transaction: + return "Cannot start a transaction within a transaction"; + case orm_error_code::no_active_transaction: + return "No active transaction"; + case orm_error_code::invalid_collate_argument_enum: + return "Invalid collate_argument enum"; + case orm_error_code::failed_to_init_a_backup: + return "Failed to init a backup"; + case orm_error_code::unknown_member_value: + return "Unknown member value"; + case orm_error_code::incorrect_order: + return "Incorrect order"; + case orm_error_code::cannot_use_default_value: + return "The statement 'INSERT INTO * DEFAULT VALUES' can be used with only one row"; + case orm_error_code::arguments_count_does_not_match: + return "Arguments count does not match"; + case orm_error_code::function_not_found: + return "Function not found"; + case orm_error_code::index_is_out_of_bounds: + return "Index is out of bounds"; + case orm_error_code::value_is_null: + return "Value is null"; + case orm_error_code::no_tables_specified: + return "No tables specified"; + default: + return "unknown error"; + } + } + }; - binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} - }; + class sqlite_error_category : public std::error_category { + public: + const char* name() const noexcept override final { + return "SQLite error"; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_operator_v = is_base_of_template::value; + std::string message(int c) const override final { + return sqlite3_errstr(c); + } + }; - template - using is_binary_operator = polyfill::bool_constant>; + inline const orm_error_category& get_orm_error_category() { + static orm_error_category res; + return res; + } - struct conc_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 concatenation || operator - */ - template - using conc_t = binary_operator; + inline std::error_code make_error_code(sqlite_errc ev) noexcept { + return {static_cast(ev), get_sqlite_error_category()}; + } - struct add_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 addition + operator - */ - template - using add_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 sub_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 substitute - operator - */ - template - using sub_t = binary_operator; + inline std::system_error sqlite_to_system_error(int ev) { + return {sqlite_errc(ev)}; + } - struct mul_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 multiply * operator - */ - template - using mul_t = binary_operator; + [[noreturn]] inline void throw_translated_sqlite_error(int ev) { + throw sqlite_to_system_error(ev); + } - struct div_string { - serialize_result_type serialize() const { - return "/"; - } - }; + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { + throw sqlite_to_system_error(db); + } - /** - * Result of divide / operator - */ - template - using div_t = binary_operator; + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { + throw sqlite_to_system_error(sqlite3_db_handle(stmt)); + } +} - struct mod_operator_string { - serialize_result_type serialize() const { - return "%"; - } - }; +// #include "type_printer.h" - /** - * Result of mod % operator - */ - template - using mod_t = binary_operator; +#include // std::string +#include // std::shared_ptr, std::unique_ptr +#include // std::vector +// #include "functional/cxx_optional.h" - struct bitwise_shift_left_string { - serialize_result_type serialize() const { - return "<<"; - } - }; +// #include "functional/cxx_type_traits_polyfill.h" - /** - * Result of bitwise shift left << operator - */ - template - using bitwise_shift_left_t = binary_operator; +// #include "type_traits.h" - struct bitwise_shift_right_string { - serialize_result_type serialize() const { - return ">>"; - } - }; +// #include "is_std_ptr.h" - /** - * Result of bitwise shift right >> operator - */ - template - using bitwise_shift_right_t = binary_operator; +#include +#include - struct bitwise_and_string { - serialize_result_type serialize() const { - return "&"; - } - }; +namespace sqlite_orm { - /** - * Result of bitwise and & operator - */ - template - using bitwise_and_t = binary_operator; + /** + * Specialization for optional type (std::shared_ptr / std::unique_ptr). + */ + template + struct is_std_ptr : std::false_type {}; - struct bitwise_or_string { - serialize_result_type serialize() const { - return "|"; - } - }; + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::shared_ptr::element_type; - /** - * Result of bitwise or | operator - */ - template - using bitwise_or_t = binary_operator; + static std::shared_ptr make(std::remove_cv_t&& v) { + return std::make_shared(std::move(v)); + } + }; - struct bitwise_not_string { - serialize_result_type serialize() const { - return "~"; - } - }; + template + struct is_std_ptr> : std::true_type { + using element_type = typename std::unique_ptr::element_type; - /** - * Result of bitwise not ~ operator - */ - template - struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { - using argument_type = T; + static auto make(std::remove_cv_t&& v) { + return std::make_unique(std::move(v)); + } + }; +} - argument_type argument; - - bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} - }; - - struct assign_string { - serialize_result_type serialize() const { - return "="; - } - }; - - /** - * Result of assign = operator - */ - template - using assign_t = binary_operator; - - /** - * Assign operator traits. Common case - */ - template - struct is_assign_t : public std::false_type {}; - - /** - * Assign operator traits. Specialized case - */ - template - struct is_assign_t> : public std::true_type {}; - } - - /** - * Public interface for || concatenation operator. Example: `select(conc(&User::name, "@gmail.com"));` => SELECT - * name || '@gmail.com' FROM users - */ - template - internal::conc_t conc(L l, R r) { - return {std::move(l), std::move(r)}; - } - - /** - * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users - */ - template - internal::add_t add(L l, R r) { - return {std::move(l), std::move(r)}; - } +namespace sqlite_orm { /** - * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users + * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) */ - template - internal::sub_t sub(L l, R r) { - return {std::move(l), std::move(r)}; - } + template + struct type_printer {}; - /** - * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users - */ - template - internal::mul_t mul(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct integer_printer { + const std::string& print() const { + static const std::string res = "INTEGER"; + return res; + } + }; - /** - * Public interface for / operator. Example: `select(div(&User::salary, 3));` => SELECT salary / 3 FROM users - * @note Please notice that ::div function already exists in pure C standard library inside header. - * If you use `using namespace sqlite_orm` directive you an specify which `div` you call explicitly using `::div` or `sqlite_orm::div` statements. - */ - template - internal::div_t div(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct text_printer { + const std::string& print() const { + static const std::string res = "TEXT"; + return res; + } + }; - /** - * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users - */ - template - internal::mod_t mod(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct real_printer { + const std::string& print() const { + static const std::string res = "REAL"; + return res; + } + }; - template - internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct blob_printer { + const std::string& print() const { + static const std::string res = "BLOB"; + return res; + } + }; - template - internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { - return {std::move(l), std::move(r)}; - } + // Note: char, unsigned/signed char are used for storing integer values, not char values. + template + struct type_printer>, + std::is_integral>::value>> : integer_printer { + }; - template - internal::bitwise_and_t bitwise_and(L l, R r) { - return {std::move(l), std::move(r)}; - } + template + struct type_printer::value>> : real_printer {}; - template - internal::bitwise_or_t bitwise_or(L l, R r) { - return {std::move(l), std::move(r)}; - } + template + struct type_printer, + std::is_base_of, + std::is_base_of>::value>> + : text_printer {}; template - internal::bitwise_not_t bitwise_not(T t) { - return {std::move(t)}; - } + struct type_printer::value>> : type_printer {}; - template - internal::assign_t assign(L l, R r) { - return {std::move(l), std::move(r)}; - } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct type_printer>> + : type_printer {}; +#endif + template<> + struct type_printer, void> : blob_printer {}; } -#pragma once - -#include // std::tuple -#include // std::string -#include // std::unique_ptr -#include // std::is_same, std::is_member_object_pointer -#include // std::move - -// #include "../functional/cxx_universal.h" -// #include "../functional/cxx_type_traits_polyfill.h" +// #include "constraints.h" -// #include "../tuple_helper/tuple_traits.h" +#include // std::is_base_of, std::false_type, std::true_type +#include // std::system_error +#include // std::ostream +#include // std::string +#include // std::tuple -// #include "../tuple_helper/tuple_filter.h" +// #include "functional/cxx_universal.h" -// #include "../type_traits.h" +// #include "functional/cxx_type_traits_polyfill.h" -// #include "../member_traits/member_traits.h" +// #include "functional/mpl.h" -// #include "../type_is_nullable.h" +// #include "tuple_helper/same_or_void.h" -// #include "../constraints.h" +#include // std::common_type namespace sqlite_orm { - namespace internal { - struct column_identifier { + /** + * Accepts any number of arguments and evaluates a nested `type` typename as `T` if all arguments are the same, otherwise `void`. + */ + template + struct same_or_void { + using type = void; + }; - /** - * Column name. - */ - std::string name; + template + struct same_or_void { + using type = A; }; - struct empty_setter {}; + template + struct same_or_void { + using type = A; + }; - /* - * Encapsulates object member pointers that are used as column fields, - * and whose object is mapped to storage. - * - * G is a member object pointer or member function pointer - * S is a member function pointer or `empty_setter` - */ - template - struct column_field { - using member_pointer_t = G; - using setter_type = S; - using object_type = member_object_type_t; - using field_type = member_field_type_t; + template + using same_or_void_t = typename same_or_void::type; - /** - * Member pointer used to read a field value. - * If it is a object member pointer it is also used to write a field value. - */ - const member_pointer_t member_pointer; + template + struct same_or_void : same_or_void {}; - /** - * Setter member function to write a field value - */ - SQLITE_ORM_NOUNIQUEADDRESS - const setter_type setter; + template + struct common_type_of; - /** - * Simplified interface for `NOT NULL` constraint - */ - constexpr bool is_not_null() const { - return !type_is_nullable::value; - } - }; + template class Pack, class... Types> + struct common_type_of> : std::common_type {}; - /* - * Encapsulates a tuple of column constraints. + /** + * Accepts a pack of types and defines a nested `type` typename to a common type if possible, otherwise nonexistent. * - * Op... is a constraints pack, e.g. primary_key_t, unique_t etc + * @note: SFINAE friendly like `std::common_type`. */ - template - struct column_constraints { - using constraints_type = std::tuple; + template + using common_type_of_t = typename common_type_of::type; + } +} - SQLITE_ORM_NOUNIQUEADDRESS - constraints_type constraints; +// #include "tuple_helper/tuple_traits.h" - /** - * Checks whether contraints contain specified type. - */ - template class Trait> - constexpr static bool is() { - return tuple_has::value; - } +// #include "tuple_helper/tuple_filter.h" - /** - * Simplified interface for `DEFAULT` constraint - * @return string representation of default value if it exists otherwise nullptr - */ - std::unique_ptr default_value() const; - }; +// #include "type_traits.h" - /** - * Column definition. - * - * It is a composition of orthogonal information stored in different base classes. - */ - template - struct column_t : column_identifier, column_field, column_constraints { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - column_t(std::string name, G memberPointer, S setter, std::tuple op) : - column_identifier{std::move(name)}, column_field{memberPointer, setter}, - column_constraints{std::move(op)} {} -#endif - }; +// #include "collate_argument.h" - template - struct column_field_expression { - using type = void; - }; +namespace sqlite_orm { - template - struct column_field_expression, void> { - using type = typename column_t::member_pointer_t; - }; + namespace internal { - template - using column_field_expression_t = typename column_field_expression::type; + enum class collate_argument { + binary, + nocase, + rtrim, + }; + } +} - template - SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of::value; +// #include "error_code.h" - template - using is_column = polyfill::bool_constant>; +// #include "table_type_of.h" - template - using col_index_sequence_with_field_type = - filter_tuple_sequence_t::template fn, - field_type_t, - filter_tuple_sequence_t>; +// #include "type_printer.h" - template class TraitFn> - using col_index_sequence_with = filter_tuple_sequence_t::template fn, - constraints_type_t, - filter_tuple_sequence_t>; +namespace sqlite_orm { - template class TraitFn> - using col_index_sequence_excluding = filter_tuple_sequence_t::template fn, - constraints_type_t, - filter_tuple_sequence_t>; - } + namespace internal { - /** - * Factory function for a column definition from a member object pointer of the object to be mapped. - */ - template = true> - internal::column_t - make_column(std::string name, M memberPointer, Op... constraints) { - static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + enum class conflict_clause_t { + rollback, + abort, + fail, + ignore, + replace, + }; - // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, - // as this will lead to UB with Clang on MinGW! - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), memberPointer, {}, std::tuple{std::move(constraints)...}}); - } + struct primary_key_base { + enum class order_by { + unspecified, + ascending, + descending, + }; + struct { + order_by asc_option = order_by::unspecified; + conflict_clause_t conflict_clause = conflict_clause_t::rollback; + bool conflict_clause_is_on = false; + } options; + }; - /** - * Factory function for a column definition from "setter" and "getter" member function pointers of the object to be mapped. - */ - template = true, - internal::satisfies = true> - internal::column_t make_column(std::string name, S setter, G getter, Op... constraints) { - static_assert(std::is_same, internal::getter_field_type_t>::value, - "Getter and setter must get and set same data type"); - static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + template + struct primary_key_with_autoincrement : T { + using primary_key_type = T; - // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, - // as this will lead to UB with Clang on MinGW! - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), getter, setter, std::tuple{std::move(constraints)...}}); - } + const primary_key_type& as_base() const { + return *this; + } +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + primary_key_with_autoincrement(primary_key_type primary_key) : primary_key_type{primary_key} {} +#endif + }; - /** - * Factory function for a column definition from "getter" and "setter" member function pointers of the object to be mapped. - */ - template = true, - internal::satisfies = true> - internal::column_t make_column(std::string name, G getter, S setter, Op... constraints) { - static_assert(std::is_same, internal::getter_field_type_t>::value, - "Getter and setter must get and set same data type"); - static_assert(polyfill::conjunction_v...>, "Incorrect constraints pack"); + /** + * PRIMARY KEY constraint class. + * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when + * used within `make_column` function. + */ + template + struct primary_key_t : primary_key_base { + using self = primary_key_t; + using order_by = primary_key_base::order_by; + using columns_tuple = std::tuple; - // attention: do not use `std::make_tuple()` for constructing the tuple member `[[no_unique_address]] column_constraints::constraints`, - // as this will lead to UB with Clang on MinGW! - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), getter, setter, std::tuple{std::move(constraints)...}}); - } -} -#pragma once + columns_tuple columns; -#include // std::string -#include // std::enable_if, std::is_same, std::remove_const -#include // std::vector -#include // std::tuple -#include // std::move, std::forward -#include // std::stringstream + primary_key_t(columns_tuple columns) : columns(std::move(columns)) {} -// #include "functional/cxx_universal.h" + self asc() const { + auto res = *this; + res.options.asc_option = order_by::ascending; + return res; + } -// #include "functional/cxx_type_traits_polyfill.h" + self desc() const { + auto res = *this; + res.options.asc_option = order_by::descending; + return res; + } -// #include "is_base_of_template.h" + primary_key_with_autoincrement autoincrement() const { + return {*this}; + } -// #include "type_traits.h" + self on_conflict_rollback() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::rollback; + return res; + } -// #include "collate_argument.h" + self on_conflict_abort() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::abort; + return res; + } -// #include "constraints.h" + self on_conflict_fail() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::fail; + return res; + } -// #include "optional_container.h" + self on_conflict_ignore() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::ignore; + return res; + } -namespace sqlite_orm { + self on_conflict_replace() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::replace; + return res; + } + }; - namespace internal { + struct unique_base { + operator std::string() const { + return "UNIQUE"; + } + }; /** - * This is a cute class which allows storing something or nothing - * depending on template argument. Useful for optional class members + * UNIQUE constraint class. */ - template - struct optional_container { - using type = T; + template + struct unique_t : unique_base { + using columns_tuple = std::tuple; - type field; + columns_tuple columns; - template - void apply(const L& l) const { - l(this->field); - } + unique_t(columns_tuple columns_) : columns(std::move(columns_)) {} }; - template<> - struct optional_container { - using type = void; + struct unindexed_t {}; - template - void apply(const L&) const { - //.. - } - }; - } -} + template + struct prefix_t { + using value_type = T; -// #include "serializer_context.h" + value_type value; + }; -namespace sqlite_orm { - - namespace internal { + template + struct tokenize_t { + using value_type = T; - struct serializer_context_base { - bool replace_bindable_with_question = false; - bool skip_table_name = true; - bool use_parentheses = true; - bool fts5_columns = false; + value_type value; }; - template - struct serializer_context : serializer_context_base { - using db_objects_type = DBOs; + template + struct content_t { + using value_type = T; - const db_objects_type& db_objects; + value_type value; + }; - serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} + template + struct table_content_t { + using mapped_type = T; }; - template - struct serializer_context_builder { - using storage_type = S; - using db_objects_type = typename storage_type::db_objects_type; + /** + * DEFAULT constraint class. + * T is a value type. + */ + template + struct default_t { + using value_type = T; - serializer_context_builder(const storage_type& storage_) : storage{storage_} {} + value_type value; - serializer_context operator()() const { - return {obtain_db_objects(this->storage)}; + operator std::string() const { + return "DEFAULT"; } - - const storage_type& storage; }; - } -} +#if SQLITE_VERSION_NUMBER >= 3006019 + /** + * FOREIGN KEY constraint class. + * Cs are columns which has foreign key + * Rs are column which C references to + * Available in SQLite 3.6.19 or higher + */ -// #include "serialize_result_type.h" + template + struct foreign_key_t; -// #include "tags.h" + enum class foreign_key_action { + none, // not specified + no_action, + restrict_, + set_null, + set_default, + cascade, + }; -// #include "table_reference.h" + inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { + switch(action) { + case foreign_key_action::no_action: + os << "NO ACTION"; + break; + case foreign_key_action::restrict_: + os << "RESTRICT"; + break; + case foreign_key_action::set_null: + os << "SET NULL"; + break; + case foreign_key_action::set_default: + os << "SET DEFAULT"; + break; + case foreign_key_action::cascade: + os << "CASCADE"; + break; + case foreign_key_action::none: + break; + } + return os; + } -#include // std::remove_const, std::type_identity -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED -#include -#endif + struct on_update_delete_base { + const bool update; // true if update and false if delete -// #include "functional/cxx_type_traits_polyfill.h" + operator std::string() const { + if(this->update) { + return "ON UPDATE"; + } else { + return "ON DELETE"; + } + } + }; -namespace sqlite_orm { - namespace internal { - /* - * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. + /** + * F - foreign key class */ - template - struct table_reference : polyfill::type_identity {}; - - template - struct decay_table_ref : std::remove_const {}; - template - struct decay_table_ref> : polyfill::type_identity {}; - template - struct decay_table_ref> : polyfill::type_identity {}; + template + struct on_update_delete_t : on_update_delete_base { + using foreign_key_type = F; - template - using decay_table_ref_t = typename decay_table_ref::type; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - using auto_decay_table_ref_t = typename decay_table_ref::type; -#endif + const foreign_key_type& fk; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_table_reference_v = - polyfill::is_specialization_of_v, table_reference>; + on_update_delete_t(decltype(fk) fk_, decltype(update) update_, foreign_key_action action_) : + on_update_delete_base{update_}, fk(fk_), _action(action_) {} - template - struct is_table_reference : polyfill::bool_constant> {}; - } + foreign_key_action _action = foreign_key_action::none; -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. - * - * A concrete table reference has the following traits: - * - specialization of `table_reference`, whose `type` typename references a mapped object. - */ - template - concept orm_table_reference = polyfill::is_specialization_of_v, internal::table_reference>; -#endif -} + foreign_key_type no_action() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::no_action; + } else { + res.on_delete._action = foreign_key_action::no_action; + } + return res; + } -// #include "alias_traits.h" + foreign_key_type restrict_() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::restrict_; + } else { + res.on_delete._action = foreign_key_action::restrict_; + } + return res; + } -#include // std::is_base_of, std::is_same -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include -#endif + foreign_key_type set_null() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::set_null; + } else { + res.on_delete._action = foreign_key_action::set_null; + } + return res; + } -// #include "functional/cxx_universal.h" + foreign_key_type set_default() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::set_default; + } else { + res.on_delete._action = foreign_key_action::set_default; + } + return res; + } -// #include "functional/cxx_type_traits_polyfill.h" + foreign_key_type cascade() const { + auto res = this->fk; + if(update) { + res.on_update._action = foreign_key_action::cascade; + } else { + res.on_delete._action = foreign_key_action::cascade; + } + return res; + } -// #include "type_traits.h" + operator bool() const { + return this->_action != foreign_key_action::none; + } + }; -// #include "table_reference.h" + template + bool operator==(const on_update_delete_t& lhs, const on_update_delete_t& rhs) { + return lhs._action == rhs._action; + } -namespace sqlite_orm { + template + struct foreign_key_t, std::tuple> { + using columns_type = std::tuple; + using references_type = std::tuple; + using self = foreign_key_t; - /** @short Base class for a custom table alias, column alias or expression alias. - */ - struct alias_tag {}; + /** + * Holds obect type of all referenced columns. + */ + using target_type = same_or_void_t...>; - namespace internal { + /** + * Holds obect type of all source columns. + */ + using source_type = same_or_void_t...>; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_alias_v = std::is_base_of::value; + columns_type columns; + references_type references; - template - struct is_alias : polyfill::bool_constant> {}; + on_update_delete_t on_update; + on_update_delete_t on_delete; - /** @short Alias of a column in a record set, see `orm_column_alias`. - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_column_alias_v = - polyfill::conjunction, polyfill::negation>>::value; + static_assert(std::tuple_size::value == std::tuple_size::value, + "Columns size must be equal to references tuple"); + static_assert(!std::is_same::value, "All references must have the same type"); - template - struct is_column_alias : is_alias {}; + foreign_key_t(columns_type columns_, references_type references_) : + columns(std::move(columns_)), references(std::move(references_)), + on_update(*this, true, foreign_key_action::none), on_delete(*this, false, foreign_key_action::none) {} - /** @short Alias of any type of record set, see `orm_recordset_alias`. - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_recordset_alias_v = - polyfill::conjunction, polyfill::is_detected>::value; + foreign_key_t(const self& other) : + columns(other.columns), references(other.references), on_update(*this, true, other.on_update._action), + on_delete(*this, false, other.on_delete._action) {} - template - struct is_recordset_alias : polyfill::bool_constant> {}; + self& operator=(const self& other) { + this->columns = other.columns; + this->references = other.references; + this->on_update = {*this, true, other.on_update._action}; + this->on_delete = {*this, false, other.on_delete._action}; + return *this; + } + }; - /** @short Alias of a concrete table, see `orm_table_alias`. + template + bool operator==(const foreign_key_t& lhs, const foreign_key_t& rhs) { + return lhs.columns == rhs.columns && lhs.references == rhs.references && lhs.on_update == rhs.on_update && + lhs.on_delete == rhs.on_delete; + } + + /** + * Cs can be a class member pointer, a getter function member pointer or setter + * func member pointer + * Available in SQLite 3.6.19 or higher */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction< - is_recordset_alias, - polyfill::negation, std::remove_const_t>>>::value; + template + struct foreign_key_intermediate_t { + using tuple_type = std::tuple; - template - struct is_table_alias : polyfill::bool_constant> {}; + tuple_type columns; - /** @short Moniker of a CTE, see `orm_cte_moniker`. - */ - template - SQLITE_ORM_INLINE_VAR constexpr bool is_cte_moniker_v = -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - polyfill::conjunction_v, - std::is_same, std::remove_const_t>>; -#else - false; + template + foreign_key_t, std::tuple> references(Rs... refs) { + return {std::move(this->columns), {std::forward(refs)...}}; + } + }; #endif - template - using is_cte_moniker = polyfill::bool_constant>; - } - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - concept orm_alias = std::derived_from; + struct collate_constraint_t { + collate_argument argument = collate_argument::binary; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + collate_constraint_t(collate_argument argument) : argument{argument} {} +#endif - /** @short Specifies that a type is an alias of a column in a record set. - * - * A column alias has the following traits: - * - is derived from `alias_tag` - * - must not have a nested `type` typename - */ - template - concept orm_column_alias = (orm_alias && !orm_names_type); + operator std::string() const { + return "COLLATE " + this->string_from_collate_argument(this->argument); + } - /** @short Specifies that a type is an alias of any type of record set. - * - * A record set alias has the following traits: - * - is derived from `alias_tag`. - * - has a nested `type` typename, which refers to a mapped object. - */ - template - concept orm_recordset_alias = (orm_alias && orm_names_type); + static std::string string_from_collate_argument(collate_argument argument) { + switch(argument) { + case collate_argument::binary: + return "BINARY"; + case collate_argument::nocase: + return "NOCASE"; + case collate_argument::rtrim: + return "RTRIM"; + } + throw std::system_error{orm_error_code::invalid_collate_argument_enum}; + } + }; - /** @short Specifies that a type is an alias of a concrete table. - * - * A concrete table alias has the following traits: - * - is derived from `alias_tag`. - * - has a `type` typename, which refers to another mapped object (i.e. doesn't refer to itself). - */ - template - concept orm_table_alias = (orm_recordset_alias && !std::same_as>); + template + struct check_t { + using expression_type = T; - /** @short Moniker of a CTE. - * - * A CTE moniker has the following traits: - * - is derived from `alias_tag`. - * - has a `type` typename, which refers to itself. - */ - template - concept orm_cte_moniker = (orm_recordset_alias && std::same_as>); + expression_type expression; + }; - /** @short Specifies that a type refers to a mapped table (possibly aliased). - */ - template - concept orm_refers_to_table = (orm_table_reference || orm_table_alias); + struct basic_generated_always { + enum class storage_type { + not_specified, + virtual_, + stored, + }; - /** @short Specifies that a type refers to a recordset. - */ - template - concept orm_refers_to_recordset = (orm_table_reference || orm_recordset_alias); +#if SQLITE_VERSION_NUMBER >= 3031000 + bool full = true; + storage_type storage = storage_type::not_specified; +#endif - /** @short Specifies that a type is a mapped recordset (table reference). - */ - template - concept orm_mapped_recordset = (orm_table_reference || orm_cte_moniker); +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} #endif -} + }; -// #include "expression.h" +#if SQLITE_VERSION_NUMBER >= 3031000 + template + struct generated_always_t : basic_generated_always { + using expression_type = T; -#include -#include // std::enable_if -#include // std::move, std::forward, std::declval -// #include "functional/cxx_optional.h" + expression_type expression; -// #include "functional/cxx_universal.h" + generated_always_t(expression_type expression_, bool full, storage_type storage) : + basic_generated_always{full, storage}, expression(std::move(expression_)) {} -// #include "functional/cxx_type_traits_polyfill.h" + generated_always_t virtual_() { + return {std::move(this->expression), this->full, storage_type::virtual_}; + } -// #include "tags.h" + generated_always_t stored() { + return {std::move(this->expression), this->full, storage_type::stored}; + } + }; +#endif -namespace sqlite_orm { + struct null_t {}; - namespace internal { + struct not_null_t {}; + } - template - struct in_t; + namespace internal { - template - struct and_condition_t; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = +#if SQLITE_VERSION_NUMBER >= 3006019 + polyfill::is_specialization_of::value; +#else + false; +#endif - template - struct or_condition_t; + template + struct is_foreign_key : polyfill::bool_constant> {}; - /** - * Result of c(...) function. Has operator= overloaded which returns assign_t - */ template - struct expression_t { - T value; + SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = std::is_base_of::value; - template - assign_t operator=(R r) const { - return {this->value, std::move(r)}; - } + template + struct is_primary_key : polyfill::bool_constant> {}; - assign_t operator=(nullptr_t) const { - return {this->value, nullptr}; - } -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - assign_t operator=(std::nullopt_t) const { - return {this->value, std::nullopt}; - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = +#if SQLITE_VERSION_NUMBER >= 3031000 + polyfill::is_specialization_of::value; +#else + false; #endif - template - in_t in(Args... args) const { - return {this->value, {std::forward(args)...}, false}; - } - template - in_t not_in(Args... args) const { - return {this->value, {std::forward(args)...}, true}; - } + template + struct is_generated_always : polyfill::bool_constant> {}; - template - and_condition_t and_(R right) const { - return {this->value, std::move(right)}; - } + /** + * PRIMARY KEY INSERTABLE traits. + */ + template + struct is_primary_key_insertable + : polyfill::disjunction< + mpl::invoke_t, + check_if_has_template>, + constraints_type_t>, + std::is_base_of>>> { - template - or_condition_t or_(R right) const { - return {this->value, std::move(right)}; - } + static_assert(tuple_has, is_primary_key>::value, + "an unexpected type was passed"); }; template - SQLITE_ORM_INLINE_VAR constexpr bool - is_operator_argument_v::value>> = true; + 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 - T get_from_expression(T value) { - return std::move(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 - T get_from_expression(expression_t expression) { - return std::move(expression.value); - } - - template - using unwrap_expression_t = decltype(get_from_expression(std::declval())); + 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 /** - * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or - * `storage.update(set(c(&User::name) = "Dua Lipa")); + * FOREIGN KEY constraint construction function that takes member pointer as argument + * Available in SQLite 3.6.19 or higher */ - template - internal::expression_t c(T value) { - return {std::move(value)}; + template + internal::foreign_key_intermediate_t foreign_key(Cs... columns) { + return {{std::forward(columns)...}}; } -} - -// #include "column_pointer.h" - -#include // std::enable_if, std::is_convertible -#include // std::move - -// #include "functional/cxx_core_features.h" - -// #include "functional/cxx_type_traits_polyfill.h" - -// #include "type_traits.h" - -// #include "table_reference.h" - -// #include "alias_traits.h" - -// #include "tags.h" - -namespace sqlite_orm { - namespace internal { - /** - * This class is used to store explicit mapped type T and its column descriptor (member pointer/getter/setter). - * Is useful when mapped type is derived from other type and base class has members mapped to a storage. - */ - template - struct column_pointer { - using type = T; - using field_type = F; - - field_type field; - }; - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_column_pointer_v = - polyfill::is_specialization_of::value; - - template - struct is_column_pointer : polyfill::bool_constant> {}; +#endif - template - SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v::value>> = - true; + /** + * UNIQUE table constraint builder function. + */ + template + internal::unique_t unique(Args... args) { + return {{std::forward(args)...}}; + } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template - struct alias_holder; -#endif + /** + * UNIQUE column constraint builder function. + */ + inline internal::unique_t<> unique() { + return {{}}; } +#if SQLITE_VERSION_NUMBER >= 3009000 /** - * 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)); + * UNINDEXED column constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#the_unindexed_column_option */ - template = true> - constexpr internal::column_pointer column(F Base::*field) { - static_assert(std::is_convertible::value, "Field must be from derived class"); - return {field}; + inline internal::unindexed_t unindexed() { + return {}; } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Explicitly refer to a column. + * prefix=N table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#prefix_indexes */ - template - constexpr auto column(F O::*field) { - return column>(field); + template + internal::prefix_t prefix(T value) { + return {std::move(value)}; } - // 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); - } + /** + * tokenize='...'' table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#tokenizers + */ + template + internal::tokenize_t tokenize(T value) { + return {std::move(value)}; } /** - * Make a table reference. + * content='' table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#contentless_tables */ - template - requires(!orm_recordset_alias) - consteval internal::table_reference column() { - return {}; + template + internal::content_t content(T value) { + return {std::move(value)}; } /** - * Make a table reference. + * content='table' table constraint builder function. Used in FTS virtual tables. + * + * https://www.sqlite.org/fts5.html#external_content_tables */ - template - requires(!orm_recordset_alias) - consteval internal::table_reference c() { + template + internal::table_content_t content() { return {}; } #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()))); + * PRIMARY KEY table constraint builder function. */ - template = true> - constexpr auto column(F field) { - using namespace ::sqlite_orm::internal; - - static_assert(is_cte_moniker_v, "`Moniker' must be a CTE moniker"); - - if constexpr(polyfill::is_specialization_of_v) { - static_assert(is_column_alias_v>); - return column_pointer{{}}; - } else if constexpr(is_column_alias_v) { - return column_pointer>{{}}; - } else { - return column_pointer{std::move(field)}; - } - (void)field; + template + internal::primary_key_t primary_key(Cs... cs) { + return {{std::forward(cs)...}}; } -#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 column constraint builder function. */ - template - constexpr auto column(F field) { - using Moniker = std::remove_const_t; - return column(std::forward(field)); + inline internal::primary_key_t<> primary_key() { + return {{}}; } - /** - * Explicitly refer to a column mapped into a CTE or subquery. - * - * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) - * because recordset aliases are derived from `sqlite_orm::alias_tag` - * - * Example: - * struct Object { ... }; - * using cte_1 = decltype(1_ctealias); - * storage.with(cte()(select(&Object::id)), select(1_ctealias->*&Object::id)); - * storage.with(cte()(select(&Object::id)), select(1_ctealias->*1_colalias)); - */ - template - 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)}; } -#endif -#endif -} -// #include "tags.h" -// #include "type_printer.h" + inline internal::collate_constraint_t collate_nocase() { + return {internal::collate_argument::nocase}; + } -// #include "literal.h" + inline internal::collate_constraint_t collate_binary() { + return {internal::collate_argument::binary}; + } -namespace sqlite_orm { - namespace internal { + inline internal::collate_constraint_t collate_rtrim() { + return {internal::collate_argument::rtrim}; + } - /* - * Protect an otherwise bindable element so that it is always serialized as a literal value. - */ - template - struct literal_holder { - using type = T; + template + internal::check_t check(T t) { + return {std::move(t)}; + } - type value; - }; + 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"; - } - }; - - /** - * Stores LIMIT/OFFSET info - */ - template - struct limit_t : limit_string { - T lim; - optional_container off; - - limit_t() = default; +// #include "functional/cxx_universal.h" +// ::nullptr_t +// #include "functional/cxx_type_traits_polyfill.h" - limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} +// #include "is_std_ptr.h" - limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} - }; +// #include "type_traits.h" - template - struct is_limit : std::false_type {}; +namespace sqlite_orm { - template - struct is_limit> : std::true_type {}; + /** + * Is used to print members mapped to objects in storage_t::dump member function. + * Other developers can create own specialization to map custom types + */ + template + struct field_printer; - /** - * Stores OFFSET only info + namespace internal { + /* + * Implementation note: the technique of indirect expression testing is because + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. + * It must also be a type that differs from those for `is_preparable_v`, `is_bindable_v`. */ - template - struct offset_t { - T off; - }; + template + struct indirectly_test_printable; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v = false; template - using is_offset = polyfill::is_specialization_of; + SQLITE_ORM_INLINE_VAR constexpr bool + is_printable_v{})>>> = true; - /** - * Collated something - */ template - struct collate_t : public condition_t { - T expr; - collate_argument argument; - - collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} + struct is_printable : polyfill::bool_constant> {}; + } - operator std::string() const { - return collate_constraint_t{this->argument}; - } - }; + template + struct field_printer> { + std::string operator()(const T& t) const { + std::stringstream ss; + ss << t; + return ss.str(); + } + }; - struct named_collate_base { - std::string name; + /** + * Upgrade to integer is required when using unsigned char(uint8_t) + */ + template<> + struct field_printer { + std::string operator()(const unsigned char& t) const { + std::stringstream ss; + ss << +t; + return ss.str(); + } + }; - operator std::string() const { - return "COLLATE " + this->name; - } - }; + /** + * Upgrade to integer is required when using signed char(int8_t) + */ + template<> + struct field_printer { + std::string operator()(const signed char& t) const { + std::stringstream ss; + ss << +t; + return ss.str(); + } + }; - /** - * Collated something with custom collate function - */ - template - struct named_collate : named_collate_base { - T expr; + /** + * char is neither signed char nor unsigned char so it has its own specialization + */ + template<> + struct field_printer { + std::string operator()(const char& t) const { + std::stringstream ss; + ss << +t; + return ss.str(); + } + }; - named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} - }; + template + struct field_printer> { + std::string operator()(std::string string) const { + return string; + } + }; - struct negated_condition_string { - operator std::string() const { - return "NOT"; + template<> + struct field_printer, void> { + std::string operator()(const std::vector& t) const { + std::stringstream ss; + ss << std::hex; + for(auto c: t) { + ss << c; } - }; - - /** - * Result of not operator - */ - template - struct negated_condition_t : condition_t, negated_condition_string { - C c; + return ss.str(); + } + }; +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring (UTF-16 assumed). + */ + template + struct field_printer> { + std::string operator()(const std::wstring& wideString) const { + std::wstring_convert> converter; + return converter.to_bytes(wideString); + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT + template<> + struct field_printer { + std::string operator()(const nullptr_t&) const { + return "NULL"; + } + }; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template<> + struct field_printer { + std::string operator()(const std::nullopt_t&) const { + return "NULL"; + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct field_printer, + internal::is_printable>>::value>> { + using unqualified_type = std::remove_cv_t; - negated_condition_t(C c_) : c(std::move(c_)) {} - }; + std::string operator()(const T& t) const { + if(t) { + return field_printer()(*t); + } else { + return field_printer{}(nullptr); + } + } + }; - /** - * Base class for binary conditions - * L is left argument type - * R is right argument type - * S is 'string' class (a class which has cast to `std::string` operator) - * Res is result type - */ - template - struct binary_condition : condition_t, S { - using left_type = L; - using right_type = R; - using result_type = Res; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct field_printer< + T, + std::enable_if_t, + internal::is_printable>>>> { + using unqualified_type = std::remove_cv_t; - left_type lhs; - right_type rhs; + std::string operator()(const T& t) const { + if(t.has_value()) { + return field_printer()(*t); + } else { + return field_printer{}(std::nullopt); + } + } + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +} - binary_condition() = default; +// #include "rowid.h" - binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} - }; +#include // std::string - template - SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; +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; - - 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 add_t = binary_operator; - struct greater_or_equal_string { + struct sub_string { serialize_result_type serialize() const { - return ">="; + return "-"; } }; /** - * >= operator object. + * Result of substitute - operator */ template - struct greater_or_equal_t : binary_condition, negatable_t { - using self = greater_or_equal_t; - - using binary_condition::binary_condition; + using sub_t = binary_operator; - collate_t collate_binary() const { - return {*this, collate_argument::binary}; + struct mul_string { + serialize_result_type serialize() const { + return "*"; } + }; - collate_t collate_nocase() const { - return {*this, collate_argument::nocase}; - } + /** + * Result of multiply * operator + */ + template + using mul_t = binary_operator; - collate_t collate_rtrim() const { - return {*this, collate_argument::rtrim}; + struct div_string { + serialize_result_type serialize() const { + return "/"; } }; - struct less_than_string { + /** + * Result of divide / operator + */ + template + using div_t = binary_operator; + + struct mod_operator_string { serialize_result_type serialize() const { - return "<"; + return "%"; } }; /** - * < operator object. + * Result of mod % operator */ template - struct less_than_t : binary_condition, negatable_t { - using self = less_than_t; + using mod_t = binary_operator; - using binary_condition::binary_condition; - - collate_t collate_binary() const { - return {*this, collate_argument::binary}; - } - - collate_t collate_nocase() const { - return {*this, collate_argument::nocase}; - } - - collate_t collate_rtrim() const { - return {*this, collate_argument::rtrim}; - } - }; - - struct less_or_equal_string { + struct bitwise_shift_left_string { serialize_result_type serialize() const { - return "<="; + return "<<"; } }; /** - * <= operator object. + * Result of bitwise shift left << operator */ template - struct less_or_equal_t : binary_condition, negatable_t { - using self = less_or_equal_t; - - using binary_condition::binary_condition; - - collate_t collate_binary() const { - return {*this, collate_argument::binary}; - } - - collate_t collate_nocase() const { - return {*this, collate_argument::nocase}; - } + using bitwise_shift_left_t = binary_operator; - collate_t collate_rtrim() const { - return {*this, collate_argument::rtrim}; + struct bitwise_shift_right_string { + serialize_result_type serialize() const { + return ">>"; } }; - struct in_base { - bool negative = false; // used in not_in + /** + * Result of bitwise shift right >> operator + */ + template + using bitwise_shift_right_t = binary_operator; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - in_base(bool negative) : negative{negative} {} -#endif + struct bitwise_and_string { + serialize_result_type serialize() const { + return "&"; + } }; /** - * IN operator object. + * Result of bitwise and & operator */ - template - struct dynamic_in_t : condition_t, in_base, negatable_t { - using self = dynamic_in_t; - - L left; // left expression - A argument; // in arg + template + using bitwise_and_t = binary_operator; - dynamic_in_t(L left_, A argument_, bool negative_) : - in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + struct bitwise_or_string { + serialize_result_type serialize() const { + return "|"; + } }; - template - struct in_t : condition_t, in_base, negatable_t { - L left; - std::tuple argument; - - in_t(L left_, decltype(argument) argument_, bool negative_) : - in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} - }; + /** + * Result of bitwise or | operator + */ + template + using bitwise_or_t = binary_operator; - struct is_null_string { - operator std::string() const { - return "IS NULL"; + struct bitwise_not_string { + serialize_result_type serialize() const { + return "~"; } }; /** - * IS NULL operator object. + * Result of bitwise not ~ operator */ template - struct is_null_t : is_null_string, negatable_t { - using self = is_null_t; + struct bitwise_not_t : bitwise_not_string, arithmetic_t, negatable_t { + using argument_type = T; - T t; + argument_type argument; - is_null_t(T t_) : t(std::move(t_)) {} + bitwise_not_t(argument_type argument_) : argument(std::move(argument_)) {} }; - struct is_not_null_string { - operator std::string() const { - return "IS NOT NULL"; + struct assign_string { + serialize_result_type serialize() const { + return "="; } }; /** - * IS NOT NULL operator object. + * Result of assign = operator + */ + template + using assign_t = binary_operator; + + /** + * Assign operator traits. Common case */ template - struct is_not_null_t : is_not_null_string, negatable_t { - using self = is_not_null_t; + struct is_assign_t : public std::false_type {}; - T t; + /** + * Assign operator traits. Specialized case + */ + template + struct is_assign_t> : public std::true_type {}; + } - is_not_null_t(T t_) : t(std::move(t_)) {} - }; + /** + * 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)}; + } - struct order_by_base { - int asc_desc = 0; // 1: asc, -1: desc - std::string _collate_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)}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - order_by_base() = default; + /** + * 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)}; + } - order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : - asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} -#endif - }; + /** + * 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 order_by_string { - operator std::string() const { - return "ORDER BY"; - } - }; + /** + * 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)}; + } - /** - * ORDER BY argument holder. - */ - template - struct order_by_t : order_by_base, order_by_string { - using expression_type = O; - using self = order_by_t; + /** + * 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)}; + } - expression_type expression; + template + internal::bitwise_shift_left_t bitwise_shift_left(L l, R r) { + return {std::move(l), std::move(r)}; + } - order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} + template + internal::bitwise_shift_right_t bitwise_shift_right(L l, R r) { + return {std::move(l), std::move(r)}; + } - self asc() const { - auto res = *this; - res.asc_desc = 1; - return res; - } + template + internal::bitwise_and_t bitwise_and(L l, R r) { + return {std::move(l), std::move(r)}; + } - self desc() const { - auto res = *this; - res.asc_desc = -1; - return res; - } + template + internal::bitwise_or_t bitwise_or(L l, R r) { + return {std::move(l), std::move(r)}; + } - self collate_binary() const { - auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); - return res; - } + template + internal::bitwise_not_t bitwise_not(T t) { + return {std::move(t)}; + } - self collate_nocase() const { - auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); - return res; - } + template + internal::assign_t assign(L l, R r) { + return {std::move(l), std::move(r)}; + } +} - self collate_rtrim() const { - auto res = *this; - res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); - return res; - } +// #include "select_constraints.h" - self collate(std::string name) const { - auto res = *this; - res._collate_argument = std::move(name); - return res; - } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +#include +#endif +#include // std::remove_cvref, std::is_convertible, std::is_same, std::is_member_pointer +#include // std::string +#include // std::move +#include // std::tuple, std::get, std::tuple_size +// #include "functional/cxx_optional.h" - template - self collate() const { - std::stringstream ss; - ss << C::name() << std::flush; - return this->collate(ss.str()); - } - }; +// #include "functional/cxx_universal.h" +// ::size_t +// #include "functional/cxx_type_traits_polyfill.h" - /** - * ORDER BY pack holder. - */ - template - struct multi_order_by_t : order_by_string { - using args_type = std::tuple; +// #include "is_base_of_template.h" - args_type args; +// #include "tuple_helper/tuple_traits.h" - multi_order_by_t(args_type args_) : args{std::move(args_)} {} - }; +// #include "tuple_helper/tuple_transformer.h" - struct dynamic_order_by_entry_t : order_by_base { - std::string name; +// #include "tuple_helper/tuple_iteration.h" - dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : - order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} - }; +// #include "optional_container.h" + +namespace sqlite_orm { + + namespace internal { /** - * C - serializer context class + * This is a cute class which allows storing something or nothing + * depending on template argument. Useful for optional class members */ - template - struct dynamic_order_by_t : order_by_string { - using context_t = C; - using entry_t = dynamic_order_by_entry_t; - using const_iterator = typename std::vector::const_iterator; - - dynamic_order_by_t(const context_t& context_) : context(context_) {} + template + struct optional_container { + using type = T; - 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)); - } + type field; - const_iterator begin() const { - return this->entries.begin(); + template + void apply(const L& l) const { + l(this->field); } + }; - const_iterator end() const { - return this->entries.end(); - } + template<> + struct optional_container { + using type = void; - void clear() { - this->entries.clear(); + template + void apply(const L&) const { + //.. } - - 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; +// #include "ast/where.h" - template - struct is_order_by : polyfill::bool_constant> {}; +#include // std::false_type, std::true_type +#include // std::move - struct between_string { - operator std::string() const { - return "BETWEEN"; - } - }; +// #include "../functional/cxx_universal.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 "../functional/cxx_type_traits_polyfill.h" - expression_type expr; - lower_type b1; - upper_type b2; +// #include "../serialize_result_type.h" - between_t(expression_type expr_, lower_type b1_, upper_type b2_) : - expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} - }; +namespace sqlite_orm { + namespace internal { - struct like_string { - operator std::string() const { - return "LIKE"; + struct where_string { + serialize_result_type serialize() const { + return "WHERE"; } }; /** - * LIKE operator object. + * 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 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 + template + struct where_t : where_string { + using expression_type = C; - like_t(arg_t arg_, pattern_t pattern_, optional_container escape_) : - arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} + expression_type expression; - template - like_t escape(C c) const { - optional_container newArg3{std::move(c)}; - return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; - } + where_t(expression_type expression_) : expression(std::move(expression_)) {} }; - struct glob_string { - operator std::string() const { - return "GLOB"; - } - }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of::value; - template - struct glob_t : condition_t, glob_string, negatable_t { - using self = glob_t; - using arg_t = A; - using pattern_t = T; + template + struct is_where : polyfill::bool_constant> {}; + } - arg_t arg; - pattern_t pattern; + /** + * 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)}; + } +} - glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} - }; +// #include "ast/group_by.h" - struct cross_join_string { - operator std::string() const { - return "CROSS JOIN"; - } - }; +#include // std::tuple, std::make_tuple +#include // std::true_type, std::false_type +#include // std::forward, std::move - /** - * CROSS JOIN holder. - * T is joined type which represents any mapped table. - */ - template - struct cross_join_t : cross_join_string { - using type = T; - }; +// #include "../functional/cxx_type_traits_polyfill.h" - struct natural_join_string { - operator std::string() const { - return "NATURAL JOIN"; - } - }; +namespace sqlite_orm { + namespace internal { - /** - * NATURAL JOIN holder. - * T is joined type which represents any mapped table. - */ - template - struct natural_join_t : natural_join_string { - using type = T; - }; + template + struct group_by_with_having { + using args_type = std::tuple; + using expression_type = T; - struct left_join_string { - operator std::string() const { - return "LEFT JOIN"; - } + args_type args; + expression_type expression; }; /** - * LEFT JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. + * GROUP BY pack holder. */ - template - struct left_join_t : left_join_string { - using type = T; - using on_type = O; - - on_type constraint; + template + struct group_by_t { + using args_type = std::tuple; - left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} - }; + args_type args; - struct join_string { - operator std::string() const { - return "JOIN"; + template + group_by_with_having having(T expression) { + return {std::move(this->args), std::move(expression)}; } }; - /** - * 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; + template + using is_group_by = polyfill::disjunction, + polyfill::is_specialization_of>; + } - on_type constraint; + /** + * GROUP BY column. + * Example: storage.get_all(group_by(&Employee::name)) + */ + template + internal::group_by_t group_by(Args... args) { + return {{std::forward(args)...}}; + } +} - join_t(on_type constraint_) : constraint(std::move(constraint_)) {} - }; +// #include "core_functions.h" - struct left_outer_join_string { - operator std::string() const { - return "LEFT OUTER JOIN"; - } - }; +#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 - /** - * 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; +// #include "functional/cxx_type_traits_polyfill.h" - on_type constraint; +// #include "functional/mpl/conditional.h" - left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} - }; +// #include "is_base_of_template.h" - struct on_string { - operator std::string() const { - return "ON"; - } - }; +// #include "tuple_helper/tuple_traits.h" - /** - * 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; +// #include "conditions.h" - arg_type arg; +#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 - on_t(arg_type arg_) : arg(std::move(arg_)) {} - }; +// #include "functional/cxx_universal.h" - /** - * USING argument holder. - */ - template - struct using_t { - column_pointer column; +// #include "functional/cxx_type_traits_polyfill.h" - operator std::string() const { - return "USING"; - } - }; +// #include "is_base_of_template.h" - struct inner_join_string { - operator std::string() const { - return "INNER JOIN"; - } +// #include "type_traits.h" + +// #include "collate_argument.h" + +// #include "constraints.h" + +// #include "optional_container.h" + +// #include "serializer_context.h" + +namespace sqlite_orm { + + namespace internal { + + struct serializer_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; + bool fts5_columns = false; }; - /** - * INNER JOIN holder. - * T is joined type which represents any mapped table. - * O is on(...) argument type. - */ - template - struct inner_join_t : inner_join_string { - using type = T; - using on_type = O; + template + struct serializer_context : serializer_context_base { + using db_objects_type = DBOs; - on_type constraint; + const db_objects_type& db_objects; - inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} + serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} }; - struct cast_string { - operator std::string() const { - return "CAST"; + template + struct serializer_context_builder { + using storage_type = S; + using db_objects_type = typename storage_type::db_objects_type; + + serializer_context_builder(const storage_type& storage_) : storage{storage_} {} + + serializer_context operator()() const { + return {obtain_db_objects(this->storage)}; } + + const storage_type& storage; }; + } + +} + +// #include "serialize_result_type.h" + +// #include "tags.h" + +// #include "table_reference.h" + +// #include "alias_traits.h" + +// #include "expression.h" + +#include +#include // std::enable_if +#include // std::move, std::forward, std::declval +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" +// ::nullptr_t +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tags.h" + +// #include "operators.h" + +namespace sqlite_orm { + + namespace internal { + + template + struct in_t; + + template + struct and_condition_t; + + template + struct or_condition_t; /** - * CAST holder. - * T is a type to cast to - * E is an expression type - * Example: cast(&User::id) + * Result of c(...) function. Has operator= overloaded which returns assign_t */ - template - struct cast_t : cast_string { - using to_type = T; - using expression_type = E; + template + struct expression_t { + T value; - expression_type expression; + template + assign_t operator=(R r) const { + return {this->value, std::move(r)}; + } - cast_t(expression_type expression_) : expression(std::move(expression_)) {} - }; + assign_t operator=(nullptr_t) const { + return {this->value, nullptr}; + } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + assign_t operator=(std::nullopt_t) const { + return {this->value, std::nullopt}; + } +#endif + template + in_t in(Args... args) const { + return {this->value, {std::forward(args)...}, false}; + } - template - struct from_t { - using tuple_type = std::tuple; + template + in_t not_in(Args... args) const { + return {this->value, {std::forward(args)...}, true}; + } + + template + and_condition_t and_(R right) const { + return {this->value, std::move(right)}; + } + + template + or_condition_t or_(R right) const { + return {this->value, std::move(right)}; + } }; template - using is_from = polyfill::is_specialization_of; + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = true; template - using is_constrained_join = polyfill::is_detected; - } + T get_from_expression(T value) { + return std::move(value); + } - /** - * Explicit FROM function. Usage: - * `storage.select(&User::id, from());` - */ - template - internal::from_t from() { - static_assert(sizeof...(Tables) > 0, ""); - return {}; + template + T get_from_expression(expression_t expression) { + return std::move(expression.value); + } + + template + using unwrap_expression_t = decltype(get_from_expression(std::declval())); } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES /** - * Explicit FROM function. Usage: - * `storage.select(&User::id, from<"a"_alias.for_>());` + * Public interface for syntax sugar for columns. Example: `where(c(&User::id) == 5)` or + * `storage.update(set(c(&User::name) = "Dua Lipa")); */ - template - auto from() { - return from...>(); + template + internal::expression_t c(T value) { + return {std::move(value)}; } -#endif +} - // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace - // to facilitate ADL (Argument Dependent Lookup) - namespace internal { - template< - class T, - std::enable_if_t, is_operator_argument>::value, - bool> = true> - negated_condition_t operator!(T arg) { - return {std::move(arg)}; - } - - 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))}; - } +// #include "column_pointer.h" - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - greater_than_t, unwrap_expression_t> operator>(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } +// #include "tags.h" - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - greater_or_equal_t, unwrap_expression_t> operator>=(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } +// #include "type_printer.h" - template, - std::is_base_of, - std::is_base_of, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - is_equal_t, unwrap_expression_t> operator==(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } +// #include "literal.h" - template, - std::is_base_of, - std::is_base_of, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - is_not_equal_t, unwrap_expression_t> operator!=(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } +namespace sqlite_orm { + namespace internal { - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - and_condition_t, unwrap_expression_t> operator&&(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + /* + * Protect an otherwise bindable element so that it is always serialized as a literal value. + */ + template + struct literal_holder { + using type = T; - template, std::is_base_of>::value, - bool> = true> - or_condition_t, unwrap_expression_t> operator||(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + type value; + }; - template< - class L, - class R, - std::enable_if_t, - std::is_base_of, - is_operator_argument, - is_operator_argument>, - // exclude conditions - polyfill::negation, - std::is_base_of>>>::value, - bool> = true> - conc_t, unwrap_expression_t> operator||(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } } +} - template - internal::using_t using_(F O::*field) { - return {field}; - } - template - internal::using_t using_(internal::column_pointer field) { - return {std::move(field)}; - } +namespace sqlite_orm { - template - internal::on_t on(T t) { - return {std::move(t)}; - } + namespace internal { - template - internal::cross_join_t cross_join() { - return {}; - } + struct limit_string { + operator std::string() const { + return "LIMIT"; + } + }; - template - internal::natural_join_t natural_join() { - return {}; - } + /** + * Stores LIMIT/OFFSET info + */ + template + struct limit_t : limit_string { + T lim; + optional_container off; - template - internal::left_join_t left_join(O o) { - return {std::move(o)}; - } + limit_t() = default; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto left_join(On on) { - return left_join, On>(std::move(on)); - } -#endif + limit_t(decltype(lim) lim_) : lim(std::move(lim_)) {} - template - internal::join_t join(O o) { - return {std::move(o)}; - } + limit_t(decltype(lim) lim_, decltype(off) off_) : lim(std::move(lim_)), off(std::move(off_)) {} + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto join(On on) { - return join, On>(std::move(on)); - } -#endif + template + struct is_limit : std::false_type {}; - template - internal::left_outer_join_t left_outer_join(O o) { - return {std::move(o)}; - } + template + struct is_limit> : std::true_type {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto left_outer_join(On on) { - return left_outer_join, On>(std::move(on)); - } -#endif + /** + * Stores OFFSET only info + */ + template + struct offset_t { + T off; + }; - template - internal::inner_join_t inner_join(O o) { - return {std::move(o)}; - } + template + using is_offset = polyfill::is_specialization_of; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto inner_join(On on) { - return inner_join, On>(std::move(on)); - } -#endif + /** + * Collated something + */ + template + struct collate_t : public condition_t { + T expr; + collate_argument argument; - template - internal::offset_t offset(T off) { - return {std::move(off)}; - } + collate_t(T expr_, collate_argument argument_) : expr(std::move(expr_)), argument(argument_) {} - template - internal::limit_t limit(T lim) { - return {std::move(lim)}; - } + operator std::string() const { + return collate_constraint_t{this->argument}; + } + }; - template = true> - internal::limit_t limit(O off, T lim) { - return {std::move(lim), {std::move(off)}}; - } + struct named_collate_base { + std::string name; - template - internal::limit_t limit(T lim, internal::offset_t offt) { - return {std::move(lim), {std::move(offt.off)}}; - } + operator std::string() const { + return "COLLATE " + this->name; + } + }; - template - auto and_(L l, R r) { - using namespace ::sqlite_orm::internal; - return and_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), - get_from_expression(std::forward(r))}; - } + /** + * Collated something with custom collate function + */ + template + struct named_collate : named_collate_base { + T expr; - template - auto or_(L l, R r) { - using namespace ::sqlite_orm::internal; - return or_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), - get_from_expression(std::forward(r))}; - } + named_collate(T expr_, std::string name_) : named_collate_base{std::move(name_)}, expr(std::move(expr_)) {} + }; - template - internal::is_not_null_t is_not_null(T t) { - return {std::move(t)}; - } + struct negated_condition_string { + operator std::string() const { + return "NOT"; + } + }; - template - internal::is_null_t is_null(T t) { - return {std::move(t)}; - } + /** + * Result of not operator + */ + template + struct negated_condition_t : condition_t, negated_condition_string { + C c; - template - internal::dynamic_in_t> in(L l, std::vector values) { - return {std::move(l), std::move(values), false}; - } + negated_condition_t(C c_) : c(std::move(c_)) {} + }; - template - internal::dynamic_in_t> in(L l, std::initializer_list values) { - return {std::move(l), std::move(values), false}; - } + /** + * Base class for binary conditions + * L is left argument type + * R is right argument type + * S is 'string' class (a class which has cast to `std::string` operator) + * Res is result type + */ + template + struct binary_condition : condition_t, S { + using left_type = L; + using right_type = R; + using result_type = Res; - template - internal::dynamic_in_t in(L l, A arg) { - return {std::move(l), std::move(arg), false}; - } + left_type lhs; + right_type rhs; - template - internal::dynamic_in_t> not_in(L l, std::vector values) { - return {std::move(l), std::move(values), true}; - } + binary_condition() = default; - template - internal::dynamic_in_t> not_in(L l, std::initializer_list values) { - return {std::move(l), std::move(values), true}; - } + binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} + }; - template - internal::dynamic_in_t not_in(L l, A arg) { - return {std::move(l), std::move(arg), true}; - } + template + SQLITE_ORM_INLINE_VAR constexpr bool is_binary_condition_v = is_base_of_template_v; - template - internal::is_equal_t is_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + template + struct is_binary_condition : polyfill::bool_constant> {}; - template - internal::is_equal_t eq(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct and_condition_string { + serialize_result_type serialize() const { + return "AND"; + } + }; - template - internal::is_equal_with_table_t is_equal(R rhs) { - return {std::move(rhs)}; - } + /** + * Result of and operator + */ + template + struct and_condition_t : binary_condition { + using super = binary_condition; - template - internal::is_not_equal_t is_not_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + using super::super; + }; - template - internal::is_not_equal_t ne(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct or_condition_string { + serialize_result_type serialize() const { + return "OR"; + } + }; - template - internal::greater_than_t greater_than(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * Result of or operator + */ + template + struct or_condition_t : binary_condition { + using super = binary_condition; - template - internal::greater_than_t gt(L l, R r) { - return {std::move(l), std::move(r)}; - } + using super::super; + }; - template - internal::greater_or_equal_t greater_or_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + struct is_equal_string { + serialize_result_type serialize() const { + return "="; + } + }; - template - internal::greater_or_equal_t ge(L l, R r) { - return {std::move(l), std::move(r)}; - } + /** + * = and == operators object + */ + template + struct is_equal_t : binary_condition, negatable_t { + using self = is_equal_t; - template - internal::less_than_t less_than(L l, R r) { - return {std::move(l), std::move(r)}; - } + using binary_condition::binary_condition; - /** - * [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_binary() const { + return {*this, collate_argument::binary}; + } - template - internal::less_than_t lt(L l, R r) { - return {std::move(l), std::move(r)}; - } + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } - template - internal::less_or_equal_t less_or_equal(L l, R r) { - return {std::move(l), std::move(r)}; - } + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } - /** - * [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)}; - } + named_collate collate(std::string name) const { + return {*this, std::move(name)}; + } - template - internal::less_or_equal_t le(L l, R r) { - return {std::move(l), std::move(r)}; - } + template + named_collate collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return {*this, ss.str()}; + } + }; - /** - * ORDER BY column, column alias or expression - * - * Examples: - * storage.select(&User::name, order_by(&User::id)) - * storage.select(as(&User::name), order_by(get())) - */ - template> = true> - internal::order_by_t order_by(O o) { - return {std::move(o)}; - } + template + struct is_equal_with_table_t : negatable_t { + using left_type = L; + using right_type = R; - /** - * 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)}}; - } + right_type rhs; - /** - * 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)...}}; - } + is_equal_with_table_t(right_type rhs) : rhs(std::move(rhs)) {} + }; - /** - * ORDER BY column1, column2 - * Difference from `multi_order_by` is that `dynamic_order_by` can be changed at runtime using `push_back` member - * function Example: - * auto orderBy = dynamic_order_by(storage); - * if(someCondition) { - * orderBy.push_back(&User::id); - * } else { - * orderBy.push_back(&User::name); - * orderBy.push_back(&User::birthDate); - * } - */ - template - internal::dynamic_order_by_t> - dynamic_order_by(const S& storage) { - internal::serializer_context_builder builder(storage); - return builder(); - } + struct is_not_equal_string { + serialize_result_type serialize() const { + return "!="; + } + }; - /** - * 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)}; - } -} -#pragma once - -#include // std::enable_if, std::is_same, std::conditional -#include // std::make_index_sequence, std::move -#include // std::string -#include // std::stringstream -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include -#endif - -// #include "functional/cxx_type_traits_polyfill.h" + /** + * != operator object + */ + template + struct is_not_equal_t : binary_condition, negatable_t { + using self = is_not_equal_t; -// #include "functional/mpl/conditional.h" + using binary_condition::binary_condition; -// #include "functional/cstring_literal.h" + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include // std::index_sequence -#include // std::copy_n -#endif + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } -// #include "cxx_universal.h" -// ::size_t + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -namespace sqlite_orm::internal { - /* - * Wraps a C string of fixed size. - * Its main purpose is to enable the user-defined string literal operator template. - */ - template - struct cstring_literal { - static constexpr size_t size() { - return N - 1; - } + struct greater_than_string { + serialize_result_type serialize() const { + return ">"; + } + }; - constexpr cstring_literal(const char (&cstr)[N]) { - std::copy_n(cstr, N, this->cstr); - } + /** + * > operator object. + */ + template + struct greater_than_t : binary_condition, negatable_t { + using self = greater_than_t; - char cstr[N]; - }; + using binary_condition::binary_condition; - template class Template, cstring_literal literal, size_t... Idx> - consteval auto explode_into(std::index_sequence) { - return Template{}; - } -} -#endif + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } -// #include "type_traits.h" + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } -// #include "alias_traits.h" + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; -// #include "table_type_of.h" + struct greater_or_equal_string { + serialize_result_type serialize() const { + return ">="; + } + }; -// #include "tags.h" + /** + * >= operator object. + */ + template + struct greater_or_equal_t : binary_condition, negatable_t { + using self = greater_or_equal_t; -// #include "column_pointer.h" + using binary_condition::binary_condition; -namespace sqlite_orm { + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - namespace internal { -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - inline constexpr bool is_operator_argument_v>> = true; -#endif + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; + } - /** - * This is a common built-in class used for character based table aliases. - * For convenience there exist public type aliases `alias_a`, `alias_b`, ... - * The easiest way to create a table alias is using `"z"_alias.for_()`. - */ - template - struct recordset_alias : alias_tag { - using type = T; + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; + } + }; - static std::string get() { - return {A, X...}; + struct less_than_string { + serialize_result_type serialize() const { + return "<"; } }; /** - * Column expression with table alias attached like 'C.ID'. This is not a column alias + * < operator object. */ - template - struct alias_column_t { - using alias_type = T; - using column_type = C; - - column_type column; - }; + template + struct less_than_t : binary_condition, negatable_t { + using self = less_than_t; - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_operator_argument_v::value>> = - true; + using binary_condition::binary_condition; - struct basic_table; + collate_t collate_binary() const { + return {*this, collate_argument::binary}; + } - /* - * Encapsulates extracting the alias identifier of a non-alias. - * - * `extract()` always returns the empty string. - * `as_alias()` is used in contexts where a table might be aliased, and the empty string is returned. - * `as_qualifier()` is used in contexts where a table might be aliased, and the given table's name is returned. - */ - template - struct alias_extractor { - static std::string extract() { - return {}; + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; } - static std::string as_alias() { - return {}; + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; } + }; - template - static const std::string& as_qualifier(const X& table) { - return table.name; + struct less_or_equal_string { + serialize_result_type serialize() const { + return "<="; } }; - /* - * Encapsulates extracting the alias identifier of an alias. - * - * `extract()` always returns the alias identifier or CTE moniker. - * `as_alias()` is used in contexts where a recordset is aliased, and the alias identifier is returned. - * `as_qualifier()` is used in contexts where a table is aliased, and the alias identifier is returned. + /** + * <= operator object. */ - template - struct alias_extractor> { - static std::string extract() { - std::stringstream ss; - ss << A::get(); - return ss.str(); - } + template + struct less_or_equal_t : binary_condition, negatable_t { + using self = less_or_equal_t; - // for column and regular table aliases -> alias identifier - template, A> = true> - static std::string as_alias() { - return alias_extractor::extract(); + using binary_condition::binary_condition; + + collate_t collate_binary() const { + return {*this, collate_argument::binary}; } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - // for CTE monikers -> empty - template, A> = true> - static std::string as_alias() { - return {}; + collate_t collate_nocase() const { + return {*this, collate_argument::nocase}; } -#endif - // for regular table aliases -> alias identifier - template = true> - static std::string as_qualifier(const basic_table&) { - return alias_extractor::extract(); + collate_t collate_rtrim() const { + return {*this, collate_argument::rtrim}; } }; - /** - * Used to store alias for expression - */ - template - struct as_t { - using alias_type = T; - using expression_type = E; + struct in_base { + bool negative = false; // used in not_in - expression_type expression; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + in_base(bool negative) : negative{negative} {} +#endif }; /** - * 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`. + * IN operator object. */ - template - struct column_alias : alias_tag { - static std::string get() { - return {A, X...}; - } + template + struct dynamic_in_t : condition_t, in_base, negatable_t { + using self = dynamic_in_t; + + L left; // left expression + A argument; // in arg + + dynamic_in_t(L left_, A argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} }; - template - struct alias_holder { - using type = T; + template + struct in_t : condition_t, in_base, negatable_t { + L left; + std::tuple argument; - alias_holder() = default; - // CTE feature needs it to implicitly convert a column alias to an alias_holder; see `cte()` factory function - alias_holder(const T&) noexcept {} + in_t(L left_, decltype(argument) argument_, bool negative_) : + in_base{negative_}, left(std::move(left_)), argument(std::move(argument_)) {} + }; + + struct is_null_string { + operator std::string() const { + return "IS NULL"; + } }; + /** + * IS NULL operator object. + */ template - SQLITE_ORM_INLINE_VAR constexpr bool - is_operator_argument_v::value>> = true; + struct is_null_t : is_null_string, negatable_t { + using self = is_null_t; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - struct recordset_alias_builder { - template - [[nodiscard]] consteval recordset_alias for_() const { - return {}; - } + T t; - template - [[nodiscard]] consteval auto for_() const { - using T = std::remove_const_t; - return recordset_alias{}; - } + is_null_t(T t_) : t(std::move(t_)) {} }; -#endif -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template - SQLITE_ORM_CONSTEVAL auto n_to_colalias() { - constexpr column_alias<'1' + n % 10, C...> colalias{}; - if constexpr(n > 10) { - return n_to_colalias(); - } else { - return colalias; + struct is_not_null_string { + operator std::string() const { + return "IS NOT NULL"; } - } + }; + /** + * IS NOT NULL operator object. + */ template - inline constexpr bool is_builtin_numeric_column_alias_v = false; - template - inline constexpr bool is_builtin_numeric_column_alias_v> = ((C >= '0' && C <= '9') && ...); -#endif - } - - /** - * Using a column pointer, create a column reference to an aliased table column. - * - * Example: - * using als = alias_u; - * select(alias_column(column(&User::id))) - */ - template, - polyfill::negation>>>::value, - bool> = true> - constexpr auto alias_column(C field) { - using namespace ::sqlite_orm::internal; - using aliased_type = type_t; - static_assert(is_field_of_v, "Column must be from aliased table"); - - return alias_column_t{std::move(field)}; - } + struct is_not_null_t : is_not_null_string, negatable_t { + using self = is_not_null_t; - /** - * Using an object member field, create a column reference to an aliased table column. - * - * @note The object member pointer can be from a derived class without explicitly forming a column pointer. - * - * Example: - * using als = alias_u; - * select(alias_column(&User::id)) - */ - template, - polyfill::negation>>>::value, - bool> = true> - constexpr auto alias_column(F O::*field) { - using namespace ::sqlite_orm::internal; - using aliased_type = type_t; - static_assert(is_field_of_v, "Column must be from aliased table"); + T t; - using C1 = - mpl::conditional_t::value, F O::*, column_pointer>; - return alias_column_t{C1{field}}; - } + is_not_null_t(T t_) : t(std::move(t_)) {} + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Create a column reference to an aliased table column. - * - * @note An object member pointer can be from a derived class without explicitly forming a column pointer. - * - * Example: - * constexpr orm_table_alias auto als = "u"_alias.for_(); - * select(alias_column(&User::id)) - */ - template - requires(!orm_cte_moniker>) - constexpr auto alias_column(C field) { - using namespace ::sqlite_orm::internal; - using A = decltype(als); - using aliased_type = type_t; - static_assert(is_field_of_v, "Column must be from aliased table"); + struct order_by_base { + int asc_desc = 0; // 1: asc, -1: desc + std::string _collate_argument; - if constexpr(is_column_pointer_v) { - return alias_column_t{std::move(field)}; - } else if constexpr(std::is_same_v, aliased_type>) { - return alias_column_t{field}; - } else { - // wrap in column_pointer - using C1 = column_pointer; - return alias_column_t{{field}}; - } - } +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + order_by_base() = default; - /** - * Create a column reference to an aliased table column. - * - * @note An object member pointer can be from a derived class without explicitly forming a column pointer. - * - * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) - * because recordset aliases are derived from `sqlite_orm::alias_tag` - * - * Example: - * constexpr auto als = "u"_alias.for_(); - * select(als->*&User::id) - */ - template - requires(!orm_cte_moniker>) - constexpr auto operator->*(const A& /*tableAlias*/, F field) { - return alias_column(std::move(field)); - } + order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : + asc_desc(asc_desc_), _collate_argument(std::move(_collate_argument_)) {} #endif + }; -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - /** - * Create a column reference to an aliased CTE column. - */ - template, internal::is_cte_moniker>>, - bool> = true> - constexpr auto alias_column(C c) { - using namespace internal; - using cte_moniker_t = type_t; + struct order_by_string { + operator std::string() const { + return "ORDER BY"; + } + }; - if constexpr(is_column_pointer_v) { - static_assert(std::is_same, cte_moniker_t>::value, - "Column pointer must match aliased CTE"); - return alias_column_t{c}; - } else { - auto cp = column(c); - return alias_column_t{std::move(cp)}; - } - } + /** + * ORDER BY argument holder. + */ + template + struct order_by_t : order_by_base, order_by_string { + using expression_type = O; + using self = order_by_t; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Create a column reference to an aliased CTE column. - * - * @note (internal) Intentionally place in the sqlite_orm namespace for ADL (Argument Dependent Lookup) - * because recordset aliases are derived from `sqlite_orm::alias_tag` - */ - template - requires(orm_cte_moniker>) - constexpr auto operator->*(const A& /*tableAlias*/, C c) { - return alias_column(std::move(c)); - } + expression_type expression; - /** - * Create a column reference to an aliased CTE column. - */ - template - requires(orm_cte_moniker>) - constexpr auto alias_column(C c) { - using A = std::remove_const_t; - return alias_column(std::move(c)); - } -#endif -#endif + order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} - /** - * Alias a column expression. - */ - template = true> - internal::as_t as(E expression) { - return {std::move(expression)}; - } - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Alias a column expression. - */ - template - auto as(E expression) { - return internal::as_t{std::move(expression)}; - } - - /** - * Alias a column expression. - */ - template - internal::as_t operator>>=(E expression, const A&) { - return {std::move(expression)}; - } -#else - /** - * Alias a column expression. - */ - template = true> - internal::as_t operator>>=(E expression, const A&) { - return {std::move(expression)}; - } -#endif - - /** - * Wrap a column alias in an alias holder. - */ - template - internal::alias_holder get() { - static_assert(internal::is_column_alias_v, ""); - return {}; - } + self asc() const { + auto res = *this; + res.asc_desc = 1; + return res; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto get() { - return internal::alias_holder{}; - } -#endif + self desc() const { + auto res = *this; + res.asc_desc = -1; + return res; + } - template - using alias_a = internal::recordset_alias; - template - using alias_b = internal::recordset_alias; - template - using alias_c = internal::recordset_alias; - template - using alias_d = internal::recordset_alias; - template - using alias_e = internal::recordset_alias; - template - using alias_f = internal::recordset_alias; - template - using alias_g = internal::recordset_alias; - template - using alias_h = internal::recordset_alias; - template - using alias_i = internal::recordset_alias; - template - using alias_j = internal::recordset_alias; - template - using alias_k = internal::recordset_alias; - template - using alias_l = internal::recordset_alias; - template - using alias_m = internal::recordset_alias; - template - using alias_n = internal::recordset_alias; - template - using alias_o = internal::recordset_alias; - template - using alias_p = internal::recordset_alias; - template - using alias_q = internal::recordset_alias; - template - using alias_r = internal::recordset_alias; - template - using alias_s = internal::recordset_alias; - template - using alias_t = internal::recordset_alias; - template - using alias_u = internal::recordset_alias; - template - using alias_v = internal::recordset_alias; - template - using alias_w = internal::recordset_alias; - template - using alias_x = internal::recordset_alias; - template - using alias_y = internal::recordset_alias; - template - using alias_z = internal::recordset_alias; + self collate_binary() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); + return res; + } - using colalias_a = internal::column_alias<'a'>; - using colalias_b = internal::column_alias<'b'>; - using colalias_c = internal::column_alias<'c'>; - using colalias_d = internal::column_alias<'d'>; - using colalias_e = internal::column_alias<'e'>; - using colalias_f = internal::column_alias<'f'>; - using colalias_g = internal::column_alias<'g'>; - using colalias_h = internal::column_alias<'h'>; - using colalias_i = internal::column_alias<'i'>; + self collate_nocase() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); + return res; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** @short Create a table alias. - * - * Examples: - * constexpr orm_table_alias auto z_alias = alias<'z'>.for_(); - */ - template - inline constexpr internal::recordset_alias_builder alias{}; + self collate_rtrim() const { + auto res = *this; + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); + return res; + } - inline namespace literals { - /** @short Create a table alias. - * - * Examples: - * constexpr orm_table_alias auto z_alias = "z"_alias.for_(); - */ - template - [[nodiscard]] consteval auto operator"" _alias() { - return internal::explode_into( - std::make_index_sequence{}); - } + self collate(std::string name) const { + auto res = *this; + res._collate_argument = std::move(name); + return res; + } - /** @short Create a column alias. - * column_alias<'a'[, ...]> from a string literal. - * E.g. "a"_col, "b"_col - */ - template - [[nodiscard]] consteval auto operator"" _col() { - return internal::explode_into(std::make_index_sequence{}); - } - } -#endif + template + self collate() const { + std::stringstream ss; + ss << C::name() << std::flush; + return this->collate(ss.str()); + } + }; -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - inline namespace literals { /** - * column_alias<'1'[, ...]> from a numeric literal. - * E.g. 1_colalias, 2_colalias + * ORDER BY pack holder. */ - template - [[nodiscard]] SQLITE_ORM_CONSTEVAL auto operator"" _colalias() { - // numeric identifiers are used for automatically assigning implicit aliases to unaliased column expressions, - // which start at "1". - static_assert(std::array{Chars...}[0] > '0'); - return internal::column_alias{}; - } - } -#endif -} -#pragma once - -#include // std::string -#include // std::make_tuple, std::tuple_size -#include // std::forward, std::is_base_of, std::enable_if -#include // std::unique_ptr -#include // std::vector - -// #include "functional/cxx_type_traits_polyfill.h" + template + struct multi_order_by_t : order_by_string { + using args_type = std::tuple; -// #include "functional/mpl/conditional.h" + args_type args; -// #include "is_base_of_template.h" + multi_order_by_t(args_type args_) : args{std::move(args_)} {} + }; -// #include "tuple_helper/tuple_traits.h" + struct dynamic_order_by_entry_t : order_by_base { + std::string name; -// #include "conditions.h" + dynamic_order_by_entry_t(decltype(name) name_, int asc_desc_, std::string collate_argument_) : + order_by_base{asc_desc_, std::move(collate_argument_)}, name(std::move(name_)) {} + }; -// #include "serialize_result_type.h" + /** + * C - serializer context class + */ + template + struct dynamic_order_by_t : order_by_string { + using context_t = C; + using entry_t = dynamic_order_by_entry_t; + using const_iterator = typename std::vector::const_iterator; -// #include "operators.h" + dynamic_order_by_t(const context_t& context_) : context(context_) {} -// #include "tags.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 "table_reference.h" + const_iterator begin() const { + return this->entries.begin(); + } -// #include "ast/into.h" + const_iterator end() const { + return this->entries.end(); + } -// #include "../functional/cxx_type_traits_polyfill.h" + void clear() { + this->entries.clear(); + } -namespace sqlite_orm { - namespace internal { + protected: + std::vector entries; + context_t context; + }; template - struct into_t { - using type = T; - }; + SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = + polyfill::disjunction, + polyfill::is_specialization_of, + polyfill::is_specialization_of>::value; 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 { + struct is_order_by : polyfill::bool_constant> {}; - template - struct unique_ptr_result_of {}; + struct between_string { + operator std::string() const { + return "BETWEEN"; + } + }; /** - * Base class for operator overloading - * R - return type - * S - class with operator std::string - * Args - function arguments types + * BETWEEN operator object. */ - template - struct built_in_function_t : S, arithmetic_t { - using return_type = R; - using string_type = S; - using args_type = std::tuple; - - static constexpr size_t args_size = std::tuple_size::value; + template + struct between_t : condition_t, between_string { + using expression_type = A; + using lower_type = T; + using upper_type = T; - args_type args; + expression_type expr; + lower_type b1; + upper_type b2; - built_in_function_t(args_type&& args_) : args(std::move(args_)) {} + between_t(expression_type expr_, lower_type b1_, upper_type b2_) : + expr(std::move(expr_)), b1(std::move(b1_)), b2(std::move(b2_)) {} }; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = - is_base_of_template::value; - - template - struct is_built_in_function : polyfill::bool_constant> {}; - - template - struct filtered_aggregate_function { - using function_type = F; - using where_expression = W; - - function_type function; - where_expression where; + struct like_string { + operator std::string() const { + return "LIKE"; + } }; - template - struct where_t; + /** + * LIKE operator object. + */ + template + struct like_t : condition_t, like_string, negatable_t { + using self = like_t; + using arg_t = A; + using pattern_t = T; + using escape_t = E; - template - struct built_in_aggregate_function_t : built_in_function_t { - using super = built_in_function_t; + arg_t arg; + pattern_t pattern; + optional_container arg3; // not escape cause escape exists as a function here - using super::super; + like_t(arg_t arg_, pattern_t pattern_, optional_container escape_) : + arg(std::move(arg_)), pattern(std::move(pattern_)), arg3(std::move(escape_)) {} - template - filtered_aggregate_function, W> filter(where_t wh) { - return {*this, std::move(wh.expression)}; + template + like_t escape(C c) const { + optional_container newArg3{std::move(c)}; + return {std::move(this->arg), std::move(this->pattern), std::move(newArg3)}; } }; - struct typeof_string { - serialize_result_type serialize() const { - return "TYPEOF"; + struct glob_string { + operator std::string() const { + return "GLOB"; } }; - struct unicode_string { - serialize_result_type serialize() const { - return "UNICODE"; - } - }; + template + struct glob_t : condition_t, glob_string, negatable_t { + using self = glob_t; + using arg_t = A; + using pattern_t = T; - struct length_string { - serialize_result_type serialize() const { - return "LENGTH"; - } - }; + arg_t arg; + pattern_t pattern; - struct abs_string { - serialize_result_type serialize() const { - return "ABS"; - } + glob_t(arg_t arg_, pattern_t pattern_) : arg(std::move(arg_)), pattern(std::move(pattern_)) {} }; - struct lower_string { - serialize_result_type serialize() const { - return "LOWER"; + struct cross_join_string { + operator std::string() const { + return "CROSS JOIN"; } }; - struct upper_string { - serialize_result_type serialize() const { - return "UPPER"; - } + /** + * CROSS JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct cross_join_t : cross_join_string { + using type = T; }; - struct last_insert_rowid_string { - serialize_result_type serialize() const { - return "LAST_INSERT_ROWID"; + struct natural_join_string { + operator std::string() const { + return "NATURAL JOIN"; } }; - struct total_changes_string { - serialize_result_type serialize() const { - return "TOTAL_CHANGES"; - } + /** + * NATURAL JOIN holder. + * T is joined type which represents any mapped table. + */ + template + struct natural_join_t : natural_join_string { + using type = T; }; - struct changes_string { - serialize_result_type serialize() const { - return "CHANGES"; + struct left_join_string { + operator std::string() const { + return "LEFT JOIN"; } }; - struct trim_string { - serialize_result_type serialize() const { - return "TRIM"; - } - }; + /** + * LEFT JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct left_join_t : left_join_string { + using type = T; + using on_type = O; - struct ltrim_string { - serialize_result_type serialize() const { - return "LTRIM"; - } - }; + on_type constraint; - struct rtrim_string { - serialize_result_type serialize() const { - return "RTRIM"; - } + left_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; - struct hex_string { - serialize_result_type serialize() const { - return "HEX"; + struct join_string { + operator std::string() const { + return "JOIN"; } }; - struct quote_string { - serialize_result_type serialize() const { - return "QUOTE"; - } - }; + /** + * Simple JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct join_t : join_string { + using type = T; + using on_type = O; - struct randomblob_string { - serialize_result_type serialize() const { - return "RANDOMBLOB"; - } - }; + on_type constraint; - struct instr_string { - serialize_result_type serialize() const { - return "INSTR"; - } + join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; - struct replace_string { - serialize_result_type serialize() const { - return "REPLACE"; + struct left_outer_join_string { + operator std::string() const { + return "LEFT OUTER JOIN"; } }; - struct round_string { - serialize_result_type serialize() const { - return "ROUND"; - } - }; + /** + * LEFT OUTER JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct left_outer_join_t : left_outer_join_string { + using type = T; + using on_type = O; -#if SQLITE_VERSION_NUMBER >= 3007016 - struct char_string { - serialize_result_type serialize() const { - return "CHAR"; - } - }; + on_type constraint; - struct random_string { - serialize_result_type serialize() const { - return "RANDOM"; - } + left_outer_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; -#endif - - struct coalesce_string { - serialize_result_type serialize() const { - return "COALESCE"; - } - }; - - struct ifnull_string { - serialize_result_type serialize() const { - return "IFNULL"; - } - }; - - struct nullif_string { - serialize_result_type serialize() const { - return "NULLIF"; - } - }; - - struct date_string { - serialize_result_type serialize() const { - return "DATE"; - } - }; - - struct time_string { - serialize_result_type serialize() const { - return "TIME"; - } - }; - - struct datetime_string { - serialize_result_type serialize() const { - return "DATETIME"; - } - }; - - struct julianday_string { - serialize_result_type serialize() const { - return "JULIANDAY"; - } - }; - - struct strftime_string { - serialize_result_type serialize() const { - return "STRFTIME"; - } - }; - - struct zeroblob_string { - serialize_result_type serialize() const { - return "ZEROBLOB"; - } - }; - - struct substr_string { - serialize_result_type serialize() const { - return "SUBSTR"; - } - }; -#ifdef SQLITE_SOUNDEX - struct soundex_string { - serialize_result_type serialize() const { - return "SOUNDEX"; - } - }; -#endif - struct total_string { - serialize_result_type serialize() const { - return "TOTAL"; - } - }; - - struct sum_string { - serialize_result_type serialize() const { - return "SUM"; - } - }; - - struct count_string { - serialize_result_type serialize() const { - return "COUNT"; + struct on_string { + operator std::string() const { + return "ON"; } }; /** - * T is use to specify type explicitly for queries like - * SELECT COUNT(*) FROM table_name; - * T can be omitted with void. + * on(...) argument holder used for JOIN, LEFT JOIN, LEFT OUTER JOIN and INNER JOIN + * T is on type argument. */ template - struct count_asterisk_t : count_string { - using type = T; + struct on_t : on_string { + using arg_type = T; - template - filtered_aggregate_function, W> filter(where_t wh) { - return {*this, std::move(wh.expression)}; - } + arg_type arg; + + on_t(arg_type arg_) : arg(std::move(arg_)) {} }; /** - * The same thing as count() but without T arg. - * Is used in cases like this: - * SELECT cust_code, cust_name, cust_city, grade - * FROM customer - * WHERE grade=2 AND EXISTS - * (SELECT COUNT(*) - * FROM customer - * WHERE grade=2 - * GROUP BY grade - * HAVING COUNT(*)>2); - * `c++` - * auto rows = - * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), - * where(is_equal(&Customer::grade, 2) - * and exists(select(count(), - * where(is_equal(&Customer::grade, 2)), - * group_by(&Customer::grade), - * having(greater_than(count(), 2)))))); + * USING argument holder. */ - struct count_asterisk_without_type : count_string {}; + template + struct using_t { + column_pointer column; - struct avg_string { - serialize_result_type serialize() const { - return "AVG"; + operator std::string() const { + return "USING"; } }; - struct max_string { - serialize_result_type serialize() const { - return "MAX"; + struct inner_join_string { + operator std::string() const { + return "INNER JOIN"; } }; - struct min_string { - serialize_result_type serialize() const { - return "MIN"; - } - }; + /** + * INNER JOIN holder. + * T is joined type which represents any mapped table. + * O is on(...) argument type. + */ + template + struct inner_join_t : inner_join_string { + using type = T; + using on_type = O; - struct group_concat_string { - serialize_result_type serialize() const { - return "GROUP_CONCAT"; - } - }; -#ifdef SQLITE_ENABLE_MATH_FUNCTIONS - struct acos_string { - serialize_result_type serialize() const { - return "ACOS"; - } - }; + on_type constraint; - struct acosh_string { - serialize_result_type serialize() const { - return "ACOSH"; - } + inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; - struct asin_string { - serialize_result_type serialize() const { - return "ASIN"; + struct cast_string { + operator std::string() const { + return "CAST"; } }; - struct asinh_string { - serialize_result_type serialize() const { - return "ASINH"; - } - }; + /** + * CAST holder. + * T is a type to cast to + * E is an expression type + * Example: cast(&User::id) + */ + template + struct cast_t : cast_string { + using to_type = T; + using expression_type = E; - struct atan_string { - serialize_result_type serialize() const { - return "ATAN"; - } - }; + expression_type expression; - struct atan2_string { - serialize_result_type serialize() const { - return "ATAN2"; - } + cast_t(expression_type expression_) : expression(std::move(expression_)) {} }; - struct atanh_string { - serialize_result_type serialize() const { - return "ATANH"; - } + template + struct from_t { + using tuple_type = std::tuple; }; - struct ceil_string { - serialize_result_type serialize() const { - return "CEIL"; - } - }; + template + using is_from = polyfill::is_specialization_of; - struct ceiling_string { - serialize_result_type serialize() const { - return "CEILING"; - } - }; + template + using is_constrained_join = polyfill::is_detected; + } - struct cos_string { - serialize_result_type serialize() const { - return "COS"; - } - }; + /** + * Explicit FROM function. Usage: + * `storage.select(&User::id, from());` + */ + template + internal::from_t from() { + static_assert(sizeof...(Tables) > 0, ""); + return {}; + } - struct cosh_string { - serialize_result_type serialize() const { - return "COSH"; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Explicit FROM function. Usage: + * `storage.select(&User::id, from<"a"_alias.for_>());` + */ + template + auto from() { + return from...>(); + } +#endif - struct degrees_string { - serialize_result_type serialize() const { - return "DEGREES"; - } - }; + // 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)}; + } - struct exp_string { - serialize_result_type serialize() const { - return "EXP"; - } - }; + 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 floor_string { - serialize_result_type serialize() const { - return "FLOOR"; - } - }; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + less_or_equal_t, unwrap_expression_t> operator<=(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct ln_string { - serialize_result_type serialize() const { - return "LN"; - } - }; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + greater_than_t, unwrap_expression_t> operator>(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct log_string { - serialize_result_type serialize() const { - return "LOG"; - } - }; + 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 log10_string { - serialize_result_type serialize() const { - return "LOG10"; - } - }; + template, + std::is_base_of, + std::is_base_of, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + is_equal_t, unwrap_expression_t> operator==(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct log2_string { - serialize_result_type serialize() const { - return "LOG2"; - } - }; + template, + std::is_base_of, + std::is_base_of, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + is_not_equal_t, unwrap_expression_t> operator!=(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct mod_string { - serialize_result_type serialize() const { - return "MOD"; - } - }; + 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 pi_string { - serialize_result_type serialize() const { - return "PI"; - } - }; + template, std::is_base_of>::value, + bool> = true> + or_condition_t, unwrap_expression_t> operator||(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - struct pow_string { - serialize_result_type serialize() const { - return "POW"; - } - }; + template< + class L, + class R, + std::enable_if_t, + std::is_base_of, + is_operator_argument, + is_operator_argument>, + // exclude conditions + polyfill::negation, + std::is_base_of>>>::value, + bool> = true> + conc_t, unwrap_expression_t> operator||(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + } - struct power_string { - serialize_result_type serialize() const { - return "POWER"; - } - }; + template + internal::using_t using_(F O::*field) { + return {field}; + } + template + internal::using_t using_(internal::column_pointer field) { + return {std::move(field)}; + } - struct radians_string { - serialize_result_type serialize() const { - return "RADIANS"; - } - }; + template + internal::on_t on(T t) { + return {std::move(t)}; + } - struct sin_string { - serialize_result_type serialize() const { - return "SIN"; - } - }; + template + internal::cross_join_t cross_join() { + return {}; + } - struct sinh_string { - serialize_result_type serialize() const { - return "SINH"; - } - }; + template + internal::natural_join_t natural_join() { + return {}; + } - struct sqrt_string { - serialize_result_type serialize() const { - return "SQRT"; - } - }; + template + internal::left_join_t left_join(O o) { + return {std::move(o)}; + } - struct tan_string { - serialize_result_type serialize() const { - return "TAN"; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto left_join(On on) { + return left_join, On>(std::move(on)); + } +#endif - struct tanh_string { - serialize_result_type serialize() const { - return "TANH"; - } - }; + template + internal::join_t join(O o) { + return {std::move(o)}; + } - struct trunc_string { - serialize_result_type serialize() const { - return "TRUNC"; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto join(On on) { + return join, On>(std::move(on)); + } +#endif -#endif // SQLITE_ENABLE_MATH_FUNCTIONS -#ifdef SQLITE_ENABLE_JSON1 - struct json_string { - serialize_result_type serialize() const { - return "JSON"; - } - }; + template + internal::left_outer_join_t left_outer_join(O o) { + return {std::move(o)}; + } - struct json_array_string { - serialize_result_type serialize() const { - return "JSON_ARRAY"; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto left_outer_join(On on) { + return left_outer_join, On>(std::move(on)); + } +#endif - struct json_array_length_string { - serialize_result_type serialize() const { - return "JSON_ARRAY_LENGTH"; - } - }; + template + internal::inner_join_t inner_join(O o) { + return {std::move(o)}; + } - struct json_extract_string { - serialize_result_type serialize() const { - return "JSON_EXTRACT"; - } - }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + auto inner_join(On on) { + return inner_join, On>(std::move(on)); + } +#endif - struct json_insert_string { - serialize_result_type serialize() const { - return "JSON_INSERT"; - } - }; + template + internal::offset_t offset(T off) { + return {std::move(off)}; + } - struct json_replace_string { - serialize_result_type serialize() const { - return "JSON_REPLACE"; - } - }; + template + internal::limit_t limit(T lim) { + return {std::move(lim)}; + } - struct json_set_string { - serialize_result_type serialize() const { - return "JSON_SET"; - } - }; + template = true> + internal::limit_t limit(O off, T lim) { + return {std::move(lim), {std::move(off)}}; + } - struct json_object_string { - serialize_result_type serialize() const { - return "JSON_OBJECT"; - } - }; + template + internal::limit_t limit(T lim, internal::offset_t offt) { + return {std::move(lim), {std::move(offt.off)}}; + } - struct json_patch_string { - serialize_result_type serialize() const { - return "JSON_PATCH"; - } - }; + template + auto and_(L l, R r) { + using namespace ::sqlite_orm::internal; + return and_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), + get_from_expression(std::forward(r))}; + } - struct json_remove_string { - serialize_result_type serialize() const { - return "JSON_REMOVE"; - } - }; + template + auto or_(L l, R r) { + using namespace ::sqlite_orm::internal; + return or_condition_t, unwrap_expression_t>{get_from_expression(std::forward(l)), + get_from_expression(std::forward(r))}; + } - struct json_type_string { - serialize_result_type serialize() const { - return "JSON_TYPE"; - } - }; + template + internal::is_not_null_t is_not_null(T t) { + return {std::move(t)}; + } - struct json_valid_string { - serialize_result_type serialize() const { - return "JSON_VALID"; - } - }; + template + internal::is_null_t is_null(T t) { + return {std::move(t)}; + } - struct json_quote_string { - serialize_result_type serialize() const { - return "JSON_QUOTE"; - } - }; + template + internal::dynamic_in_t> in(L l, std::vector values) { + return {std::move(l), std::move(values), false}; + } - struct json_group_array_string { - serialize_result_type serialize() const { - return "JSON_GROUP_ARRAY"; - } - }; + template + internal::dynamic_in_t> in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), false}; + } - struct json_group_object_string { - serialize_result_type serialize() const { - return "JSON_GROUP_OBJECT"; - } - }; -#endif // SQLITE_ENABLE_JSON1 + template + internal::dynamic_in_t in(L l, A arg) { + return {std::move(l), std::move(arg), false}; + } - template - using field_type_or_type_t = polyfill::detected_or_t>; + template + internal::dynamic_in_t> not_in(L l, std::vector values) { + return {std::move(l), std::move(values), true}; + } - template - struct highlight_t { - using table_type = T; - using argument0_type = X; - using argument1_type = Y; - using argument2_type = Z; + template + internal::dynamic_in_t> not_in(L l, std::initializer_list values) { + return {std::move(l), std::move(values), true}; + } - argument0_type argument0; - argument1_type argument1; - argument2_type argument2; + template + internal::dynamic_in_t not_in(L l, A arg) { + return {std::move(l), std::move(arg), true}; + } - highlight_t(argument0_type argument0, argument1_type argument1, argument2_type argument2) : - argument0(std::move(argument0)), argument1(std::move(argument1)), argument2(std::move(argument2)) {} - }; + template + internal::is_equal_t is_equal(L l, R r) { + return {std::move(l), std::move(r)}; } -#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + template + internal::is_equal_t eq(L l, R r) { + return {std::move(l), std::move(r)}; + } - /** - * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos - * - * Example: - * - * auto rows = storage.select(sqlite_orm::acos(&Triangle::cornerA)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t acos(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::is_equal_with_table_t is_equal(R rhs) { + return {std::move(rhs)}; } - /** - * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::acos>(&Triangle::cornerA)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t acos(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::is_not_equal_t is_not_equal(L l, R r) { + return {std::move(l), std::move(r)}; } - /** - * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::acosh(&Triangle::cornerA)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t acosh(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::is_not_equal_t ne(L l, R r) { + return {std::move(l), std::move(r)}; } - /** - * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::acosh>(&Triangle::cornerA)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t acosh(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::greater_than_t greater_than(L l, R r) { + return {std::move(l), std::move(r)}; } - /** - * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin - * - * Example: - * - * auto rows = storage.select(sqlite_orm::asin(&Triangle::cornerA)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t asin(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::greater_than_t gt(L l, R r) { + return {std::move(l), std::move(r)}; } - /** - * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::asin>(&Triangle::cornerA)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t asin(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::greater_or_equal_t greater_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; } - /** - * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::asinh(&Triangle::cornerA)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t asinh(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::greater_or_equal_t ge(L l, R r) { + return {std::move(l), std::move(r)}; + } + + template + internal::less_than_t less_than(L l, R r) { + return {std::move(l), std::move(r)}; } /** - * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::asinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + * [Deprecation notice] This function is deprecated and will be removed in v1.10. Use the accurately named function `less_than(...)` instead. */ - template - internal::built_in_function_t asinh(X x) { - return {std::tuple{std::forward(x)}}; + 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)}; } - /** - * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atan(1)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t atan(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::less_than_t lt(L l, R r) { + return {std::move(l), std::move(r)}; } - /** - * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atan>(1)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t atan(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::less_or_equal_t less_or_equal(L l, R r) { + return {std::move(l), std::move(r)}; } /** - * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atan2(1, 3)); // decltype(rows) is std::vector + * [Deprecation notice] This function is deprecated and will be removed in v1.10. Use the accurately named function `less_or_equal(...)` instead. */ - template - internal::built_in_function_t atan2(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; + 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)}; } - /** - * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atan2>(1, 3)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t atan2(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; + template + internal::less_or_equal_t le(L l, R r) { + return {std::move(l), std::move(r)}; } /** - * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atanh(1)); // decltype(rows) is std::vector + * ORDER BY column, column alias or expression + * + * Examples: + * storage.select(&User::name, order_by(&User::id)) + * storage.select(as(&User::name), order_by(get())) */ - template - internal::built_in_function_t atanh(X x) { - return {std::tuple{std::forward(x)}}; + template> = true> + internal::order_by_t order_by(O o) { + return {std::move(o)}; } /** - * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::atanh>(1)); // decltype(rows) is std::vector> + * ORDER BY positional ordinal + * + * Examples: + * storage.select(&User::name, order_by(1)) */ - template - internal::built_in_function_t atanh(X x) { - return {std::tuple{std::forward(x)}}; + template> = true> + internal::order_by_t> order_by(O o) { + return {{std::move(o)}}; } /** - * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ceil(&User::rating)); // decltype(rows) is std::vector + * ORDER BY column1, column2 + * Example: storage.get_all(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) */ - template - internal::built_in_function_t ceil(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::multi_order_by_t multi_order_by(Args... args) { + return {{std::forward(args)...}}; } /** - * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ceil>(&User::rating)); // decltype(rows) is std::vector> + * ORDER BY column1, column2 + * Difference from `multi_order_by` is that `dynamic_order_by` can be changed at runtime using `push_back` member + * function Example: + * auto orderBy = dynamic_order_by(storage); + * if(someCondition) { + * orderBy.push_back(&User::id); + * } else { + * orderBy.push_back(&User::name); + * orderBy.push_back(&User::birthDate); + * } */ - template - internal::built_in_function_t ceil(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::dynamic_order_by_t> + dynamic_order_by(const S& storage) { + internal::serializer_context_builder builder(storage); + return builder(); } /** - * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ceiling(&User::rating)); // decltype(rows) is std::vector + * X BETWEEN Y AND Z + * Example: storage.select(between(&User::id, 10, 20)) */ - template - internal::built_in_function_t ceiling(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::between_t between(A expr, T b1, T b2) { + return {std::move(expr), std::move(b1), std::move(b2)}; } /** - * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ceiling>(&User::rating)); // decltype(rows) is std::vector> + * X LIKE Y + * Example: storage.select(like(&User::name, "T%")) */ - template - internal::built_in_function_t ceiling(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::like_t like(A a, T t) { + return {std::move(a), std::move(t), {}}; } /** - * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos - * - * Example: - * - * auto rows = storage.select(sqlite_orm::cos(&Triangle::cornerB)); // decltype(rows) is std::vector + * X GLOB Y + * Example: storage.select(glob(&User::name, "*S")) */ - template - internal::built_in_function_t cos(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::glob_t glob(A a, T t) { + return {std::move(a), std::move(t)}; } /** - * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::cos>(&User::rating)); // decltype(rows) is std::vector> + * X LIKE Y ESCAPE Z + * Example: storage.select(like(&User::name, "T%", "%")) */ - template - internal::built_in_function_t cos(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::like_t like(A a, T t, E e) { + return {std::move(a), std::move(t), {std::move(e)}}; } /** - * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::cosh(&Triangle::cornerB)); // decltype(rows) is std::vector + * CAST(X AS type). + * Example: cast(&User::id) */ - template - internal::built_in_function_t cosh(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::cast_t cast(E e) { + return {std::move(e)}; } +} - /** - * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::cosh>(&User::rating)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t cosh(X x) { - return {std::tuple{std::forward(x)}}; - } +// #include "serialize_result_type.h" - /** - * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees - * - * Example: - * - * auto rows = storage.select(sqlite_orm::degrees(&Triangle::cornerB)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t degrees(X x) { - return {std::tuple{std::forward(x)}}; - } +// #include "operators.h" - /** - * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::degrees>(&User::rating)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t degrees(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp - * - * Example: - * - * auto rows = storage.select(sqlite_orm::exp(&Triangle::cornerB)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t exp(X x) { - return {std::tuple{std::forward(x)}}; - } +// #include "tags.h" - /** - * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::exp>(&User::rating)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t exp(X x) { - return {std::tuple{std::forward(x)}}; - } +// #include "table_reference.h" - /** - * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor - * - * Example: - * - * auto rows = storage.select(sqlite_orm::floor(&User::rating)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t floor(X x) { - return {std::tuple{std::forward(x)}}; - } +// #include "ast/into.h" - /** - * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::floor>(&User::rating)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t floor(X x) { - return {std::tuple{std::forward(x)}}; - } +// #include "../functional/cxx_type_traits_polyfill.h" - /** - * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ln(200)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t ln(X x) { - return {std::tuple{std::forward(x)}}; - } +namespace sqlite_orm { + namespace internal { - /** - * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::ln>(200)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t ln(X x) { - return {std::tuple{std::forward(x)}}; - } + template + struct into_t { + using type = T; + }; - /** - * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log(100)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t log(X x) { - return {std::tuple{std::forward(x)}}; + template + using is_into = polyfill::is_specialization_of; } - /** - * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log>(100)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t log(X x) { - return {std::tuple{std::forward(x)}}; + template + internal::into_t into() { + return {}; } +} - /** - * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log10(100)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t log10(X x) { - return {std::tuple{std::forward(x)}}; - } +namespace sqlite_orm { - /** - * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log10>(100)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t log10(X x) { - return {std::tuple{std::forward(x)}}; - } + using int64 = sqlite_int64; + using uint64 = sqlite_uint64; - /** - * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log(10, 100)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t log(B b, X x) { - return {std::tuple{std::forward(b), std::forward(x)}}; - } + namespace internal { - /** - * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log>(10, 100)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t log(B b, X x) { - return {std::tuple{std::forward(b), std::forward(x)}}; - } + template + struct unique_ptr_result_of {}; - /** - * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log2(64)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t log2(X x) { - return {std::tuple{std::forward(x)}}; - } + /** + * 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; - /** - * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::log2>(64)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t log2(X x) { - return {std::tuple{std::forward(x)}}; - } + static constexpr size_t args_size = std::tuple_size::value; - /** - * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod - * - * Example: - * - * auto rows = storage.select(sqlite_orm::mod_f(6, 5)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t mod_f(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + args_type args; - /** - * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::mod_f>(6, 5)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t mod_f(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + built_in_function_t(args_type&& args_) : args(std::move(args_)) {} + }; - /** - * PI() function https://www.sqlite.org/lang_mathfunc.html#pi - * - * Example: - * - * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector - */ - inline internal::built_in_function_t pi() { - return {{}}; - } - - /** - * PI() function https://www.sqlite.org/lang_mathfunc.html#pi - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, etc. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t pi() { - return {{}}; - } - - /** - * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow - * - * Example: - * - * auto rows = storage.select(sqlite_orm::pow(2, 5)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t pow(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::pow>(2, 5)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t pow(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow - * - * Example: - * - * auto rows = storage.select(sqlite_orm::power(2, 5)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t power(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::power>(2, 5)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t power(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians - * - * Example: - * - * auto rows = storage.select(sqlite_orm::radians(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t radians(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::radians>(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t radians(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sin(&Triangle::cornerA)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t sin(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sin>(&Triangle::cornerA)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t sin(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sinh(&Triangle::cornerA)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t sinh(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> - */ - template - internal::built_in_function_t sinh(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t sqrt(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t sqrt(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan - * - * Example: - * - * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t tan(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t tan(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh - * - * Example: - * - * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t tanh(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t tanh(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc - * - * Example: - * - * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t trunc(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc - * - * Difference with the previous function is that previous override has `double` as return type but this - * override accepts return type from you as a template argument. You can use any bindable type: - * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. - * - * Example: - * - * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector - */ - template - internal::built_in_function_t trunc(X x) { - return {std::tuple{std::forward(x)}}; - } -#endif // SQLITE_ENABLE_MATH_FUNCTIONS - /** - * TYPEOF(x) function https://sqlite.org/lang_corefunc.html#typeof - */ - template - internal::built_in_function_t typeof_(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * UNICODE(x) function https://sqlite.org/lang_corefunc.html#unicode - */ - template - internal::built_in_function_t unicode(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length - */ - template - internal::built_in_function_t length(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * ABS(x) function https://sqlite.org/lang_corefunc.html#abs - */ - template - internal::built_in_function_t, internal::abs_string, T> abs(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower - */ - template - internal::built_in_function_t lower(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper - */ - template - internal::built_in_function_t upper(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * LAST_INSERT_ROWID(x) function https://www.sqlite.org/lang_corefunc.html#last_insert_rowid - */ - inline internal::built_in_function_t last_insert_rowid() { - return {{}}; - } - - /** - * TOTAL_CHANGES() function https://sqlite.org/lang_corefunc.html#total_changes - */ - inline internal::built_in_function_t total_changes() { - return {{}}; - } - - /** - * CHANGES() function https://sqlite.org/lang_corefunc.html#changes - */ - inline internal::built_in_function_t changes() { - return {{}}; - } - - /** - * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim - */ - template - internal::built_in_function_t trim(T t) { - return {std::tuple{std::forward(t)}}; - } - - /** - * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim - */ - template - internal::built_in_function_t trim(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim - */ - template - internal::built_in_function_t ltrim(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim - */ - template - internal::built_in_function_t ltrim(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim - */ - template - internal::built_in_function_t rtrim(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim - */ - template - internal::built_in_function_t rtrim(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * HEX(X) function https://sqlite.org/lang_corefunc.html#hex - */ - template - internal::built_in_function_t hex(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote - */ - template - internal::built_in_function_t quote(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob - */ - template - internal::built_in_function_t, internal::randomblob_string, X> randomblob(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr - */ - template - internal::built_in_function_t instr(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - - /** - * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace - */ - template, internal::is_into>::value == 0, bool> = true> - internal::built_in_function_t replace(X x, Y y, Z z) { - return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; - } - - /** - * ROUND(X) function https://sqlite.org/lang_corefunc.html#round - */ - template - internal::built_in_function_t round(X x) { - return {std::tuple{std::forward(x)}}; - } - - /** - * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round - */ - template - internal::built_in_function_t round(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } - -#if SQLITE_VERSION_NUMBER >= 3007016 - /** - * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char - */ - template - internal::built_in_function_t char_(Args... args) { - return {std::make_tuple(std::forward(args)...)}; - } - - /** - * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random - */ - inline internal::built_in_function_t random() { - return {{}}; - } -#endif + template + SQLITE_ORM_INLINE_VAR constexpr bool is_built_in_function_v = + is_base_of_template::value; - /** - * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce - */ - template - auto coalesce(Args... args) - -> internal::built_in_function_t::value, - std::common_type...>, - polyfill::type_identity>::type, - internal::coalesce_string, - Args...> { - return {std::make_tuple(std::forward(args)...)}; - } + template + struct is_built_in_function : polyfill::bool_constant> {}; - /** - * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull - */ - template - auto ifnull(X x, Y y) -> internal::built_in_function_t< - typename mpl::conditional_t< // choose R or common type - std::is_void::value, - std::common_type, internal::field_type_or_type_t>, - polyfill::type_identity>::type, - internal::ifnull_string, - X, - Y> { - return {std::make_tuple(std::move(x), std::move(y))}; - } + template + struct filtered_aggregate_function { + using function_type = F; + using where_expression = W; - /** - * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif - */ -#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) - /** - * NULLIF(X,Y) using common return type of X and Y - */ - template>, - polyfill::is_detected, - internal::field_type_or_type_t>>, - bool> = true> - auto nullif(X x, Y y) { - if constexpr(std::is_void_v) { - using F = internal::built_in_function_t< - std::optional, internal::field_type_or_type_t>>, - internal::nullif_string, - X, - Y>; + function_type function; + where_expression where; + }; - return F{std::make_tuple(std::move(x), std::move(y))}; - } else { - using F = internal::built_in_function_t; + template + struct where_t; - return F{std::make_tuple(std::move(x), std::move(y))}; - } - } -#else - template - internal::built_in_function_t nullif(X x, Y y) { - return {std::make_tuple(std::move(x), std::move(y))}; - } -#endif + template + struct built_in_aggregate_function_t : built_in_function_t { + using super = built_in_function_t; - /** - * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html - */ - template - internal::built_in_function_t date(Args... args) { - return {std::tuple{std::forward(args)...}}; - } + using super::super; - /** - * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html - */ - template - internal::built_in_function_t time(Args... args) { - return {std::tuple{std::forward(args)...}}; - } + template + filtered_aggregate_function, W> filter(where_t wh) { + return {*this, std::move(wh.expression)}; + } + }; - /** - * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html - */ - template - internal::built_in_function_t datetime(Args... args) { - return {std::tuple{std::forward(args)...}}; - } + struct typeof_string { + serialize_result_type serialize() const { + return "TYPEOF"; + } + }; - /** - * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html - */ - template - internal::built_in_function_t julianday(Args... args) { - return {std::tuple{std::forward(args)...}}; - } + struct unicode_string { + serialize_result_type serialize() const { + return "UNICODE"; + } + }; - /** - * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html - */ - template - internal::built_in_function_t strftime(Args... args) { - return {std::tuple{std::forward(args)...}}; - } + struct length_string { + serialize_result_type serialize() const { + return "LENGTH"; + } + }; - /** - * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob - */ - template - internal::built_in_function_t, internal::zeroblob_string, N> zeroblob(N n) { - return {std::tuple{std::forward(n)}}; - } + struct abs_string { + serialize_result_type serialize() const { + return "ABS"; + } + }; - /** - * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr - */ - template - internal::built_in_function_t substr(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + struct lower_string { + serialize_result_type serialize() const { + return "LOWER"; + } + }; - /** - * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr - */ - template - internal::built_in_function_t substr(X x, Y y, Z z) { - return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; - } + struct upper_string { + serialize_result_type serialize() const { + return "UPPER"; + } + }; -#ifdef SQLITE_SOUNDEX - /** - * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex - */ - template - internal::built_in_function_t soundex(X x) { - return {std::tuple{std::forward(x)}}; - } -#endif + struct last_insert_rowid_string { + serialize_result_type serialize() const { + return "LAST_INSERT_ROWID"; + } + }; - /** - * TOTAL(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t total(X x) { - return {std::tuple{std::forward(x)}}; - } + struct total_changes_string { + serialize_result_type serialize() const { + return "TOTAL_CHANGES"; + } + }; - /** - * SUM(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t, internal::sum_string, X> sum(X x) { - return {std::tuple{std::forward(x)}}; - } + struct changes_string { + serialize_result_type serialize() const { + return "CHANGES"; + } + }; - /** - * COUNT(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t count(X x) { - return {std::tuple{std::forward(x)}}; - } + struct trim_string { + serialize_result_type serialize() const { + return "TRIM"; + } + }; - /** - * COUNT(*) without FROM function. - */ - inline internal::count_asterisk_without_type count() { - return {}; - } + struct ltrim_string { + serialize_result_type serialize() const { + return "LTRIM"; + } + }; - /** - * COUNT(*) with FROM function. Specified type T will be serialized as - * a from argument. - */ - template - internal::count_asterisk_t count() { - return {}; - } + struct rtrim_string { + serialize_result_type serialize() const { + return "RTRIM"; + } + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * COUNT(*) with FROM function. Specified recordset will be serialized as - * a from argument. - */ - template - auto count() { - return count>(); - } -#endif + struct hex_string { + serialize_result_type serialize() const { + return "HEX"; + } + }; - /** - * AVG(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t avg(X x) { - return {std::tuple{std::forward(x)}}; - } + struct quote_string { + serialize_result_type serialize() const { + return "QUOTE"; + } + }; - /** - * MAX(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t, internal::max_string, X> max(X x) { - return {std::tuple{std::forward(x)}}; - } + struct randomblob_string { + serialize_result_type serialize() const { + return "RANDOMBLOB"; + } + }; - /** - * MIN(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t, internal::min_string, X> min(X x) { - return {std::tuple{std::forward(x)}}; - } + struct instr_string { + serialize_result_type serialize() const { + return "INSTR"; + } + }; - /** - * MAX(X, Y, ...) scalar function. - * The return type is the type of the first argument. - */ - template - internal::built_in_function_t, internal::max_string, X, Y, Rest...> - max(X x, Y y, Rest... rest) { - return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; - } + struct replace_string { + serialize_result_type serialize() const { + return "REPLACE"; + } + }; - /** - * MIN(X, Y, ...) scalar function. - * The return type is the type of the first argument. - */ - template - internal::built_in_function_t, internal::min_string, X, Y, Rest...> - min(X x, Y y, Rest... rest) { - return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; - } + struct round_string { + serialize_result_type serialize() const { + return "ROUND"; + } + }; - /** - * GROUP_CONCAT(X) aggregate function. - */ - template - internal::built_in_aggregate_function_t group_concat(X x) { - return {std::tuple{std::forward(x)}}; - } +#if SQLITE_VERSION_NUMBER >= 3007016 + struct char_string { + serialize_result_type serialize() const { + return "CHAR"; + } + }; - /** - * GROUP_CONCAT(X, Y) aggregate function. - */ - template - internal::built_in_aggregate_function_t group_concat(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } -#ifdef SQLITE_ENABLE_JSON1 - template - internal::built_in_function_t json(X x) { - return {std::tuple{std::forward(x)}}; - } + struct random_string { + serialize_result_type serialize() const { + return "RANDOM"; + } + }; - template - internal::built_in_function_t json_array(Args... args) { - return {std::tuple{std::forward(args)...}}; - } +#endif - template - internal::built_in_function_t json_array_length(X x) { - return {std::tuple{std::forward(x)}}; - } + struct coalesce_string { + serialize_result_type serialize() const { + return "COALESCE"; + } + }; - template - internal::built_in_function_t json_array_length(X x) { - return {std::tuple{std::forward(x)}}; - } + struct ifnull_string { + serialize_result_type serialize() const { + return "IFNULL"; + } + }; - template - internal::built_in_function_t json_array_length(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + struct nullif_string { + serialize_result_type serialize() const { + return "NULLIF"; + } + }; - template - internal::built_in_function_t json_array_length(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + struct date_string { + serialize_result_type serialize() const { + return "DATE"; + } + }; - template - internal::built_in_function_t json_extract(X x, Args... args) { - return {std::tuple{std::forward(x), std::forward(args)...}}; - } + struct time_string { + serialize_result_type serialize() const { + return "TIME"; + } + }; - template - internal::built_in_function_t json_insert(X x, - Args... args) { - static_assert(std::tuple_size>::value % 2 == 0, - "number of arguments in json_insert must be odd"); - return {std::tuple{std::forward(x), std::forward(args)...}}; - } + struct datetime_string { + serialize_result_type serialize() const { + return "DATETIME"; + } + }; - template - internal::built_in_function_t json_replace(X x, - Args... args) { - static_assert(std::tuple_size>::value % 2 == 0, - "number of arguments in json_replace must be odd"); - return {std::tuple{std::forward(x), std::forward(args)...}}; - } + struct julianday_string { + serialize_result_type serialize() const { + return "JULIANDAY"; + } + }; - template - internal::built_in_function_t json_set(X x, Args... args) { - static_assert(std::tuple_size>::value % 2 == 0, - "number of arguments in json_set must be odd"); - return {std::tuple{std::forward(x), std::forward(args)...}}; - } + struct strftime_string { + serialize_result_type serialize() const { + return "STRFTIME"; + } + }; - template - internal::built_in_function_t json_object(Args... args) { - static_assert(std::tuple_size>::value % 2 == 0, - "number of arguments in json_object must be even"); - return {std::tuple{std::forward(args)...}}; - } + struct zeroblob_string { + serialize_result_type serialize() const { + return "ZEROBLOB"; + } + }; - template - internal::built_in_function_t json_patch(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + struct substr_string { + serialize_result_type serialize() const { + return "SUBSTR"; + } + }; +#ifdef SQLITE_SOUNDEX + struct soundex_string { + serialize_result_type serialize() const { + return "SOUNDEX"; + } + }; +#endif + struct total_string { + serialize_result_type serialize() const { + return "TOTAL"; + } + }; - template - internal::built_in_function_t json_remove(X x, - Args... args) { - return {std::tuple{std::forward(x), std::forward(args)...}}; - } + struct sum_string { + serialize_result_type serialize() const { + return "SUM"; + } + }; - template - internal::built_in_function_t json_remove(X x, Args... args) { - return {std::tuple{std::forward(x), std::forward(args)...}}; - } + struct count_string { + serialize_result_type serialize() const { + return "COUNT"; + } + }; - template - internal::built_in_function_t json_type(X x) { - return {std::tuple{std::forward(x)}}; - } + /** + * T is use to specify type explicitly for queries like + * SELECT COUNT(*) FROM table_name; + * T can be omitted with void. + */ + template + struct count_asterisk_t : count_string { + using type = T; - template - internal::built_in_function_t json_type(X x) { - return {std::tuple{std::forward(x)}}; - } + template + filtered_aggregate_function, W> filter(where_t wh) { + return {*this, std::move(wh.expression)}; + } + }; + + /** + * The same thing as count() but without T arg. + * Is used in cases like this: + * SELECT cust_code, cust_name, cust_city, grade + * FROM customer + * WHERE grade=2 AND EXISTS + * (SELECT COUNT(*) + * FROM customer + * WHERE grade=2 + * GROUP BY grade + * HAVING COUNT(*)>2); + * `c++` + * auto rows = + * storage.select(columns(&Customer::code, &Customer::name, &Customer::city, &Customer::grade), + * where(is_equal(&Customer::grade, 2) + * and exists(select(count(), + * where(is_equal(&Customer::grade, 2)), + * group_by(&Customer::grade), + * having(greater_than(count(), 2)))))); + */ + struct count_asterisk_without_type : count_string {}; - template - internal::built_in_function_t json_type(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + struct avg_string { + serialize_result_type serialize() const { + return "AVG"; + } + }; - template - internal::built_in_function_t json_type(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } + struct max_string { + serialize_result_type serialize() const { + return "MAX"; + } + }; - template - internal::built_in_function_t json_valid(X x) { - return {std::tuple{std::forward(x)}}; - } + struct min_string { + serialize_result_type serialize() const { + return "MIN"; + } + }; - template - internal::built_in_function_t json_quote(X x) { - return {std::tuple{std::forward(x)}}; - } + struct group_concat_string { + serialize_result_type serialize() const { + return "GROUP_CONCAT"; + } + }; +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS + struct acos_string { + serialize_result_type serialize() const { + return "ACOS"; + } + }; - template - internal::built_in_function_t json_group_array(X x) { - return {std::tuple{std::forward(x)}}; - } + struct acosh_string { + serialize_result_type serialize() const { + return "ACOSH"; + } + }; - template - internal::built_in_function_t json_group_object(X x, Y y) { - return {std::tuple{std::forward(x), std::forward(y)}}; - } -#endif // SQLITE_ENABLE_JSON1 + struct asin_string { + serialize_result_type serialize() const { + return "ASIN"; + } + }; - // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace - // to facilitate ADL (Argument Dependent Lookup) - namespace internal { - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - add_t, unwrap_expression_t> operator+(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + struct asinh_string { + serialize_result_type serialize() const { + return "ASINH"; + } + }; - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - sub_t, unwrap_expression_t> operator-(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + struct atan_string { + serialize_result_type serialize() const { + return "ATAN"; + } + }; - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - mul_t, unwrap_expression_t> operator*(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + struct atan2_string { + serialize_result_type serialize() const { + return "ATAN2"; + } + }; - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - div_t, unwrap_expression_t> operator/(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } + struct atanh_string { + serialize_result_type serialize() const { + return "ATANH"; + } + }; - template, - std::is_base_of, - is_operator_argument, - is_operator_argument>::value, - bool> = true> - mod_t, unwrap_expression_t> operator%(L l, R r) { - return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; - } - } + struct ceil_string { + serialize_result_type serialize() const { + return "CEIL"; + } + }; - template - internal::highlight_t highlight(X x, Y y, Z z) { - return {std::move(x), std::move(y), std::move(z)}; - } -} -#pragma once + struct ceiling_string { + serialize_result_type serialize() const { + return "CEILING"; + } + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include -#endif -#include // std::remove_const -#include // std::string -#include // std::move -#include // std::tuple, std::get, std::tuple_size -// #include "functional/cxx_optional.h" + struct cos_string { + serialize_result_type serialize() const { + return "COS"; + } + }; -// #include "functional/cxx_universal.h" -// ::size_t -// #include "functional/cxx_type_traits_polyfill.h" + struct cosh_string { + serialize_result_type serialize() const { + return "COSH"; + } + }; -// #include "is_base_of_template.h" + struct degrees_string { + serialize_result_type serialize() const { + return "DEGREES"; + } + }; -// #include "tuple_helper/tuple_traits.h" + struct exp_string { + serialize_result_type serialize() const { + return "EXP"; + } + }; -// #include "tuple_helper/tuple_transformer.h" + struct floor_string { + serialize_result_type serialize() const { + return "FLOOR"; + } + }; -#include // std::remove_reference, std::common_type, std::index_sequence, std::make_index_sequence, std::forward, std::move, std::integral_constant, std::declval -#include // std::tuple_size, std::get + struct ln_string { + serialize_result_type serialize() const { + return "LN"; + } + }; -// #include "../functional/cxx_universal.h" -// ::size_t -// #include "../functional/cxx_type_traits_polyfill.h" + struct log_string { + serialize_result_type serialize() const { + return "LOG"; + } + }; -// #include "../functional/cxx_functional_polyfill.h" + struct log10_string { + serialize_result_type serialize() const { + return "LOG10"; + } + }; -// #include "../functional/mpl.h" + struct log2_string { + serialize_result_type serialize() const { + return "LOG2"; + } + }; -namespace sqlite_orm { - namespace internal { + struct mod_string { + serialize_result_type serialize() const { + return "MOD"; + } + }; - template class Op> - struct tuple_transformer; + struct pi_string { + serialize_result_type serialize() const { + return "PI"; + } + }; - template class Pack, class... Types, template class Op> - struct tuple_transformer, Op> { - using type = Pack...>; + struct pow_string { + serialize_result_type serialize() const { + return "POW"; + } }; - /* - * 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))...); - } + struct power_string { + serialize_result_type serialize() const { + return "POWER"; + } + }; - /* - * Apply a projection to a tuple's elements, and combine the results. - * - * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. - * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). - * - * @note `project` is called using `std::invoke`, which is `constexpr` since C++20. - */ - template - SQLITE_ORM_CONSTEXPR_CPP20 auto - recombine_tuple(CombineOp combine, const Tpl& tpl, Projector project, Init initial) { - return recombine_tuple(std::move(combine), - std::forward(tpl), - std::make_index_sequence::value>{}, - std::move(project), - std::move(initial)); - } + struct radians_string { + serialize_result_type serialize() const { + return "RADIANS"; + } + }; - /* - * Function object that takes integral constants and returns the sum of their values as an integral constant. - * Because it's a "transparent" functor, it must be called with at least one argument, otherwise it cannot deduce the integral constant type. - */ - struct plus_fold_integrals { - template - constexpr auto operator()(const Integrals&...) const { - using integral_type = std::common_type_t; - return std::integral_constant{}; + struct sin_string { + serialize_result_type serialize() const { + return "SIN"; } }; - /* - * Function object that takes a type, applies a projection on it, and returns the tuple size of the projected type (as an integral constant). - * The projection is applied on the argument type, not the argument value/object. - */ - template class NestedProject> - struct project_nested_tuple_size { - template - constexpr auto operator()(const T&) const { - return typename std::tuple_size>::type{}; + struct sinh_string { + serialize_result_type serialize() const { + return "SINH"; } }; - template class NestedProject, class Tpl, class IdxSeq> - using nested_tuple_size_for_t = decltype(recombine_tuple(plus_fold_integrals{}, - std::declval(), - IdxSeq{}, - project_nested_tuple_size{}, - std::integral_constant{})); -#endif + struct sqrt_string { + serialize_result_type serialize() const { + return "SQRT"; + } + }; - template - constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { - return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; - } + struct tan_string { + serialize_result_type serialize() const { + return "TAN"; + } + }; - /* - * Like `std::make_from_tuple`, but using a projection on the tuple elements. - */ - template - constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) { - return create_from_tuple( - std::forward(tpl), - std::make_index_sequence>::value>{}, - std::forward(project)); - } - } -} + struct tanh_string { + serialize_result_type serialize() const { + return "TANH"; + } + }; -// #include "tuple_helper/tuple_iteration.h" + struct trunc_string { + serialize_result_type serialize() const { + return "TRUNC"; + } + }; -#include // std::get, std::tuple_element, std::tuple_size -#include // std::index_sequence, std::make_index_sequence -#include // std::forward, std::move +#endif // SQLITE_ENABLE_MATH_FUNCTIONS +#ifdef SQLITE_ENABLE_JSON1 + struct json_string { + serialize_result_type serialize() const { + return "JSON"; + } + }; -// #include "../functional/cxx_universal.h" -// ::size_t + struct json_array_string { + serialize_result_type serialize() const { + return "JSON_ARRAY"; + } + }; -namespace sqlite_orm { - namespace internal { -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) - template - void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { - if constexpr(reversed) { - // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= - int sink; - // note: `(void)` cast silences warning 'expression result unused' - (void)((lambda(std::get(tpl)), sink) = ... = 0); - } else { - (lambda(std::get(tpl)), ...); + struct json_array_length_string { + serialize_result_type serialize() const { + return "JSON_ARRAY_LENGTH"; } - } -#else - template - void iterate_tuple(Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} + }; - template - void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { - if SQLITE_ORM_CONSTEXPR_IF(reversed) { - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); - lambda(std::get(tpl)); - } else { - lambda(std::get(tpl)); - iterate_tuple(tpl, std::index_sequence{}, std::forward(lambda)); + struct json_extract_string { + serialize_result_type serialize() const { + return "JSON_EXTRACT"; } - } -#endif - template - void iterate_tuple(Tpl&& tpl, L&& lambda) { - iterate_tuple(tpl, - std::make_index_sequence>::value>{}, - std::forward(lambda)); - } + }; -#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED - template - void iterate_tuple(std::index_sequence, L&& lambda) { - (lambda((std::tuple_element_t*)nullptr), ...); - } -#else - template - void iterate_tuple(std::index_sequence, L&& lambda) { - using Sink = int[sizeof...(Idx)]; - (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; - } -#endif - template - void iterate_tuple(L&& lambda) { - iterate_tuple(std::make_index_sequence::value>{}, std::forward(lambda)); - } + struct json_insert_string { + serialize_result_type serialize() const { + return "JSON_INSERT"; + } + }; - template class Base, class L> - struct lambda_as_template_base : L { -#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED - lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} -#endif - template - decltype(auto) operator()(const Base& object) { - return L::operator()(object); + struct json_replace_string { + serialize_result_type serialize() const { + return "JSON_REPLACE"; } }; - /* - * This method wraps the specified callable in another function object, - * which in turn implicitly casts its single argument to the specified template base class, - * then passes the converted argument to the lambda. - * - * Note: This method is useful for reducing combinatorial instantiation of template lambdas, - * as long as this library supports compilers that do not implement - * explicit template parameters in generic lambdas [SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED]. - * Unfortunately it doesn't work with user-defined conversion operators in order to extract - * parts of a class. In other words, the destination type must be a direct template base class. - */ - template class Base, class L> - lambda_as_template_base call_as_template_base(L lambda) { - return {std::move(lambda)}; - } - } -} + struct json_set_string { + serialize_result_type serialize() const { + return "JSON_SET"; + } + }; -// #include "optional_container.h" + struct json_object_string { + serialize_result_type serialize() const { + return "JSON_OBJECT"; + } + }; -// #include "ast/where.h" + struct json_patch_string { + serialize_result_type serialize() const { + return "JSON_PATCH"; + } + }; -#include // std::false_type, std::true_type -#include // std::move + struct json_remove_string { + serialize_result_type serialize() const { + return "JSON_REMOVE"; + } + }; -// #include "../functional/cxx_universal.h" + struct json_type_string { + serialize_result_type serialize() const { + return "JSON_TYPE"; + } + }; -// #include "../functional/cxx_type_traits_polyfill.h" + struct json_valid_string { + serialize_result_type serialize() const { + return "JSON_VALID"; + } + }; -// #include "../serialize_result_type.h" + struct json_quote_string { + serialize_result_type serialize() const { + return "JSON_QUOTE"; + } + }; -namespace sqlite_orm { - namespace internal { + struct json_group_array_string { + serialize_result_type serialize() const { + return "JSON_GROUP_ARRAY"; + } + }; - struct where_string { + struct json_group_object_string { serialize_result_type serialize() const { - return "WHERE"; + return "JSON_GROUP_OBJECT"; } }; +#endif // SQLITE_ENABLE_JSON1 - /** - * WHERE argument holder. - * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc - * Don't construct it manually. Call `where(...)` function instead. - */ - template - struct where_t : where_string { - using expression_type = C; + template + using field_type_or_type_t = polyfill::detected_or_t>; - expression_type expression; + template + struct highlight_t { + using table_type = T; + using argument0_type = X; + using argument1_type = Y; + using argument2_type = Z; - where_t(expression_type expression_) : expression(std::move(expression_)) {} + argument0_type argument0; + argument1_type argument1; + argument2_type argument2; + + highlight_t(argument0_type argument0, argument1_type argument1, argument2_type argument2) : + argument0(std::move(argument0)), argument1(std::move(argument1)), argument2(std::move(argument2)) {} }; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of::value; +#ifdef SQLITE_ENABLE_MATH_FUNCTIONS - template - struct is_where : polyfill::bool_constant> {}; + /** + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t acos(X x) { + return {std::tuple{std::forward(x)}}; } /** - * 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))); + * ACOS(X) function https://www.sqlite.org/lang_mathfunc.html#acos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acos>(&Triangle::cornerA)); // decltype(rows) is std::vector> */ - template - internal::where_t where(C expression) { - return {std::move(expression)}; + template + internal::built_in_function_t acos(X x) { + return {std::tuple{std::forward(x)}}; } -} -// #include "ast/group_by.h" + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t acosh(X x) { + return {std::tuple{std::forward(x)}}; + } -#include // std::tuple, std::make_tuple -#include // std::true_type, std::false_type -#include // std::forward, std::move + /** + * ACOSH(X) function https://www.sqlite.org/lang_mathfunc.html#acosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::acosh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t acosh(X x) { + return {std::tuple{std::forward(x)}}; + } -// #include "../functional/cxx_type_traits_polyfill.h" + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t asin(X x) { + return {std::tuple{std::forward(x)}}; + } -namespace sqlite_orm { - namespace internal { + /** + * ASIN(X) function https://www.sqlite.org/lang_mathfunc.html#asin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asin>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t asin(X x) { + return {std::tuple{std::forward(x)}}; + } - template - struct group_by_with_having { - using args_type = std::tuple; - using expression_type = T; + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t asinh(X x) { + return {std::tuple{std::forward(x)}}; + } - args_type args; - expression_type expression; - }; + /** + * ASINH(X) function https://www.sqlite.org/lang_mathfunc.html#asinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::asinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t asinh(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * GROUP BY pack holder. - */ - template - struct group_by_t { - using args_type = std::tuple; + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan(1)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atan(X x) { + return {std::tuple{std::forward(x)}}; + } - args_type args; + /** + * ATAN(X) function https://www.sqlite.org/lang_mathfunc.html#atan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan>(1)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atan(X x) { + return {std::tuple{std::forward(x)}}; + } - template - group_by_with_having having(T expression) { - return {std::move(this->args), std::move(expression)}; - } - }; + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2(1, 3)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atan2(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - template - using is_group_by = polyfill::disjunction, - polyfill::is_specialization_of>; + /** + * ATAN2(X, Y) function https://www.sqlite.org/lang_mathfunc.html#atan2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atan2>(1, 3)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t atan2(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh(1)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t atanh(X x) { + return {std::tuple{std::forward(x)}}; } /** - * GROUP BY column. - * Example: storage.get_all(group_by(&Employee::name)) + * ATANH(X) function https://www.sqlite.org/lang_mathfunc.html#atanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::atanh>(1)); // decltype(rows) is std::vector> */ - template - internal::group_by_t group_by(Args... args) { - return {{std::forward(args)...}}; + template + internal::built_in_function_t atanh(X x) { + return {std::tuple{std::forward(x)}}; } -} - -// #include "core_functions.h" - -// #include "alias_traits.h" - -// #include "cte_moniker.h" - -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include -#include // std::make_index_sequence -#endif -#include // std::enable_if, std::is_member_pointer, std::is_same, std::is_convertible -#include // std::ignore -#include -#endif - -// #include "functional/cxx_universal.h" - -// #include "functional/cstring_literal.h" -// #include "alias.h" + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ceil(X x) { + return {std::tuple{std::forward(x)}}; + } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -namespace sqlite_orm { + /** + * CEIL(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceil>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ceil(X x) { + return {std::tuple{std::forward(x)}}; + } - 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 - }; + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ceiling(X x) { + return {std::tuple{std::forward(x)}}; } - 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{}; - } + /** + * CEILING(X) function https://www.sqlite.org/lang_mathfunc.html#ceil + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ceiling>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ceiling(X x) { + return {std::tuple{std::forward(x)}}; + } -#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 + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t cos(X x) { + return {std::tuple{std::forward(x)}}; } -} -#endif -namespace sqlite_orm { + /** + * COS(X) function https://www.sqlite.org/lang_mathfunc.html#cos + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cos>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t cos(X x) { + return {std::tuple{std::forward(x)}}; + } - namespace internal { -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct as_optional_t { - using value_type = T; + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t cosh(X x) { + return {std::tuple{std::forward(x)}}; + } - value_type value; - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + /** + * COSH(X) function https://www.sqlite.org/lang_mathfunc.html#cosh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::cosh>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t cosh(X x) { + return {std::tuple{std::forward(x)}}; + } - struct distinct_string { - operator std::string() const { - return "DISTINCT"; - } - }; + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t degrees(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * DISCTINCT generic container. - */ - template - struct distinct_t : distinct_string { - using value_type = T; + /** + * DEGREES(X) function https://www.sqlite.org/lang_mathfunc.html#degrees + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::degrees>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t degrees(X x) { + return {std::tuple{std::forward(x)}}; + } - value_type value; + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp(&Triangle::cornerB)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t exp(X x) { + return {std::tuple{std::forward(x)}}; + } - distinct_t(value_type value_) : value(std::move(value_)) {} - }; + /** + * EXP(X) function https://www.sqlite.org/lang_mathfunc.html#exp + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::exp>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t exp(X x) { + return {std::tuple{std::forward(x)}}; + } - struct all_string { - operator std::string() const { - return "ALL"; - } - }; + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor(&User::rating)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t floor(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * ALL generic container. - */ - template - struct all_t : all_string { - T value; + /** + * FLOOR(X) function https://www.sqlite.org/lang_mathfunc.html#floor + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::floor>(&User::rating)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t floor(X x) { + return {std::tuple{std::forward(x)}}; + } - all_t(T value_) : value(std::move(value_)) {} - }; + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln(200)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t ln(X x) { + return {std::tuple{std::forward(x)}}; + } - template - struct columns_t { - using columns_type = std::tuple; + /** + * LN(X) function https://www.sqlite.org/lang_mathfunc.html#ln + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::ln>(200)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t ln(X x) { + return {std::tuple{std::forward(x)}}; + } - columns_type columns; - bool distinct = false; + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log(X x) { + return {std::tuple{std::forward(x)}}; + } - static constexpr int count = std::tuple_size::value; + /** + * LOG(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log>(100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log(X x) { + return {std::tuple{std::forward(x)}}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - columns_t(columns_type columns) : columns{std::move(columns)} {} -#endif - }; + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10(100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log10(X x) { + return {std::tuple{std::forward(x)}}; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_columns_v = polyfill::is_specialization_of::value; + /** + * LOG10(X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log10>(100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log10(X x) { + return {std::tuple{std::forward(x)}}; + } - template - using is_columns = polyfill::bool_constant>; + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log(10, 100)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log(B b, X x) { + return {std::tuple{std::forward(b), std::forward(x)}}; + } - /* - * Captures the type of an aggregate/structure/object and column expressions, such that - * `T` can be constructed in-place as part of a result row. - * `T` must be constructible using direct-list-initialization. - */ - template - struct struct_t { - using columns_type = std::tuple; + /** + * LOG(B, X) function https://www.sqlite.org/lang_mathfunc.html#log + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log>(10, 100)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log(B b, X x) { + return {std::tuple{std::forward(b), std::forward(x)}}; + } - columns_type columns; - bool distinct = false; + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2(64)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t log2(X x) { + return {std::tuple{std::forward(x)}}; + } - static constexpr int count = std::tuple_size::value; + /** + * LOG2(X) function https://www.sqlite.org/lang_mathfunc.html#log2 + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::log2>(64)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t log2(X x) { + return {std::tuple{std::forward(x)}}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - struct_t(columns_type columns) : columns{std::move(columns)} {} -#endif - }; + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f(6, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t mod_f(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + /** + * MOD(X, Y) function https://www.sqlite.org/lang_mathfunc.html#mod + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::mod_f>(6, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t mod_f(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_struct_v = polyfill::is_specialization_of::value; + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector + */ + inline internal::built_in_function_t pi() { + return {{}}; + } - template - using is_struct = polyfill::bool_constant>; + /** + * PI() function https://www.sqlite.org/lang_mathfunc.html#pi + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, etc. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pi()); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t pi() { + return {{}}; + } - /** - * Subselect object type. - */ - template - struct select_t { - using return_type = T; - using conditions_type = std::tuple; + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow(2, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t pow(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - return_type col; - conditions_type conditions; - bool highest_level = false; + /** + * POW(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::pow>(2, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t pow(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - select_t(return_type col, conditions_type conditions) : - col{std::move(col)}, conditions{std::move(conditions)} {} -#endif - }; + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power(2, 5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t power(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_select_v = polyfill::is_specialization_of::value; + /** + * POWER(X, Y) function https://www.sqlite.org/lang_mathfunc.html#pow + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::power>(2, 5)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t power(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - template - using is_select = polyfill::bool_constant>; + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t radians(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * Base for UNION, UNION ALL, EXCEPT and INTERSECT - */ - template - struct compound_operator { - using expressions_tuple = std::tuple; + /** + * RADIANS(X) function https://www.sqlite.org/lang_mathfunc.html#radians + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::radians>(&Triangle::cornerAInDegrees)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t radians(X x) { + return {std::tuple{std::forward(x)}}; + } - expressions_tuple compound; + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sin(X x) { + return {std::tuple{std::forward(x)}}; + } - compound_operator(expressions_tuple compound) : compound{std::move(compound)} { - iterate_tuple(this->compound, [](auto& expression) { - expression.highest_level = true; - }); - } - }; + /** + * SIN(X) function https://www.sqlite.org/lang_mathfunc.html#sin + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sin>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t sin(X x) { + return {std::tuple{std::forward(x)}}; + } - template - SQLITE_ORM_INLINE_VAR constexpr bool is_compound_operator_v = is_base_of_template::value; + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh(&Triangle::cornerA)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sinh(X x) { + return {std::tuple{std::forward(x)}}; + } - template - using is_compound_operator = polyfill::bool_constant>; + /** + * SINH(X) function https://www.sqlite.org/lang_mathfunc.html#sinh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sinh>(&Triangle::cornerA)); // decltype(rows) is std::vector> + */ + template + internal::built_in_function_t sinh(X x) { + return {std::tuple{std::forward(x)}}; + } - struct union_base { - bool all = false; + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sqrt(X x) { + return {std::tuple{std::forward(x)}}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - union_base(bool all) : all{all} {} -#endif + /** + * SQRT(X) function https://www.sqlite.org/lang_mathfunc.html#sqrt + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::sqrt(25)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t sqrt(X x) { + return {std::tuple{std::forward(x)}}; + } - operator std::string() const { - if(!this->all) { - return "UNION"; - } else { - return "UNION ALL"; - } - } - }; + /** + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tan(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * UNION object type. - */ - template - struct union_t : public compound_operator, union_base { - using typename compound_operator::expressions_tuple; + /** + * TAN(X) function https://www.sqlite.org/lang_mathfunc.html#tan + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tan(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tan(X x) { + return {std::tuple{std::forward(x)}}; + } - union_t(expressions_tuple compound, bool all) : - compound_operator{std::move(compound)}, union_base{all} {} - }; + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tanh(X x) { + return {std::tuple{std::forward(x)}}; + } - struct except_string { - operator std::string() const { - return "EXCEPT"; - } - }; + /** + * TANH(X) function https://www.sqlite.org/lang_mathfunc.html#tanh + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::tanh(&Triangle::cornerC)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t tanh(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * EXCEPT object type. - */ - template - struct except_t : compound_operator, except_string { - using super = compound_operator; + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t trunc(X x) { + return {std::tuple{std::forward(x)}}; + } - using super::super; - }; + /** + * TRUNC(X) function https://www.sqlite.org/lang_mathfunc.html#trunc + * + * Difference with the previous function is that previous override has `double` as return type but this + * override accepts return type from you as a template argument. You can use any bindable type: + * `float`, `int`, `std::optional` etc. This override is handy when you expect `null` as result. + * + * Example: + * + * auto rows = storage.select(sqlite_orm::trunc(5.5)); // decltype(rows) is std::vector + */ + template + internal::built_in_function_t trunc(X x) { + return {std::tuple{std::forward(x)}}; + } +#endif // SQLITE_ENABLE_MATH_FUNCTIONS + /** + * TYPEOF(x) function https://sqlite.org/lang_corefunc.html#typeof + */ + template + internal::built_in_function_t typeof_(T t) { + return {std::tuple{std::forward(t)}}; + } - struct intersect_string { - operator std::string() const { - return "INTERSECT"; - } - }; - /** - * INTERSECT object type. - */ - template - struct intersect_t : compound_operator, intersect_string { - using super = compound_operator; + /** + * UNICODE(x) function https://sqlite.org/lang_corefunc.html#unicode + */ + template + internal::built_in_function_t unicode(T t) { + return {std::tuple{std::forward(t)}}; + } - using super::super; - }; + /** + * LENGTH(x) function https://sqlite.org/lang_corefunc.html#length + */ + template + internal::built_in_function_t length(T t) { + return {std::tuple{std::forward(t)}}; + } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - /* - * Turn explicit columns for a CTE into types that the CTE backend understands - */ - template - struct decay_explicit_column { - using type = T; - }; - template - struct decay_explicit_column> { - using type = alias_holder; - }; - template - struct decay_explicit_column> { - using type = std::string; - }; - template - using decay_explicit_column_t = typename decay_explicit_column::type; + /** + * ABS(x) function https://sqlite.org/lang_corefunc.html#abs + */ + template + internal::built_in_function_t, internal::abs_string, T> abs(T t) { + return {std::tuple{std::forward(t)}}; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /* - * Materialization hint to instruct SQLite to materialize the select statement of a CTE into an ephemeral table as an "optimization fence". - */ - struct materialized_t {}; + /** + * LOWER(x) function https://sqlite.org/lang_corefunc.html#lower + */ + template + internal::built_in_function_t lower(T t) { + return {std::tuple{std::forward(t)}}; + } - /* - * Materialization hint to instruct SQLite to substitute a CTE's select statement as a subquery subject to optimization. - */ - struct not_materialized_t {}; -#endif + /** + * UPPER(x) function https://sqlite.org/lang_corefunc.html#upper + */ + template + internal::built_in_function_t upper(T t) { + return {std::tuple{std::forward(t)}}; + } - /** - * Monikered (aliased) CTE expression. - */ - template - struct common_table_expression { - using cte_moniker_type = Moniker; - using expression_type = Select; - using explicit_colrefs_tuple = ExplicitCols; - using hints_tuple = Hints; - static constexpr size_t explicit_colref_count = std::tuple_size_v; + /** + * LAST_INSERT_ROWID(x) function https://www.sqlite.org/lang_corefunc.html#last_insert_rowid + */ + inline internal::built_in_function_t last_insert_rowid() { + return {{}}; + } - SQLITE_ORM_NOUNIQUEADDRESS hints_tuple hints; - explicit_colrefs_tuple explicitColumns; - expression_type subselect; + /** + * TOTAL_CHANGES() function https://sqlite.org/lang_corefunc.html#total_changes + */ + inline internal::built_in_function_t total_changes() { + return {{}}; + } - common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : - explicitColumns{std::move(explicitColumns)}, subselect{std::move(subselect)} { - this->subselect.highest_level = true; - } - }; + /** + * CHANGES() function https://sqlite.org/lang_corefunc.html#changes + */ + inline internal::built_in_function_t changes() { + return {{}}; + } - template - using common_table_expressions = std::tuple; + /** + * TRIM(X) function https://sqlite.org/lang_corefunc.html#trim + */ + template + internal::built_in_function_t trim(T t) { + return {std::tuple{std::forward(t)}}; + } - template - struct cte_builder { - ExplicitCols explicitColumns; + /** + * TRIM(X,Y) function https://sqlite.org/lang_corefunc.html#trim + */ + template + internal::built_in_function_t trim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } -#if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) - template = true> - common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; - } + /** + * LTRIM(X) function https://sqlite.org/lang_corefunc.html#ltrim + */ + template + internal::built_in_function_t ltrim(X x) { + return {std::tuple{std::forward(x)}}; + } - template = true> - common_table_expression, select_t> - as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; - } -#else - template = true> - common_table_expression, Select> as(Select sel) && { - return {std::move(this->explicitColumns), std::move(sel)}; - } + /** + * LTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#ltrim + */ + template + internal::built_in_function_t ltrim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - template = true> - common_table_expression, select_t> as(Compound sel) && { - return {std::move(this->explicitColumns), {std::move(sel)}}; - } -#endif - }; + /** + * RTRIM(X) function https://sqlite.org/lang_corefunc.html#rtrim + */ + template + internal::built_in_function_t rtrim(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * WITH object type - expression with prepended CTEs. - */ - template - struct with_t { - using cte_type = common_table_expressions; - using expression_type = E; + /** + * RTRIM(X,Y) function https://sqlite.org/lang_corefunc.html#rtrim + */ + template + internal::built_in_function_t rtrim(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - bool recursiveIndicated; - cte_type cte; - expression_type expression; + /** + * HEX(X) function https://sqlite.org/lang_corefunc.html#hex + */ + template + internal::built_in_function_t hex(X x) { + return {std::tuple{std::forward(x)}}; + } - with_t(bool recursiveIndicated, cte_type cte, expression_type expression) : - recursiveIndicated{recursiveIndicated}, cte{std::move(cte)}, expression{std::move(expression)} { - if constexpr(is_select_v) { - this->expression.highest_level = true; - } - } - }; -#endif + /** + * QUOTE(X) function https://sqlite.org/lang_corefunc.html#quote + */ + template + internal::built_in_function_t quote(X x) { + return {std::tuple{std::forward(x)}}; + } - /** - * Generic way to get DISTINCT value from any type. - */ - template - bool get_distinct(const T&) { - return false; - } + /** + * RANDOMBLOB(X) function https://sqlite.org/lang_corefunc.html#randomblob + */ + template + internal::built_in_function_t, internal::randomblob_string, X> randomblob(X x) { + return {std::tuple{std::forward(x)}}; + } - template - bool get_distinct(const columns_t& cols) { - return cols.distinct; - } + /** + * INSTR(X) function https://sqlite.org/lang_corefunc.html#instr + */ + template + internal::built_in_function_t instr(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - template - bool get_distinct(const struct_t& cols) { - return cols.distinct; - } + /** + * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace + */ + template, internal::is_into>::value == 0, bool> = true> + internal::built_in_function_t replace(X x, Y y, Z z) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; + } - template - struct asterisk_t { - using type = T; + /** + * ROUND(X) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::built_in_function_t round(X x) { + return {std::tuple{std::forward(x)}}; + } - bool defined_order = false; + /** + * ROUND(X, Y) function https://sqlite.org/lang_corefunc.html#round + */ + template + internal::built_in_function_t round(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - asterisk_t(bool definedOrder) : defined_order{definedOrder} {} +#if SQLITE_VERSION_NUMBER >= 3007016 + /** + * CHAR(X1,X2,...,XN) function https://sqlite.org/lang_corefunc.html#char + */ + template + internal::built_in_function_t char_(Args... args) { + return {std::make_tuple(std::forward(args)...)}; + } + + /** + * RANDOM() function https://www.sqlite.org/lang_corefunc.html#random + */ + inline internal::built_in_function_t random() { + return {{}}; + } #endif - }; - template - struct object_t { - using type = T; + /** + * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce + */ + template + auto coalesce(Args... args) + -> internal::built_in_function_t::value, + std::common_type...>, + polyfill::type_identity>::type, + internal::coalesce_string, + Args...> { + return {std::make_tuple(std::forward(args)...)}; + } - bool defined_order = false; + /** + * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull + */ + template + auto ifnull(X x, Y y) -> internal::built_in_function_t< + typename mpl::conditional_t< // choose R or common type + std::is_void::value, + std::common_type, internal::field_type_or_type_t>, + polyfill::type_identity>::type, + internal::ifnull_string, + X, + Y> { + return {std::make_tuple(std::move(x), std::move(y))}; + } -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - object_t(bool definedOrder) : defined_order{definedOrder} {} -#endif - }; + /** + * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif + */ +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + /** + * NULLIF(X,Y) using common return type of X and Y + */ + template>, + polyfill::is_detected, + internal::field_type_or_type_t>>, + bool> = true> + auto nullif(X x, Y y) { + if constexpr(std::is_void_v) { + using F = internal::built_in_function_t< + std::optional, internal::field_type_or_type_t>>, + internal::nullif_string, + X, + Y>; - template - struct then_t { - using expression_type = T; + return F{std::make_tuple(std::move(x), std::move(y))}; + } else { + using F = internal::built_in_function_t; - expression_type expression; - }; + return F{std::make_tuple(std::move(x), std::move(y))}; + } + } +#else + template + internal::built_in_function_t nullif(X x, Y y) { + return {std::make_tuple(std::move(x), std::move(y))}; + } +#endif - template - struct simple_case_t { - using return_type = R; - using case_expression_type = T; - using args_type = std::tuple; - using else_expression_type = E; + /** + * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::built_in_function_t date(Args... args) { + return {std::tuple{std::forward(args)...}}; + } - optional_container case_expression; - args_type args; - optional_container else_expression; - }; + /** + * TIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::built_in_function_t time(Args... args) { + return {std::tuple{std::forward(args)...}}; + } - /** - * T is a case expression type - * E is else type (void is ELSE is omitted) - * Args... is a pack of WHEN expressions - */ - template - struct simple_case_builder { - using return_type = R; - using case_expression_type = T; - using args_type = std::tuple; - using else_expression_type = E; + /** + * DATETIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::built_in_function_t datetime(Args... args) { + return {std::tuple{std::forward(args)...}}; + } - optional_container case_expression; - args_type args; - optional_container else_expression; + /** + * JULIANDAY(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::built_in_function_t julianday(Args... args) { + return {std::tuple{std::forward(args)...}}; + } - template - simple_case_builder> when(W w, then_t t) { - using result_args_type = std::tuple>; - std::pair newPair{std::move(w), std::move(t.expression)}; - result_args_type result_args = std::tuple_cat(std::move(this->args), std::make_tuple(newPair)); - std::get::value - 1>(result_args) = std::move(newPair); - return {std::move(this->case_expression), std::move(result_args), std::move(this->else_expression)}; - } + /** + * STRFTIME(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html + */ + template + internal::built_in_function_t strftime(Args... args) { + return {std::tuple{std::forward(args)...}}; + } - simple_case_t end() { - return {std::move(this->case_expression), std::move(args), std::move(this->else_expression)}; - } + /** + * ZEROBLOB(N) function https://www.sqlite.org/lang_corefunc.html#zeroblob + */ + template + internal::built_in_function_t, internal::zeroblob_string, N> zeroblob(N n) { + return {std::tuple{std::forward(n)}}; + } - template - simple_case_builder else_(El el) { - return {{std::move(this->case_expression)}, std::move(args), {std::move(el)}}; - } - }; + /** + * SUBSTR(X,Y) function https://www.sqlite.org/lang_corefunc.html#substr + */ + template + internal::built_in_function_t substr(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - template - void validate_conditions() { - static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); - static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); - static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); - static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 LIMIT blocks"); - static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 FROM blocks"); - } + /** + * SUBSTR(X,Y,Z) function https://www.sqlite.org/lang_corefunc.html#substr + */ + template + internal::built_in_function_t substr(X x, Y y, Z z) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(z)}}; } -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - internal::as_optional_t as_optional(T value) { - return {std::move(value)}; +#ifdef SQLITE_SOUNDEX + /** + * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex + */ + template + internal::built_in_function_t soundex(X x) { + return {std::tuple{std::forward(x)}}; } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - internal::then_t then(T t) { - return {std::move(t)}; +#endif + + /** + * TOTAL(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t total(X x) { + return {std::tuple{std::forward(x)}}; } - template - internal::simple_case_builder case_(T t) { - return {{std::move(t)}}; + /** + * SUM(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t, internal::sum_string, X> sum(X x) { + return {std::tuple{std::forward(x)}}; } - template - internal::simple_case_builder case_() { + /** + * COUNT(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t count(X x) { + return {std::tuple{std::forward(x)}}; + } + + /** + * COUNT(*) without FROM function. + */ + inline internal::count_asterisk_without_type count() { return {}; } + /** + * COUNT(*) with FROM function. Specified type T will be serialized as + * a from argument. + */ template - internal::distinct_t distinct(T t) { - return {std::move(t)}; + internal::count_asterisk_t count() { + return {}; } - template - internal::all_t all(T t) { - return {std::move(t)}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * COUNT(*) with FROM function. Specified recordset will be serialized as + * a from argument. + */ + template + auto count() { + return count>(); } +#endif - template - internal::columns_t distinct(internal::columns_t cols) { - cols.distinct = true; - return cols; + /** + * AVG(X) aggregate function. + */ + template + internal::built_in_aggregate_function_t avg(X x) { + return {std::tuple{std::forward(x)}}; } - /* - * Combine multiple columns in a tuple. + /** + * MAX(X) aggregate function. */ - template - constexpr internal::columns_t columns(Args... args) { - return {std::make_tuple(std::forward(args)...)}; + template + internal::built_in_aggregate_function_t, internal::max_string, X> max(X x) { + return {std::tuple{std::forward(x)}}; } - /* - * Construct an unmapped structure ad-hoc from multiple columns. - * `T` must be constructible from the column results using direct-list-initialization. + /** + * MIN(X) aggregate function. */ - template - constexpr internal::struct_t struct_(Args... args) { - return {std::make_tuple(std::forward(args)...)}; + template + internal::built_in_aggregate_function_t, internal::min_string, X> min(X x) { + return {std::tuple{std::forward(x)}}; } /** - * Public function for subselect query. Is useful in UNION queries. + * MAX(X, Y, ...) scalar function. + * The return type is the type of the first argument. */ - template - internal::select_t select(T t, Args... args) { - using args_tuple = std::tuple; - internal::validate_conditions(); - return {std::move(t), {std::forward(args)...}}; + template + internal::built_in_function_t, internal::max_string, X, Y, Rest...> + max(X x, Y y, Rest... rest) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; } /** - * Public function for UNION operator. - * Expressions are subselect objects. - * Look through example in examples/union.cpp + * MIN(X, Y, ...) scalar function. + * The return type is the type of the first argument. */ - template - internal::union_t union_(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); - return {{std::forward(expressions)...}, false}; + template + internal::built_in_function_t, internal::min_string, X, Y, Rest...> + min(X x, Y y, Rest... rest) { + return {std::tuple{std::forward(x), std::forward(y), std::forward(rest)...}}; } /** - * Public function for UNION ALL operator. - * Expressions are subselect objects. - * Look through example in examples/union.cpp + * GROUP_CONCAT(X) aggregate function. */ - template - internal::union_t union_all(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); - return {{std::forward(expressions)...}, true}; + template + internal::built_in_aggregate_function_t group_concat(X x) { + return {std::tuple{std::forward(x)}}; } /** - * Public function for EXCEPT operator. - * Expressions are subselect objects. - * Look through example in examples/except.cpp + * GROUP_CONCAT(X, Y) aggregate function. */ - template - internal::except_t except(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); - return {{std::forward(expressions)...}}; + template + internal::built_in_aggregate_function_t group_concat(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } +#ifdef SQLITE_ENABLE_JSON1 + template + internal::built_in_function_t json(X x) { + return {std::tuple{std::forward(x)}}; } - template - internal::intersect_t intersect(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); - return {{std::forward(expressions)...}}; + template + internal::built_in_function_t json_array(Args... args) { + return {std::tuple{std::forward(args)...}}; } -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#if SQLITE_VERSION_NUMBER >= 3035003 -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /* - * Materialization hint to instruct SQLite to materialize the select statement of a CTE into an ephemeral table as an "optimization fence". - * - * Example: - * 1_ctealias().as(select(1)); - */ - inline consteval internal::materialized_t materialized() { - return {}; + template + internal::built_in_function_t json_array_length(X x) { + return {std::tuple{std::forward(x)}}; } - /* - * Materialization hint to instruct SQLite to substitute a CTE's select statement as a subquery subject to optimization. - * - * Example: - * 1_ctealias().as(select(1)); - */ - inline consteval internal::not_materialized_t not_materialized() { - return {}; + template + internal::built_in_function_t json_array_length(X x) { + return {std::tuple{std::forward(x)}}; + } + + template + internal::built_in_function_t json_array_length(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_array_length(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } + + template + internal::built_in_function_t json_extract(X x, Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_insert(X x, + Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_insert must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_replace(X x, + Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_replace must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; + } + + template + internal::built_in_function_t json_set(X x, Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_set must be odd"); + return {std::tuple{std::forward(x), std::forward(args)...}}; } -#endif -#endif - - /** - * Introduce the construction of a common table expression using the specified moniker. - * - * The list of explicit columns is optional; - * if provided the number of columns must match the number of columns of the subselect. - * The column names will be merged with the subselect: - * 1. column names of subselect - * 2. explicit columns - * 3. fill in empty column names with column index - * - * Example: - * using cte_1 = decltype(1_ctealias); - * cte()(select(&Object::id)); - * cte(&Object::name)(select("object")); - */ - template, - std::is_member_pointer, - internal::is_column, - std::is_same>, - std::is_convertible>...>, - bool> = true> - auto cte(ExplicitCols... explicitColumns) { - using namespace ::sqlite_orm::internal; - static_assert(is_cte_moniker_v, "Moniker must be a CTE moniker"); - static_assert((!is_builtin_numeric_column_alias_v && ...), - "Numeric column aliases are reserved for referencing columns locally within a single CTE."); - using builder_type = - cte_builder, decay_explicit_column_t>>; - return builder_type{{std::move(explicitColumns)...}}; + template + internal::built_in_function_t json_object(Args... args) { + static_assert(std::tuple_size>::value % 2 == 0, + "number of arguments in json_object must be even"); + return {std::tuple{std::forward(args)...}}; } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - requires((internal::is_column_alias_v || std::is_member_pointer_v || - internal::is_column_v || - std::same_as> || - std::convertible_to) && - ...) - auto cte(ExplicitCols... explicitColumns) { - using namespace ::sqlite_orm::internal; - static_assert((!is_builtin_numeric_column_alias_v && ...), - "Numeric column aliases are reserved for referencing columns locally within a single CTE."); + template + internal::built_in_function_t json_patch(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; + } - using builder_type = - cte_builder, decay_explicit_column_t>>; - return builder_type{{std::move(explicitColumns)...}}; + template + internal::built_in_function_t json_remove(X x, + Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; } -#endif - namespace internal { -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - template - requires((is_column_alias_v || std::is_member_pointer_v || - std::same_as> || - std::convertible_to) && - ...) - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { - return cte>(std::forward(explicitColumns)...); - } -#else - template - template, - std::is_member_pointer, - std::is_same>, - std::is_convertible>...>, - bool>> - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { - return cte>(std::forward(explicitColumns)...); - } -#endif + template + internal::built_in_function_t json_remove(X x, Args... args) { + return {std::tuple{std::forward(x), std::forward(args)...}}; } - /** - * With-clause for a tuple of ordinary CTEs. - * - * Despite the missing RECURSIVE keyword, the CTEs can be recursive. - */ - template = true> - internal::with_t with(internal::common_table_expressions ctes, E expression) { - return {false, std::move(ctes), std::move(expression)}; + template + internal::built_in_function_t json_type(X x) { + return {std::tuple{std::forward(x)}}; } - /** - * With-clause for a tuple of ordinary CTEs. - * - * Despite the missing RECURSIVE keyword, the CTEs can be recursive. - */ - template = true> - internal::with_t, CTEs...> with(internal::common_table_expressions ctes, - Compound sel) { - return {false, std::move(ctes), sqlite_orm::select(std::move(sel))}; + template + internal::built_in_function_t json_type(X x) { + return {std::tuple{std::forward(x)}}; } - /** - * With-clause for a single ordinary CTE. - * - * Despite the missing `RECURSIVE` keyword, the CTE can be recursive. - * - * Example: - * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; - * with(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); - */ - template = true, - internal::satisfies_not = true> - internal::with_t with(CTE cte, E expression) { - return {false, {std::move(cte)}, std::move(expression)}; + template + internal::built_in_function_t json_type(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } - /** - * With-clause for a single ordinary CTE. - * - * Despite the missing `RECURSIVE` keyword, the CTE can be recursive. - * - * Example: - * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; - * with(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); - */ - template = true, - internal::satisfies = true> - internal::with_t, CTE> with(CTE cte, Compound sel) { - return {false, {std::move(cte)}, sqlite_orm::select(std::move(sel))}; + template + internal::built_in_function_t json_type(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } - /** - * With-clause for a tuple of potentially recursive CTEs. - * - * @note The use of RECURSIVE does not force common table expressions to be recursive. - */ - template = true> - internal::with_t with_recursive(internal::common_table_expressions ctes, E expression) { - return {true, std::move(ctes), std::move(expression)}; + template + internal::built_in_function_t json_valid(X x) { + return {std::tuple{std::forward(x)}}; } - /** - * With-clause for a tuple of potentially recursive CTEs. - * - * @note The use of RECURSIVE does not force common table expressions to be recursive. - */ - template = true> - internal::with_t, CTEs...> - with_recursive(internal::common_table_expressions ctes, Compound sel) { - return {true, std::move(ctes), sqlite_orm::select(std::move(sel))}; + template + internal::built_in_function_t json_quote(X x) { + return {std::tuple{std::forward(x)}}; } - /** - * With-clause for a single potentially recursive CTE. - * - * @note The use of RECURSIVE does not force common table expressions to be recursive. - * - * Example: - * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; - * with_recursive(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); - */ - template = true, - internal::satisfies_not = true> - internal::with_t with_recursive(CTE cte, E expression) { - return {true, {std::move(cte)}, std::move(expression)}; + template + internal::built_in_function_t json_group_array(X x) { + return {std::tuple{std::forward(x)}}; } - /** - * With-clause for a single potentially recursive CTE. - * - * @note The use of RECURSIVE does not force common table expressions to be recursive. - * - * Example: - * constexpr orm_cte_moniker auto cte_1 = 1_ctealias; - * with_recursive(cte_1().as(select(&Object::id)), select(cte_1->*1_colalias)); - */ - template = true, - internal::satisfies = true> - internal::with_t, CTE> with_recursive(CTE cte, Compound sel) { - return {true, {std::move(cte)}, sqlite_orm::select(std::move(sel))}; + template + internal::built_in_function_t json_group_object(X x, Y y) { + return {std::tuple{std::forward(x), std::forward(y)}}; } -#endif +#endif // SQLITE_ENABLE_JSON1 + + // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace + // to facilitate ADL (Argument Dependent Lookup) + namespace internal { + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + add_t, unwrap_expression_t> operator+(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + sub_t, unwrap_expression_t> operator-(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + mul_t, unwrap_expression_t> operator*(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + div_t, unwrap_expression_t> operator/(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } - /** - * `SELECT * FROM T` expression that fetches results as tuples. - * T is a type mapped to a storage, or an alias of it. - * The `definedOrder` parameter denotes the expected order of result columns. - * The default is the implicit order as returned by SQLite, which may differ from the defined order - * if the schema of a table has been changed. - * By specifying the defined order, the columns are written out in the resulting select SQL string. - * - * In pseudo code: - * select(asterisk(false)) -> SELECT * from User - * select(asterisk(true)) -> SELECT id, name from User - * - * Example: auto rows = storage.select(asterisk()); - * // decltype(rows) is std::vector> - * Example: auto rows = storage.select(asterisk(true)); - * // decltype(rows) is std::vector> - * - * If you need to fetch results as objects instead of tuples please use `object()`. - */ - template - internal::asterisk_t asterisk(bool definedOrder = false) { - return {definedOrder}; + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + mod_t, unwrap_expression_t> operator%(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Example: - * constexpr orm_table_alias auto m = "m"_alias.for_(); - * auto reportingTo = - * storage.select(asterisk(), inner_join(on(m->*&Employee::reportsTo == &Employee::employeeId))); - */ - template - auto asterisk(bool definedOrder = false) { - return asterisk>(definedOrder); + template + internal::highlight_t highlight(X x, Y y, Z z) { + return {std::move(x), std::move(y), std::move(z)}; } -#endif +} - /** - * `SELECT * FROM T` expression that fetches results as objects of type T. - * T is a type mapped to a storage, or an alias of it. - * - * Example: auto rows = storage.select(object()); - * // decltype(rows) is std::vector, where the User objects are constructed from columns in implicitly stored order - * Example: auto rows = storage.select(object(true)); - * // decltype(rows) is std::vector, where the User objects are constructed from columns in declared make_table order - * - * If you need to fetch results as tuples instead of objects please use `asterisk()`. - */ - template - internal::object_t object(bool definedOrder = false) { - return {definedOrder}; - } +// #include "alias_traits.h" + +// #include "cte_moniker.h" +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto object(bool definedOrder = false) { - return object>(definedOrder); - } +#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 -} -#pragma once - -#include // std::string // #include "functional/cxx_universal.h" -namespace sqlite_orm { +// #include "functional/cstring_literal.h" - struct table_info { - int cid = 0; - std::string name; - std::string type; - bool notnull = false; - std::string dflt_value; - int pk = 0; +// #include "alias.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_) {} +#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 - }; + }; + } - 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 + 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{}; + } -#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_} {} +#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 - }; + } } -#pragma once +#endif -#include -#include -#include -#include +// #include "schema/column.h" + +#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 "../optional_container.h" +// #include "../functional/cxx_type_traits_polyfill.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_traits.h" + +// #include "../tuple_helper/tuple_filter.h" + +// #include "../type_traits.h" + +// #include "../member_traits/member_traits.h" + +// #include "../type_is_nullable.h" + +#include // std::false_type, std::true_type, std::enable_if +#include // std::shared_ptr, std::unique_ptr +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { - namespace internal { - enum class trigger_timing { trigger_before, trigger_after, trigger_instead_of }; - enum class trigger_type { trigger_delete, trigger_insert, trigger_update }; - /** - * 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; + /** + * 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, +#endif + polyfill::is_specialization_of, + polyfill::is_specialization_of>::value>> : std::true_type { + bool operator()(const T& t) const { + return static_cast(t); + } + }; +} + +// #include "../constraints.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; +namespace sqlite_orm { - partial_trigger_t(T trigger_base, S... statements) : - base{std::move(trigger_base)}, statements{std::make_tuple(std::forward(statements)...)} {} + namespace internal { - partial_trigger_t& end() { - return *this; - } - }; + struct column_identifier { - struct base_trigger { /** - * Name of the trigger + * Column name. */ std::string name; }; - /** - * 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 + 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 trigger_t : base_trigger { - using object_type = void; - using elements_type = typename partial_trigger_t::statements_type; + 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; /** - * Base of the trigger (contains its type, timing and associated table) + * Member pointer used to read a field value. + * If it is a object member pointer it is also used to write a field value. */ - T base; + const member_pointer_t member_pointer; /** - * Statements of the triggers (to be executed when the trigger fires) + * Setter member function to write a field value */ - 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 - }; - - /** - * 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; + SQLITE_ORM_NOUNIQUEADDRESS + const setter_type setter; /** - * 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 + * Simplified interface for `NOT NULL` constraint */ - optional_container container_when; - - trigger_base_t(trigger_type_base type_base_) : type_base(std::move(type_base_)) {} - - trigger_base_t& for_each_row() { - this->do_for_each_row = true; - return *this; - } - - template - trigger_base_t when(WW expression) { - trigger_base_t res(this->type_base); - res.container_when.field = std::move(expression); - return res; - } - - template - partial_trigger_t, S...> begin(S... statements) { - return {*this, std::forward(statements)...}; + constexpr bool is_not_null() const { + return !type_is_nullable::value; } }; - /** - * Contains the trigger type and timing + /* + * Encapsulates a tuple of column constraints. + * + * Op... is a constraints pack, e.g. primary_key_t, unique_t etc */ - struct trigger_type_base_t { + template + struct column_constraints { + using constraints_type = std::tuple; + + SQLITE_ORM_NOUNIQUEADDRESS + constraints_type constraints; + /** - * Value indicating if the trigger is run BEFORE, AFTER or INSTEAD OF - * the statement that fired it. + * Checks whether contraints contain specified type. */ - trigger_timing timing; + template class Trait> + constexpr static bool is() { + return tuple_has::value; + } + /** - * The type of the statement that would cause the trigger to fire. - * Can be DELETE, INSERT, or UPDATE. + * Simplified interface for `DEFAULT` constraint + * @return string representation of default value if it exists otherwise nullptr */ - trigger_type type; - - trigger_type_base_t(trigger_timing timing, trigger_type type) : timing(timing), type(type) {} - - template - trigger_base_t on() { - return {*this}; - } + std::unique_ptr default_value() const; }; /** - * Special case for UPDATE OF (columns) - * Contains the trigger type and timing + * Column definition. + * + * It is a composition of orthogonal information stored in different base classes. */ - 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 column_t : column_identifier, column_field, column_constraints { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + column_t(std::string name, G memberPointer, S setter, std::tuple op) : + column_identifier{std::move(name)}, column_field{memberPointer, setter}, + column_constraints{std::move(op)} {} +#endif }; - struct trigger_timing_t { - trigger_timing timing; - - trigger_type_base_t delete_() { - return {timing, trigger_type::trigger_delete}; - } - - trigger_type_base_t insert() { - return {timing, trigger_type::trigger_insert}; - } - - trigger_type_base_t update() { - return {timing, trigger_type::trigger_update}; - } - - template - trigger_update_type_t update_of(Cs... columns) { - return {timing, trigger_type::trigger_update, std::forward(columns)...}; - } + template + struct column_field_expression { + 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 column_field_expression, void> { + using type = typename column_t::member_pointer_t; }; - template - struct new_t { - using expression_type = T; + template + using column_field_expression_t = typename column_field_expression::type; - expression_type expression; - }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of::value; template - struct old_t { - using expression_type = T; + using is_column = polyfill::bool_constant>; - expression_type expression; - }; - } // NAMESPACE internal + template + using col_index_sequence_with_field_type = + filter_tuple_sequence_t::template fn, + field_type_t, + filter_tuple_sequence_t>; - /** - * NEW.expression function used within TRIGGER expressions - */ - template - internal::new_t new_(T expression) { - return {std::move(expression)}; + 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>; } /** - * OLD.expression function used within TRIGGER expressions + * Factory function for a column definition from a member object pointer of the object to be mapped. */ - template - internal::old_t old(T 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"); - /** - * RAISE(IGNORE) expression used within TRIGGER expressions - */ - inline internal::raise_t raise_ignore() { - return {internal::raise_t::type_t::ignore, {}}; + // 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)...}}); } /** - * RAISE(ROLLBACK, %message%) expression used within TRIGGER expressions + * Factory function for a column definition from "setter" and "getter" member function pointers of the object to be mapped. */ - inline internal::raise_t raise_rollback(std::string message) { - return {internal::raise_t::type_t::rollback, std::move(message)}; - } + 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"); - /** - * 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)}; + // 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)...}}); } /** - * RAISE(FAIL, %message%) expression used within TRIGGER expressions + * Factory function for a column definition from "getter" and "setter" member function pointers of the object to be mapped. */ - inline internal::raise_t raise_fail(std::string message) { - return {internal::raise_t::type_t::fail, std::move(message)}; - } + 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"); - template - internal::trigger_t make_trigger(std::string name, const internal::partial_trigger_t& part) { + // 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), 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}; + return {std::move(name), getter, setter, std::tuple{std::move(constraints)...}}); } } -#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" +namespace sqlite_orm { -// #include "functional/cxx_functional_polyfill.h" + namespace internal { +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct as_optional_t { + using value_type = T; -// #include "is_std_ptr.h" + value_type value; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED -// #include "tuple_helper/tuple_filter.h" + struct distinct_string { + operator std::string() const { + return "DISTINCT"; + } + }; -// #include "type_traits.h" + /** + * DISCTINCT generic container. + */ + template + struct distinct_t : distinct_string { + using value_type = T; -// #include "error_code.h" + value_type value; -// #include "arithmetic_tag.h" + distinct_t(value_type value_) : value(std::move(value_)) {} + }; -#include // std::is_integral + struct all_string { + operator std::string() const { + return "ALL"; + } + }; -// #include "functional/mpl/conditional.h" + /** + * ALL generic container. + */ + template + struct all_t : all_string { + T value; -namespace sqlite_orm { + all_t(T value_) : value(std::move(value_)) {} + }; - /** - * Helper classes used by statement_binder and row_extractor. - */ - struct int_or_smaller_tag {}; - struct bigint_tag {}; - struct real_tag {}; + template + struct columns_t { + using columns_type = std::tuple; - template - using arithmetic_tag_t = - mpl::conditional_t::value, - // Integer class - mpl::conditional_t, - // Floating-point class - real_tag>; -} + columns_type columns; + bool distinct = false; -// #include "xdestroy_handling.h" + static constexpr int count = std::tuple_size::value; -#include // std::integral_constant -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED -#include +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + columns_t(columns_type columns) : columns{std::move(columns)} {} #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 + template + SQLITE_ORM_INLINE_VAR constexpr bool is_columns_v = polyfill::is_specialization_of::value; -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - /** - * Yield a deleter's function pointer. - */ - template - struct yield_fp_of { - using type = decltype(+std::declval()); - }; -#else + template + using is_columns = polyfill::bool_constant>; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_stateless_deleter_v = - std::is_empty::value && std::is_default_constructible::value; + /* + * Captures the type of an aggregate/structure/object and column expressions, such that + * `T` can be constructed in-place as part of a result row. + * `T` must be constructible using direct-list-initialization. + */ + template + struct struct_t { + using columns_type = std::tuple; - template - struct is_integral_fp_c : std::false_type {}; - template - struct is_integral_fp_c< - D, - polyfill::void_t>::value>>> - : std::true_type {}; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_integral_fp_c_v = is_integral_fp_c::value; + columns_type columns; + bool distinct = false; - template - struct can_yield_fp : std::false_type {}; - template - struct can_yield_fp< - D, - polyfill::void_t< - decltype(+std::declval()), - std::enable_if_t())>>::value>>> - : std::true_type {}; - template - SQLITE_ORM_INLINE_VAR constexpr bool can_yield_fp_v = can_yield_fp::value; + static constexpr int count = std::tuple_size::value; - template> - struct yield_fp_of { - using type = void; - }; - template - struct yield_fp_of { - using type = decltype(+std::declval()); - }; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + struct_t(columns_type columns) : columns{std::move(columns)} {} #endif - template - using yielded_fn_t = typename yield_fp_of::type; + }; -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED - template - concept is_unusable_for_xdestroy = - (!stateless_deleter && (yields_fp && !std::convertible_to, xdestroy_fn_t>)); + template + SQLITE_ORM_INLINE_VAR constexpr bool is_struct_v = polyfill::is_specialization_of::value; + + template + using is_struct = polyfill::bool_constant>; /** - * This concept tests whether a deleter yields a function pointer, which is convertible to an xdestroy function pointer. - * Note: We are using 'is convertible' rather than 'is same' because of any exception specification. + * Subselect object type. */ - template - concept yields_xdestroy = yields_fp && std::convertible_to, xdestroy_fn_t>; + template + struct select_t { + using return_type = T; + using conditions_type = std::tuple; - template - concept needs_xdestroy_proxy = - (stateless_deleter && (!yields_fp || !std::convertible_to, xdestroy_fn_t>)); + return_type col; + conditions_type conditions; + bool highest_level = false; - /** - * xDestroy function that constructs and invokes the stateless deleter. - * - * Requires that the deleter can be called with the q-qualified pointer argument; - * it doesn't check so explicitly, but a compiler error will occur. - */ - template - requires(!integral_fp_c) - void xdestroy_proxy(void* p) noexcept { - // C-casting `void* -> P*` like statement_binder> - auto o = (P*)p; - // ignoring return code - (void)D{}(o); - } +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + select_t(return_type col, conditions_type conditions) : + col{std::move(col)}, conditions{std::move(conditions)} {} +#endif + }; + + template + SQLITE_ORM_INLINE_VAR constexpr bool is_select_v = polyfill::is_specialization_of::value; + + template + using is_select = polyfill::bool_constant>; /** - * xDestroy function that invokes the integral function pointer constant. - * - * Performs a const-cast of the argument pointer in order to allow for C API functions - * that take a non-const parameter, but user code passes a pointer to a const object. + * Base for UNION, UNION ALL, EXCEPT and INTERSECT */ - template - void xdestroy_proxy(void* p) noexcept { - // C-casting `void* -> P*` like statement_binder>, - auto o = (std::remove_cv_t

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

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

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

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

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

{}; - 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))...)}); - } + /* + * Pass through `structure` + */ + template + struct column_result_proxy> : P {}; - 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))...)}); + template + using column_result_proxy_t = typename column_result_proxy::type; } } -#pragma once -#include // std::string +// #include "alias.h" + +// #include "cte_types.h" + +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#include +#include +#endif + +// #include "functional/cxx_core_features.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tuple_helper/tuple_fy.h" +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) namespace sqlite_orm { namespace internal { - struct rowid_t { - operator std::string() const { - return "rowid"; - } - }; - - struct oid_t { - operator std::string() const { - return "oid"; - } - }; + /** + * 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; - struct _rowid_t { - operator std::string() const { - return "_rowid_"; - } + F field; }; - template - struct table_rowid_t : public rowid_t { - using type = T; - }; + /** + * 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; - template - struct table_oid_t : public oid_t { - using type = T; - }; - template - struct table__rowid_t : public _rowid_t { - using type = T; + // this type name is used to detect the mapping from moniker to object + using cte_moniker_type = Moniker; + using fields_type = std::tuple; + // this type captures the expressions forming the columns in a subselect; + // it is currently unused, however proves to be useful in compilation errors, + // as it simplifies recognizing errors in column expressions + using expressions_tuple = tuplify_t; + // this type captures column reference expressions specified at CTE construction; + // those are: member pointers, alias holders + using explicit_colrefs_tuple = ExplicitColRefs; + // this type captures column reference expressions from the subselect; + // those are: member pointers, alias holders + using subselect_colrefs_tuple = SubselectColRefs; + // this type captures column reference expressions merged from SubselectColRefs and ExplicitColRefs + using final_colrefs_tuple = FinalColRefs; }; - - } - - inline internal::rowid_t rowid() { - return {}; } +} +#endif - inline internal::oid_t oid() { - return {}; - } +// #include "storage_traits.h" - inline internal::_rowid_t _rowid_() { - return {}; - } +#include // std::tuple - template - internal::table_rowid_t rowid() { - return {}; - } +// #include "functional/cxx_type_traits_polyfill.h" - template - internal::table_oid_t oid() { - return {}; - } +// #include "tuple_helper/tuple_filter.h" - template - internal::table__rowid_t _rowid_() { - return {}; - } -} -#pragma once +// #include "tuple_helper/tuple_transformer.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 "type_traits.h" -// #include "../functional/cxx_universal.h" -// ::size_t -// #include "../functional/cxx_type_traits_polyfill.h" +// #include "storage_lookup.h" -// #include "../functional/cxx_functional_polyfill.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 "../functional/static_magic.h" +// #include "functional/cxx_universal.h" +// ::size_t +// #include "functional/cxx_type_traits_polyfill.h" -// #include "../functional/mpl.h" +// #include "type_traits.h" -// #include "../functional/index_sequence_util.h" +namespace sqlite_orm { + namespace internal { -// #include "../tuple_helper/tuple_filter.h" + template + struct storage_t; -// #include "../tuple_helper/tuple_traits.h" + template + using db_objects_tuple = std::tuple; -// #include "../tuple_helper/tuple_iteration.h" + struct basic_table; + struct index_base; + struct base_trigger; -// #include "../tuple_helper/tuple_transformer.h" + template + struct is_storage : std::false_type {}; -// #include "../member_traits/member_traits.h" + template + struct is_storage> : std::true_type {}; + template + struct is_storage> : std::true_type {}; -// #include "../typed_comparator.h" + template + struct is_db_objects : std::false_type {}; -namespace sqlite_orm { + 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 internal { + /** + * `std::true_type` if given object is mapped, `std::false_type` otherwise. + * + * Note: unlike table_t<>, index_t<>::object_type and trigger_t<>::object_type is always void. + */ + template + struct object_type_matches : polyfill::conjunction>>, + std::is_same>> {}; - template - bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { - return false; - } - template - bool compare_any(const O& lhs, const O& rhs) { - return lhs == rhs; - } + /** + * `std::true_type` if given lookup type (object or moniker) is mapped, `std::false_type` otherwise. + */ + template + using lookup_type_matches = object_type_matches; } -} - -// #include "../type_traits.h" -// #include "../alias_traits.h" - -// #include "../constraints.h" - -// #include "../table_info.h" + // pick/lookup metafunctions + namespace internal { -// #include "column.h" + /** + * Indirect enabler for DBO, accepting an index to disambiguate non-unique DBOs + */ + template + struct enable_found_table : std::enable_if::value, DBO> {}; -namespace sqlite_orm { + /** + * 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; - namespace internal { + template + struct storage_pick_table, db_objects_tuple> + : enable_found_table... {}; - 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>; + /** + * 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; -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) /** - * A subselect mapper's CTE moniker, void otherwise. + * 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 - using moniker_of_or_void_t = polyfill::detected_or_t; + template + struct storage_find_table : polyfill::detected {}; - /** - * 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. + /** + * 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 mapped_object_type_for_t = polyfill::detected_or_t; + 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 - struct basic_table { - - /** - * Table name. - */ - std::string name; - }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_mapped_v = is_mapped::value; + } +} +// runtime lookup functions +namespace sqlite_orm { + namespace internal { /** - * Table definition. + * 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 - 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 = true> + auto& pick_table(DBOs& dbObjects) { + using table_type = storage_pick_table_t; + return std::get(dbObjects); + } - static constexpr bool is_without_rowid_v = WithoutRowId; + /** + * Return passed in DBOs. + */ + template = true> + decltype(auto) db_objects_for_expression(DBOs& dbObjects, const E&) { + return dbObjects; + } - using is_without_rowid = polyfill::bool_constant; + template = true> + decltype(auto) lookup_table_name(const DBOs& dbObjects); + } +} - elements_type elements; +// #include "schema/column.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 { + namespace internal { - table_t without_rowid() const { - return {this->name, this->elements}; - } + namespace storage_traits { - /* - * Returns the number of elements of the specified type. + /** + * DBO - db object (table) */ - template class Trait> - static constexpr int count_of() { - using sequence_of = filter_tuple_sequence_t; - return int(sequence_of::size()); - } + template + struct storage_mapped_columns_impl + : tuple_transformer, is_column>, field_type_t> {}; - /* - * 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 storage_mapped_columns_impl { + using type = std::tuple<>; + }; - /* - * Returns the number of columns having the specified constraint trait. + /** + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type */ - 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 storage_mapped_columns : storage_mapped_columns_impl> {}; /** - * 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. + * DBO - db object (table) */ - template = true> - decltype(auto) object_field_value(const object_type& object, M memberPointer) const { - return polyfill::invoke(memberPointer, object); - } - - template = true> - const member_field_type_t* object_field_value(const object_type& object, M memberPointer) const { - using field_type = member_field_type_t; - const field_type* res = nullptr; - iterate_tuple(this->elements, - col_index_sequence_with_field_type{}, - call_as_template_base([&res, &memberPointer, &object](const auto& column) { - if(compare_any(column.setter, memberPointer)) { - res = &polyfill::invoke(column.member_pointer, object); - } - })); - return res; - } + template + struct storage_mapped_column_expressions_impl + : tuple_transformer, is_column>, column_field_expression_t> {}; - 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 storage_mapped_column_expressions_impl { + using type = std::tuple<>; + }; /** - * Call passed lambda with all defined primary keys. + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type */ - template - void for_each_primary_key(L&& lambda) const { - using pk_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->elements, pk_index_sequence{}, lambda); - } - - std::vector composite_key_columns_names() const { - std::vector res; - this->for_each_primary_key([this, &res](auto& primaryKey) { - res = this->composite_key_columns_names(primaryKey); - }); - return res; - } - - std::vector primary_key_column_names() const { - using pkcol_index_sequence = col_index_sequence_with; - - if(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 storage_mapped_column_expressions + : storage_mapped_column_expressions_impl> {}; + } + } +} - 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 "function.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 // 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 - /** - * 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 "functional/cxx_universal.h" +// ::size_t, ::nullptr_t +// #include "functional/cxx_type_traits_polyfill.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 "functional/cstring_literal.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 "functional/function_traits.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 "cxx_type_traits_polyfill.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 "mpl.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); - } +namespace sqlite_orm { + namespace internal { + /* + * Define nested typenames: + * - return_type + * - arguments_tuple + * - signature_type + */ + template + struct function_traits; - std::vector get_table_info() const; - }; + /* + * A function's return type + */ + template + using function_return_type_t = typename function_traits::return_type; - template - struct is_table : std::false_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; - template - struct is_table> : std::true_type {}; + /* + * A function's signature + */ + template + using function_signature_type_t = typename function_traits::signature_type; - template - struct virtual_table_t : basic_table { - using module_details_type = M; - using object_type = typename module_details_type::object_type; - using elements_type = typename module_details_type::columns_type; + template + struct function_traits { + using return_type = R; - static constexpr bool is_without_rowid_v = false; - using is_without_rowid = polyfill::bool_constant; + template class Tuple, template class ProjectOp> + using arguments_tuple = Tuple...>; - module_details_type module_details; + using signature_type = R(Args...); + }; -#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 + // non-exhaustive partial specializations of `function_traits` - /** - * 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 function_traits : function_traits { + using signature_type = R(Args...) const; + }; - /** - * 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); - } +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template + struct function_traits : function_traits { + using signature_type = R(Args...) noexcept; + }; - /** - * 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 function_traits : function_traits { + using signature_type = R(Args...) const noexcept; }; +#endif - template - struct is_virtual_table : std::false_type {}; + /* + * Pick signature of function pointer + */ + template + struct function_traits : function_traits {}; - template - struct is_virtual_table> : std::true_type {}; + /* + * Pick signature of function reference + */ + template + struct function_traits : function_traits {}; -#if SQLITE_VERSION_NUMBER >= 3009000 - template - struct using_fts5_t { - using object_type = T; - using columns_type = std::tuple; + /* + * Pick signature of pointer-to-member function + */ + template + struct function_traits : function_traits {}; + } +} - columns_type columns; +// #include "type_traits.h" - using_fts5_t(columns_type columns) : columns(std::move(columns)) {} +// #include "tags.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->columns, col_index_sequence_excluding{}, lambda); - } +namespace sqlite_orm { - /** - * 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); - } + struct arg_values; - /** - * Call passed lambda with all defined columns. - * @param lambda Lambda called for each column. Function signature: `void(auto& column)` - */ - template - void for_each_column(L&& lambda) const { - using col_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->columns, col_index_sequence{}, lambda); - } - }; -#endif + // note (internal): forward declare even if `SQLITE_VERSION_NUMBER < 3020000` in order to simplify coding below + template + struct pointer_arg; + // note (internal): forward declare even if `SQLITE_VERSION_NUMBER < 3020000` in order to simplify coding below + template + class pointer_binding; - template - 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; - } + namespace internal { + template + using scalar_call_function_t = decltype(&F::operator()); - template - bool exists_in_composite_primary_key(const virtual_table_t& /*virtualTable*/, - const column_field& /*column*/) { - return false; - } - } + template + using aggregate_step_function_t = decltype(&F::step); -#if SQLITE_VERSION_NUMBER >= 3009000 - template>::object_type> - internal::using_fts5_t using_fts5(Cs... columns) { - static_assert(polyfill::conjunction_v...>, - "Incorrect table elements or constraints"); + template + using aggregate_fin_function_t = decltype(&F::fin); - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); - } + 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 - internal::using_fts5_t using_fts5(Cs... columns) { - static_assert(polyfill::conjunction_v...>, - "Incorrect table elements or constraints"); + template + struct is_scalar_udf : polyfill::bool_constant> {}; - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); - } -#endif + 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; - /** - * 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 is_aggregate_udf : polyfill::bool_constant> {}; - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), std::make_tuple(std::forward(args)...)}); + template + struct function; } - /** - * Factory function for a table definition. +#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. * - * The mapped object type is explicitly specified. + * 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 - internal::table_t make_table(std::string name, Cs... args) { - static_assert(polyfill::conjunction_v...>, - "Incorrect table elements or constraints"); + template + concept orm_classic_function_object = + ((!requires { typename F::is_transparent; }) && (requires { &F::operator(); }) && + /*rule out sqlite_orm scalar function*/ + (!requires { F::name(); })); - SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( - return {std::move(name), std::make_tuple(std::forward(args)...)}); - } + /** @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; + }; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - /** - * Factory function for a table definition. + /** @short Specifies that a type is a user-defined aggregate function. * - * The mapped object type is explicitly specified. + * `UDF` must meet the following requirements: + * - `UDF::name()` static function + * - `UDF::step()` member function + * - `UDF::fin()` member function */ - template - auto make_table(std::string name, Cs... args) { - return make_table>(std::move(name), std::forward(args)...); - } -#endif + 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>; + }; - 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 + /** @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 // std::string + /** @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 "functional/cxx_universal.h" -// ::size_t -// #include "functional/static_magic.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 "functional/index_sequence_util.h" + namespace internal { + template + struct callable_arguments_impl; -// #include "tuple_helper/tuple_traits.h" + template + struct callable_arguments_impl> { + using args_tuple = function_arguments, std::tuple, std::decay_t>; + using return_type = function_return_type_t>; + }; -// #include "tuple_helper/tuple_filter.h" + template + struct callable_arguments_impl> { + using args_tuple = function_arguments, std::tuple, std::decay_t>; + using return_type = function_return_type_t>; + }; -// #include "tuple_helper/tuple_iteration.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 -// #include "type_traits.h" + template + struct callable_arguments : callable_arguments_impl {}; -// #include "select_constraints.h" +#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; -// #include "cte_types.h" + using std::string::basic_string; -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -#include -#include + const std::string& operator()() const { + return *this; + } + }; #endif -// #include "functional/cxx_core_features.h" +#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; -// #include "functional/cxx_type_traits_polyfill.h" + template>::value, bool> = true> + decltype(auto) operator()() const { + return UDF::name(); + } -// #include "tuple_helper/tuple_fy.h" + template::value, bool> = true> + std::string operator()() const { + return std::string{UDF::name()}; + } + }; -#include + /* + * Represents a call of a user-defined function. + */ + template + struct function_call { + using udf_type = UDF; + using args_tuple = std::tuple; -namespace sqlite_orm { + udf_holder name; + args_tuple callArgs; + }; - namespace internal { + template + SQLITE_ORM_INLINE_VAR constexpr bool + is_operator_argument_v::value>> = true; - template - struct tuplify { - using type = std::tuple; + template + struct unpacked_arg { + using type = T; }; - template - struct tuplify> { - using type = std::tuple; + template + struct unpacked_arg> { + using type = typename callable_arguments::return_type; }; + template + using unpacked_arg_t = typename unpacked_arg::type; - template - using tuplify_t = typename tuplify::type; - } -} - -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) -namespace sqlite_orm { + template + SQLITE_ORM_CONSTEVAL bool expected_pointer_value() { + static_assert(polyfill::always_false_v, "Expected a pointer value for I-th argument"); + return false; + } - namespace internal { + template + constexpr bool is_same_pvt_v = expected_pointer_value(); - /** - * 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; + // 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; - F field; - }; + 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; + } - /** - * 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; +#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; + } - // 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; - }; - } -} + template + constexpr bool + is_same_pvt_v> = + assert_same_pointer_tag(); #endif -// #include "storage_lookup.h" + // not a pointer value, currently leave it unchecked + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::false_type) { + return true; + } -#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 + // check the type of pointer values + template + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::true_type) { + return is_same_pvt_v; + } -// #include "functional/cxx_universal.h" -// ::size_t -// #include "functional/cxx_type_traits_polyfill.h" + 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>; -// #include "type_traits.h" +#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) > {}); -namespace sqlite_orm { - namespace internal { + return validate_pointer_value_types(polyfill::index_constant{}) && valid; +#else + return validate_pointer_value_types(polyfill::index_constant{}) && + validate_pointer_value_type, + unpacked_arg_t>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v) || + (polyfill::is_specialization_of_v) > {}); +#endif + } - template - struct storage_t; + /* + * 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 - using db_objects_tuple = std::tuple; + /* + * 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; - struct basic_table; - struct index_base; - struct base_trigger; + /* + * Generates the SQL function call. + */ + template + function_call operator()(CallArgs... callArgs) const { + check_function_call(); + return {this->udf_holder(), {std::forward(callArgs)...}}; + } - template - struct is_storage : std::false_type {}; + constexpr auto udf_holder() const { + return internal::udf_holder{}; + } - template - struct is_storage> : std::true_type {}; - template - struct is_storage> : std::true_type {}; + // returns a character range + constexpr auto name() const { + return this->udf_holder()(); + } + }; - template - struct is_db_objects : std::false_type {}; +#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; - 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 {}; + /* + * Generates the SQL function call. + */ + template + function_call operator()(CallArgs... callArgs) const { + check_function_call(); + return {this->udf_holder(), {std::forward(callArgs)...}}; + } - /** - * `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>> {}; + /* + * 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); + } + } - /** - * `std::true_type` if given lookup type (object or moniker) is mapped, `std::false_type` otherwise. - */ - template - using lookup_type_matches = object_type_matches; - } + constexpr auto udf_holder() const { + return internal::udf_holder{this->name()}; + } - // pick/lookup metafunctions - namespace internal { + constexpr auto name() const { + return this->nme; + } - /** - * Indirect enabler for DBO, accepting an index to disambiguate non-unique DBOs - */ - template - struct enable_found_table : std::enable_if::value, DBO> {}; + template + consteval quoted_scalar_function(const char (&name)[N], Args&&... constructorArgs) : + udf(std::forward(constructorArgs)...) { + std::copy_n(name, N, this->nme); + } - /** - * 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; + F udf; + char nme[N]; + }; - template - struct storage_pick_table, db_objects_tuple> - : enable_found_table... {}; + template + struct quoted_function_builder : cstring_literal { + using cstring_literal::cstring_literal; - /** - * 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; + /* + * 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)}; + } - /** - * 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 {}; + /* + * From a function object instance, picking the overloaded call operator. + */ + template + requires((stateless || std::copy_constructible)) + [[nodiscard]] consteval auto quote(F callable) const { + // detect whether overloaded call operator can be picked using `Sig` + using call_operator_type = decltype(static_cast(&F::operator())); + return quoted_scalar_function{this->cstr, std::move(callable)}; + } - /** - * Find 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; + /* + * 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)...}; + } -#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 {}; + /* + * From a function object type, picking the overloaded call operator. + */ + template + requires((stateless || std::copy_constructible)) + [[nodiscard]] consteval auto quote(Args&&... constructorArgs) const { + // detect whether overloaded call operator can be picked using `Sig` + using call_operator_type = decltype(static_cast(&F::operator())); + return quoted_scalar_function{this->cstr, std::forward(constructorArgs)...}; + } + }; #endif - - template - SQLITE_ORM_INLINE_VAR constexpr bool is_mapped_v = is_mapped::value; } -} -// 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); - } + /** @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{}; - /** - * Return passed in DBOs. +#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 = true> - decltype(auto) db_objects_for_expression(DBOs& dbObjects, const E&) { - return dbObjects; + template + [[nodiscard]] consteval auto operator"" _scalar() { + return builder; } - - template = true> - decltype(auto) lookup_table_name(const DBOs& dbObjects); } +#endif } -// interface functions +// #include "ast/special_keywords.h" + namespace sqlite_orm { namespace internal { + struct current_time_t {}; + struct current_date_t {}; + struct current_timestamp_t {}; + } - template - using tables_index_sequence = filter_tuple_sequence_t; + inline internal::current_time_t current_time() { + return {}; + } - 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; - } + inline internal::current_date_t current_date() { + return {}; + } - 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); - } + inline internal::current_timestamp_t current_timestamp() { + return {}; + } +} - /** - * 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); - } +namespace sqlite_orm { - /** - * 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; - } + namespace internal { -#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) /** - * Materialize column pointer: - * 3. by moniker and alias_holder<>. + * Obtains the result type of expressions that form the columns of a select statement. * - * internal note: there's an overload for `find_column_name()` that avoids going through `table_t<>::find_column_name()` + * 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 = true> - constexpr decltype(auto) materialize_column_pointer(const DBOs&, - const column_pointer>&) { - using table_type = storage_pick_table_t; - using cte_mapper_type = cte_mapper_type_t; + template + struct column_result_t { +#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 + }; - // 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 column_result_of_t = typename column_result_t::type; - return &aliased_field< - ColAlias, - std::tuple_element_t>::field; - } -#endif + template + using column_result_for_tuple_t = + transform_tuple_t::template fn>; - /** - * Find column name by: - * 1. by explicit object type and member pointer. - * 2. by moniker and member pointer. - */ - template = true> - const std::string* find_column_name(const DBOs& dbObjects, const column_pointer& cp) { - auto field = materialize_column_pointer(dbObjects, cp); - return pick_table(dbObjects).find_column_name(field); - } +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct column_result_t, void> { + using type = std::optional>; + }; + + template + struct column_result_t, void> { + using type = std::optional; + }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED -#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 column_result_t, void> { + using type = bool; + }; - // 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 + struct column_result_t, void> { + using type = bool; + }; - // 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 - } -} -#pragma once + template + struct column_result_t { + using type = std::string; + }; -#include // std::string + template + struct column_result_t { + using type = std::string; + }; -// #include "constraints.h" + template + struct column_result_t { + using type = std::string; + }; -// #include "serializer_context.h" + template + struct column_result_t> : member_field_type {}; -// #include "storage_lookup.h" + template + struct column_result_t, void> { + using type = R; + }; -namespace sqlite_orm { + template + struct column_result_t, void> { + using type = R; + }; - namespace internal { + template + struct column_result_t, void> { + using type = typename callable_arguments::return_type; + }; - template - auto serialize(const T& t, const C& context); + template + struct column_result_t, S, X, Rest...>, void> { + using type = std::unique_ptr>; + }; - /** - * 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); - } + template + struct column_result_t, S, X>, void> { + using type = std::unique_ptr>; + }; - } + template + struct column_result_t, void> { + using type = int; + }; -} -#pragma once + template + struct column_result_t { + using type = nullptr_t; + }; -#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" + template + struct column_result_t { + using type = int; + }; -// #include "functional/cxx_universal.h" + template + struct column_result_t, void> : column_result_t {}; -// #include "functional/cxx_functional_polyfill.h" + template + struct column_result_t, void> : column_result_t {}; -// #include "functional/static_magic.h" + template + struct column_result_t, void> { + using type = std::string; + }; -// #include "functional/mpl.h" + template + struct column_result_t, void> { + using type = double; + }; -// #include "tuple_helper/tuple_traits.h" + template + struct column_result_t, void> { + using type = double; + }; -// #include "tuple_helper/tuple_filter.h" + template + struct column_result_t, void> { + using type = double; + }; -// #include "tuple_helper/tuple_transformer.h" + template + struct column_result_t, void> { + using type = double; + }; -// #include "tuple_helper/tuple_iteration.h" + template + struct column_result_t, void> { + using type = double; + }; -// #include "type_traits.h" + template + struct column_result_t, void> { + using type = int; + }; -// #include "alias.h" + template + struct column_result_t, void> { + using type = int; + }; -// #include "error_code.h" + template + struct column_result_t, void> { + using type = int; + }; -// #include "type_printer.h" + template + struct column_result_t, void> { + using type = int; + }; -// #include "constraints.h" + template + struct column_result_t, void> { + using type = int; + }; -// #include "field_printer.h" + template + struct column_result_t { + using type = int64; + }; -#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 column_result_t { + using type = int64; + }; -// #include "functional/cxx_universal.h" + template + struct column_result_t { + using type = int64; + }; -// #include "functional/cxx_type_traits_polyfill.h" + template + struct column_result_t, void> { + using type = int64; + }; -// #include "is_std_ptr.h" + template + struct column_result_t, void> { + using type = int64; + }; -// #include "type_traits.h" + template + struct column_result_t, void> { + using type = int64; + }; -namespace sqlite_orm { + template + struct column_result_t, void> : column_result_t {}; - /** - * 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 column_result_t, void> : column_result_t {}; - 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; +#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; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v = false; - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_printable_v{})>>> = true; + // 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 - template - struct is_printable : polyfill::bool_constant> {}; - } + template + struct column_result_t, void> + : conc_tuple>>...> {}; - template - struct field_printer> { - std::string operator()(const T& t) const { - std::stringstream ss; - ss << t; - return ss.str(); - } - }; + template + struct column_result_t, void> { + using type = structure>>...>>; + }; - /** - * 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(); - } - }; + template + struct column_result_t> : column_result_t {}; - /** - * 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(); - } - }; + template + struct column_result_t> { + using type = + polyfill::detected_t>; + static_assert(!std::is_same::value, + "Compound select statements must return a common type"); + }; - /** - * 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(); - } - }; + template + struct column_result_t> { + using type = typename T::result_type; + }; - template - struct field_printer> { - std::string operator()(std::string string) const { - return string; - } - }; + template + struct column_result_t, void> { + using type = std::string; + }; - template<> - struct field_printer, void> { - std::string operator()(const std::vector& t) const { - std::stringstream ss; - ss << std::hex; - for(auto c: t) { - ss << c; - } - return ss.str(); - } - }; -#ifndef SQLITE_ORM_OMITS_CODECVT - /** - * Specialization for std::wstring (UTF-16 assumed). - */ - template - struct field_printer> { - std::string operator()(const std::wstring& wideString) const { - std::wstring_convert> converter; - return converter.to_bytes(wideString); - } - }; -#endif // SQLITE_ORM_OMITS_CODECVT - template<> - struct field_printer { - std::string operator()(const nullptr_t&) const { - return "NULL"; - } - }; -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template<> - struct field_printer { - std::string operator()(const std::nullopt_t&) const { - return "NULL"; - } - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct field_printer, - internal::is_printable>>::value>> { - using unqualified_type = std::remove_cv_t; + /** + * Result for the most simple queries like `SELECT 1` + */ + template + struct column_result_t> { + using type = T; + }; - std::string operator()(const T& t) const { - if(t) { - return field_printer()(*t); - } else { - return field_printer{}(nullptr); - } - } - }; + /** + * Result for the most simple queries like `SELECT 'ototo'` + */ + template + struct column_result_t { + using type = std::string; + }; -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct field_printer< - T, - std::enable_if_t, - internal::is_printable>>>> { - using unqualified_type = std::remove_cv_t; + template + struct column_result_t { + using type = std::string; + }; - 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 -} + template + struct column_result_t, void> : column_result_t> {}; -// #include "rowid.h" + template + struct column_result_t, void> + : storage_traits::storage_mapped_columns> {}; -// #include "operators.h" + template + struct column_result_t, void> { + using type = table_reference; + }; -// #include "select_constraints.h" + template + struct column_result_t, void> { + using type = T; + }; -// #include "core_functions.h" + template + struct column_result_t, void> { + using type = R; + }; -// #include "conditions.h" + template + struct column_result_t, void> { + using type = bool; + }; -// #include "statement_binder.h" + template + struct column_result_t, void> { + using type = bool; + }; -// #include "column_result.h" + template + struct column_result_t, void> { + using type = bool; + }; -#include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of -#include // std::reference_wrapper + template + struct column_result_t, void> : column_result_t {}; + } +} -// #include "functional/cxx_universal.h" -// ::nullptr_t -// #include "functional/cxx_type_traits_polyfill.h" +// #include "mapped_type_proxy.h" -// #include "functional/mpl.h" +// #include "sync_schema_result.h" -// #include "tuple_helper/tuple_traits.h" +#include -// #include "tuple_helper/tuple_fy.h" +namespace sqlite_orm { -// #include "tuple_helper/tuple_filter.h" + enum class sync_schema_result { -// #include "tuple_helper/tuple_transformer.h" + /** + * created new table, table with the same tablename did not exist + */ + new_table_created, -// #include "tuple_helper/same_or_void.h" + /** + * table schema is the same as storage, nothing to be done + */ + already_in_sync, -// #include "type_traits.h" + /** + * removed excess columns in table (than storage) without dropping a table + */ + old_columns_removed, -// #include "member_traits/member_traits.h" + /** + * lacking columns in table (than storage) added without dropping a table + */ + new_columns_added, -// #include "mapped_type_proxy.h" + /** + * both old_columns_removed and new_columns_added + */ + new_columns_added_and_old_columns_removed, -#include // std::remove_const + /** + * 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 "type_traits.h" +// #include "table_info.h" -// #include "table_reference.h" +#include // std::string -// #include "alias_traits.h" +// #include "functional/cxx_universal.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 {}; + struct table_info { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - struct mapped_type_proxy : R {}; +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) + table_info(decltype(cid) cid_, + decltype(name) name_, + decltype(type) type_, + decltype(notnull) notnull_, + decltype(dflt_value) dflt_value_, + decltype(pk) pk_) : + cid(cid_), + name(std::move(name_)), type(std::move(type_)), notnull(notnull_), dflt_value(std::move(dflt_value_)), + pk(pk_) {} #endif + }; - template - struct mapped_type_proxy> : std::remove_const> {}; + struct table_xinfo { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; + int hidden = 0; // different than 0 => generated_always_as() - TODO verify - template - using mapped_type_proxy_t = typename mapped_type_proxy::type; - } +#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 "core_functions.h" - -// #include "select_constraints.h" - -// #include "operators.h" - -// #include "rowid.h" - -// #include "column_result_proxy.h" - -// #include "alias.h" +// #include "storage_impl.h" -// #include "cte_types.h" +#include // std::string -// #include "storage_traits.h" +// #include "functional/cxx_universal.h" +// ::size_t +// #include "functional/static_magic.h" -#include // std::tuple +// #include "functional/index_sequence_util.h" -// #include "functional/cxx_type_traits_polyfill.h" +// #include "tuple_helper/tuple_traits.h" // #include "tuple_helper/tuple_filter.h" -// #include "tuple_helper/tuple_transformer.h" +// #include "tuple_helper/tuple_iteration.h" // #include "type_traits.h" -// #include "storage_lookup.h" - -// #include "schema/column.h" +// #include "select_constraints.h" -namespace sqlite_orm { - namespace internal { +// #include "cte_types.h" - namespace storage_traits { +// #include "schema/column.h" - /** - * DBO - db object (table) - */ - template - struct storage_mapped_columns_impl - : tuple_transformer, is_column>, field_type_t> {}; +// #include "schema/table.h" - template<> - struct storage_mapped_columns_impl { - using type = std::tuple<>; - }; +#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 - /** - * DBOs - db_objects_tuple type - * Lookup - mapped or unmapped data type - */ - template - struct storage_mapped_columns : storage_mapped_columns_impl> {}; +// #include "../functional/cxx_universal.h" +// ::size_t +// #include "../functional/cxx_type_traits_polyfill.h" - /** - * DBO - db object (table) - */ - template - struct storage_mapped_column_expressions_impl - : tuple_transformer, is_column>, column_field_expression_t> {}; +// #include "../functional/cxx_functional_polyfill.h" - template<> - struct storage_mapped_column_expressions_impl { - using type = std::tuple<>; - }; +// #include "../functional/static_magic.h" - /** - * DBOs - db_objects_tuple type - * Lookup - mapped or unmapped data type - */ - template - struct storage_mapped_column_expressions - : storage_mapped_column_expressions_impl> {}; - } - } -} +// #include "../functional/mpl.h" -// #include "function.h" +// #include "../functional/index_sequence_util.h" -#include // std::enable_if, std::is_member_function_pointer, std::is_function, std::remove_const, std::decay, std::is_convertible, std::is_same, std::false_type, std::true_type -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED -#include // std::copy_constructible -#endif -#include // std::tuple, std::tuple_size, std::tuple_element -#include // std::min, std::copy_n -#include // std::move, std::forward +// #include "../tuple_helper/tuple_filter.h" -// #include "functional/cxx_universal.h" -// ::size_t, ::nullptr_t -// #include "functional/cxx_type_traits_polyfill.h" +// #include "../tuple_helper/tuple_traits.h" -// #include "functional/cstring_literal.h" +// #include "../tuple_helper/tuple_iteration.h" -// #include "functional/function_traits.h" +// #include "../tuple_helper/tuple_transformer.h" -// #include "cxx_type_traits_polyfill.h" +// #include "../member_traits/member_traits.h" -// #include "mpl.h" +// #include "../typed_comparator.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` + namespace internal { - template - struct function_traits : function_traits { - using signature_type = R(Args...) const; - }; + 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_NOTHROW_ALIASES_SUPPORTED - template - struct function_traits : function_traits { - using signature_type = R(Args...) noexcept; - }; +// #include "../type_traits.h" - template - struct function_traits : function_traits { - using signature_type = R(Args...) const noexcept; - }; -#endif +// #include "../alias_traits.h" - /* - * Pick signature of function pointer - */ - template - struct function_traits : function_traits {}; +// #include "../constraints.h" - /* - * Pick signature of function reference - */ - template - struct function_traits : function_traits {}; +// #include "../table_info.h" - /* - * Pick signature of pointer-to-member function - */ - template - struct function_traits : function_traits {}; - } -} +// #include "index.h" -// #include "type_traits.h" +#include // std::tuple, std::make_tuple, std::declval, std::tuple_element_t +#include // std::string +#include // std::forward -// #include "tags.h" +// #include "../functional/cxx_universal.h" -namespace sqlite_orm { +// #include "../tuple_helper/tuple_traits.h" - struct arg_values; +// #include "../indexed_column.h" - // 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; +#include // std::string +#include // std::move - namespace internal { - template - using scalar_call_function_t = decltype(&F::operator()); +// #include "functional/cxx_universal.h" - template - using aggregate_step_function_t = decltype(&F::step); +// #include "ast/where.h" - template - using aggregate_fin_function_t = decltype(&F::fin); +namespace sqlite_orm { - template - SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v = false; - template - SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v>> = true; + namespace internal { - template - struct is_scalar_udf : polyfill::bool_constant> {}; + template + struct indexed_column_t { + using column_type = C; - 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; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + indexed_column_t(column_type _column_or_expression) : + column_or_expression(std::move(_column_or_expression)) {} +#endif - template - struct is_aggregate_udf : polyfill::bool_constant> {}; + column_type column_or_expression; + std::string _collation_name; + int _order = 0; // -1 = desc, 1 = asc, 0 = not specified - template - struct function; - } + indexed_column_t collate(std::string name) { + auto res = std::move(*this); + res._collation_name = std::move(name); + return res; + } -#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; + indexed_column_t asc() { + auto res = std::move(*this); + res._order = 1; + return res; + } - /** @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(); })); + indexed_column_t desc() { + auto res = std::move(*this); + res._order = -1; + return res; + } + }; - /** @short Specifies that a type is a user-defined scalar function. - * - * `UDF` must meet the following requirements: - * - `UDF::name()` static function - * - `UDF::operator()()` call operator - */ - template - concept orm_scalar_udf = requires { - UDF::name(); - typename internal::scalar_call_function_t; - }; + template + indexed_column_t make_indexed_column(C col) { + return {std::move(col)}; + } - /** @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>; - }; + template + where_t make_indexed_column(where_t wher) { + return std::move(wher); + } - /** @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); + template + indexed_column_t make_indexed_column(indexed_column_t col) { + return std::move(col); + } + } - /** @short Specifies that a type is a framed user-defined aggregate function. + /** + * Use this function to specify indexed column inside `make_index` function call. + * Example: make_index("index_name", indexed_column(&User::id).asc()) */ - template - concept orm_aggregate_function = (polyfill::is_specialization_of_v, internal::function> && - orm_aggregate_udf); + template + internal::indexed_column_t indexed_column(C column_or_expression) { + return {std::move(column_or_expression)}; + } +} - /** @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 "../table_type_of.h" - namespace internal { - template - struct callable_arguments_impl; +namespace sqlite_orm { - template - struct callable_arguments_impl> { - using args_tuple = function_arguments, std::tuple, std::decay_t>; - using return_type = function_return_type_t>; - }; + namespace internal { - template - struct callable_arguments_impl> { - using args_tuple = function_arguments, std::tuple, std::decay_t>; - using return_type = function_return_type_t>; - }; + struct index_base { + std::string name; + bool unique = false; -#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>; - }; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + index_base(std::string name, bool unique) : name{std::move(name)}, unique{unique} {} #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; + template + struct index_t : index_base { + using elements_type = std::tuple; + using object_type = void; + using table_mapped_type = T; - using std::string::basic_string; +#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 - const std::string& operator()() const { - return *this; - } + elements_type elements; }; -#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 + 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>::value, bool> = true> - decltype(auto) operator()() const { - return UDF::name(); - } + 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::value, bool> = true> - std::string operator()() const { - return std::string{UDF::name()}; - } - }; + 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))...)}); + } +} - /* - * Represents a call of a user-defined function. - */ - template - struct function_call { - using udf_type = UDF; - using args_tuple = std::tuple; +// #include "column.h" - udf_holder name; - args_tuple callArgs; - }; +namespace sqlite_orm { - template - SQLITE_ORM_INLINE_VAR constexpr bool - is_operator_argument_v::value>> = true; + namespace internal { 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; + 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>; - template - SQLITE_ORM_CONSTEVAL bool expected_pointer_value() { - static_assert(polyfill::always_false_v, "Expected a pointer value for I-th argument"); - return false; - } +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) + /** + * A subselect mapper's CTE moniker, void otherwise. + */ + template + using moniker_of_or_void_t = polyfill::detected_or_t; - template - constexpr bool is_same_pvt_v = expected_pointer_value(); + /** + * If O is a subselect_mapper then returns its nested type name O::cte_moniker_type, + * otherwise O itself is a regular object type to be mapped. + */ + template + using mapped_object_type_for_t = polyfill::detected_or_t; +#endif - // 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; + struct basic_table { - 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; - } + /** + * Table name. + */ + std::string name; + }; -#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(); + /** + * 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 - 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(); + using object_type = O; #endif + using elements_type = std::tuple; - // 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; - } + static constexpr bool is_without_rowid_v = WithoutRowId; - 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>; + using is_without_rowid = polyfill::bool_constant; -#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) > {}); + elements_type elements; - 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) > {}); +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + table_t(std::string name_, elements_type elements_) : + basic_table{std::move(name_)}, elements{std::move(elements_)} {} #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"); - } + table_t without_rowid() const { + return {this->name, this->elements}; + } - /* - * 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; + /* + * Returns the number of elements of the specified type. + */ + template class Trait> + static constexpr int count_of() { + using sequence_of = filter_tuple_sequence_t; + return int(sequence_of::size()); + } + + /* + * Returns the number of columns having the specified constraint trait. + */ + template class Trait> + static constexpr int count_of_columns_with() { + using filtered_index_sequence = col_index_sequence_with; + return int(filtered_index_sequence::size()); + } + + /* + * Returns the number of columns having the specified constraint trait. + */ + template class Trait> + static constexpr int count_of_columns_excluding() { + using excluded_col_index_sequence = col_index_sequence_excluding; + return int(excluded_col_index_sequence::size()); + } + + /** + * Function used to get field value from object by mapped member pointer/setter/getter. + * + * For a setter the corresponding getter has to be searched, + * so the method returns a pointer to the field as returned by the found getter. + * Otherwise the method invokes the member pointer and returns its result. + */ + template = true> + decltype(auto) object_field_value(const object_type& object, M memberPointer) const { + return polyfill::invoke(memberPointer, object); + } + + template = true> + const member_field_type_t* object_field_value(const object_type& object, M memberPointer) const { + using field_type = member_field_type_t; + const field_type* res = nullptr; + iterate_tuple(this->elements, + col_index_sequence_with_field_type{}, + call_as_template_base([&res, &memberPointer, &object](const auto& column) { + if(compare_any(column.setter, memberPointer)) { + res = &polyfill::invoke(column.member_pointer, object); + } + })); + return res; + } - /* - * Generates the SQL function call. + 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; + } + + /** + * Call passed lambda with all defined primary keys. */ - template - function_call operator()(CallArgs... callArgs) const { - check_function_call(); - return {this->udf_holder(), {std::forward(callArgs)...}}; + 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); } - constexpr auto udf_holder() const { - return internal::udf_holder{}; + 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; } - // returns a character range - constexpr auto name() const { - return this->udf_holder()(); + std::vector primary_key_column_names() const { + using pkcol_index_sequence = col_index_sequence_with; + + 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(); + } } - }; -#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; + 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); + }); + } - /* - * Generates the SQL function call. + template + std::vector composite_key_columns_names(const primary_key_t& primaryKey) const { + return create_from_tuple>(primaryKey.columns, + [this, empty = std::string{}](auto& memberPointer) { + if(const std::string* columnName = + this->find_column_name(memberPointer)) { + return *columnName; + } else { + return empty; + } + }); + } + + /** + * Searches column name by class member pointer passed as the first argument. + * @return column name or empty string if nothing found. */ - template - function_call operator()(CallArgs... callArgs) const { - check_function_call(); - return {this->udf_holder(), {std::forward(callArgs)...}}; + 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; } - /* - * Return original `udf` if stateless or a copy of it otherwise + /** + * Call passed lambda with all defined foreign keys. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` */ - constexpr decltype(auto) callable() const { - if constexpr(stateless) { - return (this->udf); - } else { - // non-const copy - return F(this->udf); - } + 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); } - constexpr auto udf_holder() const { - return internal::udf_holder{this->name()}; + 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); } - constexpr auto name() const { - return this->nme; + /** + * 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 - consteval quoted_scalar_function(const char (&name)[N], Args&&... constructorArgs) : - udf(std::forward(constructorArgs)...) { - std::copy_n(name, N, this->nme); + /** + * 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); } - F udf; - char nme[N]; + /** + * 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); + } + + std::vector get_table_info() const; }; - template - struct quoted_function_builder : cstring_literal { - using cstring_literal::cstring_literal; + template + struct is_table : std::false_type {}; - /* - * From a freestanding function, possibly overloaded. + template + struct is_table> : std::true_type {}; + + template + struct virtual_table_t : basic_table { + using module_details_type = M; + using object_type = typename module_details_type::object_type; + using elements_type = typename module_details_type::columns_type; + + static constexpr bool is_without_rowid_v = false; + using is_without_rowid = polyfill::bool_constant; + + module_details_type module_details; + +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + virtual_table_t(std::string name, module_details_type module_details) : + basic_table{std::move(name)}, module_details{std::move(module_details)} {} +#endif + + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. */ - template - [[nodiscard]] consteval auto quote(F* callable) const { - return quoted_scalar_function{this->cstr, std::move(callable)}; + template class OpTraitFn, class L> + void for_each_column_excluding(L&& lambda) const { + this->module_details.template for_each_column_excluding(lambda); } - /* - * From a classic function object instance. + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. */ - 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 = true> + void for_each_column_excluding(L&& lambda) const { + this->module_details.template for_each_column_excluding(lambda); + } + + /** + * Call passed lambda with all defined columns. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template + void for_each_column(L&& lambda) const { + this->module_details.for_each_column(lambda); } + }; + + template + struct is_virtual_table : std::false_type {}; + + template + struct is_virtual_table> : std::true_type {}; + +#if SQLITE_VERSION_NUMBER >= 3009000 + template + struct using_fts5_t { + using object_type = T; + using columns_type = std::tuple; + + columns_type columns; + + using_fts5_t(columns_type columns) : columns(std::move(columns)) {} - /* - * From a function object instance, picking the overloaded call operator. + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. */ - 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 class OpTraitFn, class L> + void for_each_column_excluding(L&& lambda) const { + iterate_tuple(this->columns, col_index_sequence_excluding{}, lambda); } - /* - * From a classic function object type. + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. */ - template - requires(stateless || std::copy_constructible) - [[nodiscard]] consteval auto quote(Args&&... constructorArgs) const { - using Sig = function_signature_type_t; - return quoted_scalar_function{this->cstr, std::forward(constructorArgs)...}; + template = true> + void for_each_column_excluding(L&& lambda) const { + this->template for_each_column_excluding(lambda); } - /* - * From a function object type, picking the overloaded call operator. + /** + * Call passed lambda with all defined columns. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` */ - 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)...}; + template + void for_each_column(L&& lambda) const { + using col_index_sequence = filter_tuple_sequence_t; + iterate_tuple(this->columns, col_index_sequence{}, lambda); } }; #endif + + 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 + bool exists_in_composite_primary_key(const virtual_table_t& /*virtualTable*/, + const column_field& /*column*/) { + return false; + } } - /** @short Call a user-defined function. +#if SQLITE_VERSION_NUMBER >= 3009000 + template>::object_type> + internal::using_fts5_t using_fts5(Cs... columns) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); + } + + template + internal::using_fts5_t using_fts5(Cs... columns) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::make_tuple(std::forward(columns)...)}); + } +#endif + + /** + * Factory function for a table definition. * - * 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)); + * The mapped object type is determined implicitly from the first column definition. + */ + template>::object_type> + internal::table_t make_table(std::string name, Cs... args) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } + + /** + * Factory function for a table definition. * + * The mapped object type is explicitly specified. */ - template + template + internal::table_t make_table(std::string name, Cs... args) { + static_assert(polyfill::conjunction_v...>, + "Incorrect table elements or constraints"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {std::move(name), std::make_tuple(std::forward(args)...)}); + } + #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - requires(orm_scalar_udf || orm_aggregate_udf) + /** + * 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 - 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()`. + template + internal::virtual_table_t make_virtual_table(std::string name, M module_details) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(module_details)}); + } +} + +// #include "storage_lookup.h" + +// interface functions +namespace sqlite_orm { + namespace internal { + + template + using tables_index_sequence = filter_tuple_sequence_t; + + template = true> + int foreign_keys_count(const DBOs& dbObjects) { + int res = 0; + iterate_tuple(dbObjects, tables_index_sequence{}, [&res](const auto& table) { + res += table.template count_of(); + }); + return res; + } + + template> + 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); + } + + /** + * 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); + } + + /** + * 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<>. * - * 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)); + * internal note: there's an overload for `find_column_name()` that avoids going through `table_t<>::find_column_name()` */ - template - [[nodiscard]] consteval auto operator"" _scalar() { - return builder; + 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; + + // 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."); + + return &aliased_field< + ColAlias, + std::tuple_element_t>::field; + } +#endif + + /** + * Find column name by: + * 1. by explicit object type and member pointer. + * 2. by moniker and member pointer. + */ + template = true> + const std::string* find_column_name(const DBOs& dbObjects, const column_pointer& cp) { + auto field = materialize_column_pointer(dbObjects, cp); + return pick_table(dbObjects).find_column_name(field); + } + +#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; } - } #endif + } } -// #include "ast/special_keywords.h" +// #include "journal_mode.h" + +#include // std::back_inserter +#include // std::string +#include // std::unique_ptr +#include // std::array +#include // std::transform +#include // std::toupper + +#if defined(_WINNT_) +// DELETE is a macro defined in the Windows SDK (winnt.h) +#pragma push_macro("DELETE") +#undef DELETE +#endif namespace sqlite_orm { - namespace internal { - struct current_time_t {}; - struct current_date_t {}; - struct current_timestamp_t {}; - } - inline internal::current_time_t current_time() { - return {}; - } + /** + * 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, + }; - inline internal::current_date_t current_date() { - return {}; - } + namespace internal { - inline internal::current_timestamp_t current_timestamp() { - return {}; + inline const std::string& to_string(journal_mode j) { + static std::string res[] = { + "DELETE", + "TRUNCATE", + "PERSIST", + "MEMORY", + "WAL", + "OFF", + }; + return res[static_cast(j)]; + } + + inline std::unique_ptr journal_mode_from_string(const std::string& str) { + std::string upper_str; + std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) { + return static_cast(std::toupper(static_cast(c))); + }); + static std::array all = {{ + journal_mode::DELETE, + journal_mode::TRUNCATE, + journal_mode::PERSIST, + journal_mode::MEMORY, + journal_mode::WAL, + journal_mode::OFF, + }}; + for(auto j: all) { + if(to_string(j) == upper_str) { + return std::make_unique(j); + } + } + return {}; + } } } -namespace sqlite_orm { +#if defined(_WINNT_) +#pragma pop_macro("DELETE") +#endif - namespace internal { +// #include "mapped_view.h" - /** - * Obtains the result type of expressions that form the columns of a select statement. - * - * This is a proxy class used to define what type must have result type depending on select - * arguments (member pointer, aggregate functions, etc). Below you can see specializations - * for different types. E.g. specialization for internal::length_t has `type` int cause - * LENGTH returns INTEGER in sqlite. Every column_result_t must have `type` type that equals - * c++ SELECT return type for T - * DBOs - db_objects_tuple type - * T - C++ type - * SFINAE - sfinae argument - */ - template - struct column_result_t { -#ifdef __FUNCTION__ - // produce an error message that reveals `T` and `DBOs` - static constexpr bool reveal() { - static_assert(polyfill::always_false_v, "T not found in DBOs - " __FUNCTION__); - } - static constexpr bool trigger = reveal(); +#include +#include // std::forward, std::move + +// #include "row_extractor.h" + +#include +#include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if +#include // std::atof, std::atoi, std::atoll +#include // std::strlen +#include // std::system_error +#include // std::string, std::wstring +#ifndef SQLITE_ORM_OMITS_CODECVT +#include // std::wstring_convert +#include // std::codecvt_utf8_utf16 +#endif +#include // std::vector +#include // std::copy +#include // std::back_inserter +#include // std::tuple, std::tuple_size, std::tuple_element +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED +#include #endif - }; - template - using column_result_of_t = typename column_result_t::type; +// #include "functional/cxx_universal.h" +// ::nullptr_t, ::size_t +// #include "functional/cxx_functional_polyfill.h" - template - using column_result_for_tuple_t = - transform_tuple_t::template fn>; +// #include "functional/static_magic.h" -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template - struct column_result_t, void> { - using type = std::optional>; - }; +// #include "tuple_helper/tuple_transformer.h" - template - struct column_result_t, void> { - using type = std::optional; - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "column_result_proxy.h" - template - struct column_result_t, void> { - using type = bool; - }; +// #include "arithmetic_tag.h" - template - struct column_result_t, void> { - using type = bool; - }; +// #include "pointer_value.h" - template - struct column_result_t { - using type = std::string; - }; +// #include "journal_mode.h" - template - struct column_result_t { - using type = std::string; - }; +// #include "error_code.h" - template - struct column_result_t { - using type = std::string; +// #include "is_std_ptr.h" + +// #include "type_traits.h" + +namespace sqlite_orm { + + /** + * 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; + + /* + * Called during multi-step query execution (result set) for each column of a result row. + */ + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; + + /* + * Called before invocation of user-defined scalar or aggregate functions, + * in order to unbox dynamically typed SQL function values into a tuple of C++ function arguments. + */ + V extract(sqlite3_value* value) const = delete; + }; + +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + template + concept orm_column_text_extractable = requires(const row_extractor& extractor, const char* columnText) { + { extractor.extract(columnText) } -> std::same_as; + }; + + template + concept orm_row_value_extractable = + requires(const row_extractor& extractor, sqlite3_stmt* stmt, int columnIndex) { + { extractor.extract(stmt, columnIndex) } -> std::same_as; }; - template - struct column_result_t> : member_field_type {}; + template + concept orm_boxed_value_extractable = requires(const row_extractor& extractor, sqlite3_value* value) { + { extractor.extract(value) } -> std::same_as; + }; +#endif + + namespace internal { + /* + * Make a row extractor to be used for casting SQL column text to a C++ typed value. + */ + template +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_column_text_extractable) +#endif + row_extractor column_text_extractor() { + return {}; + } - template - struct column_result_t, void> { - using type = R; - }; + /* + * 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, void> { - using type = R; - }; + /* + * 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, void> { - using type = typename callable_arguments::return_type; - }; + 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, S, X, Rest...>, void> { - using type = std::unique_ptr>; - }; +#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, S, X>, void> { - using type = std::unique_ptr>; - }; + V extract(const char* columnText) const = delete; - template - struct column_result_t, void> { - using type = int; - }; + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; - template - struct column_result_t { - using type = nullptr_t; - }; + V extract(sqlite3_value* value) const { + return {(P*)sqlite3_value_pointer(value, T::value)}; + } + }; - template - struct column_result_t { - using type = int; - }; + /** + * Undefine using pointer_binding<> for querying values + */ + template + struct row_extractor, void>; +#endif - template - struct column_result_t, void> : column_result_t {}; + /** + * 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, void> : column_result_t {}; + V extract(sqlite3_stmt* stmt, int columnIndex) const { + return this->extract(stmt, columnIndex, tag()); + } - template - struct column_result_t, void> { - using type = std::string; - }; + V extract(sqlite3_value* value) const { + return this->extract(value, tag()); + } - template - struct column_result_t, void> { - using type = double; - }; + private: + using tag = arithmetic_tag_t; - template - struct column_result_t, void> { - using type = double; - }; + V extract(const char* columnText, const int_or_smaller_tag&) const { + return static_cast(std::atoi(columnText)); + } - template - struct column_result_t, void> { - using type = double; - }; + V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { + return static_cast(sqlite3_column_int(stmt, columnIndex)); + } - template - struct column_result_t, void> { - using type = double; - }; + V extract(sqlite3_value* value, const int_or_smaller_tag&) const { + return static_cast(sqlite3_value_int(value)); + } - template - struct column_result_t, void> { - using type = double; - }; + V extract(const char* columnText, const bigint_tag&) const { + return static_cast(std::atoll(columnText)); + } - template - struct column_result_t, void> { - using type = int; - }; + V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { + return static_cast(sqlite3_column_int64(stmt, columnIndex)); + } - template - struct column_result_t, void> { - using type = int; - }; + V extract(sqlite3_value* value, const bigint_tag&) const { + return static_cast(sqlite3_value_int64(value)); + } - template - struct column_result_t, void> { - using type = int; - }; + V extract(const char* columnText, const real_tag&) const { + return static_cast(std::atof(columnText)); + } - template - struct column_result_t, void> { - using type = int; - }; + V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { + return static_cast(sqlite3_column_double(stmt, columnIndex)); + } - template - struct column_result_t, void> { - using type = int; - }; + V extract(sqlite3_value* value, const real_tag&) const { + return static_cast(sqlite3_value_double(value)); + } + }; - template - struct column_result_t { - using type = int64; - }; + /** + * 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 { - using type = int64; - }; + 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 { - using type = int64; - }; + 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 = int64; - }; + std::wstring extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); + if(cStr) { + std::wstring_convert> converter; + return converter.from_bytes(cStr); + } else { + return {}; + } + } - template - struct column_result_t, void> { - using type = int64; - }; + std::wstring extract(sqlite3_value* value) const { + if(auto cStr = (const wchar_t*)sqlite3_value_text16(value)) { + return cStr; + } else { + return {}; + } + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT + + template + struct row_extractor::value>> { + using unqualified_type = std::remove_cv_t; - template - struct column_result_t, void> { - using type = int64; - }; + V extract(const char* columnText) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_column_text_extractable) +#endif + { + if(columnText) { + const row_extractor rowExtractor{}; + return is_std_ptr::make(rowExtractor.extract(columnText)); + } else { + return {}; + } + } - template - struct column_result_t, void> : column_result_t {}; + V extract(sqlite3_stmt* stmt, int columnIndex) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_row_value_extractable) +#endif + { + auto type = sqlite3_column_type(stmt, columnIndex); + if(type != SQLITE_NULL) { + const row_extractor rowExtractor{}; + return is_std_ptr::make(rowExtractor.extract(stmt, columnIndex)); + } else { + return {}; + } + } - template - struct column_result_t, void> : column_result_t {}; + V extract(sqlite3_value* value) const +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires(orm_boxed_value_extractable) +#endif + { + auto type = sqlite3_value_type(value); + if(type != SQLITE_NULL) { + const row_extractor rowExtractor{}; + return is_std_ptr::make(rowExtractor.extract(value)); + } else { + return {}; + } + } + }; -#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; +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct row_extractor>> { + using unqualified_type = std::remove_cv_t; - // lookup ColAlias in the final column references - using colalias_index = - find_tuple_type>; - static_assert(colalias_index::value < std::tuple_size_v, - "No such column mapped into the CTE."); - using type = std::tuple_element_t; - }; + V extract(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> - : conc_tuple>>...> {}; + 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 = structure>>...>>; - }; + 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> : column_result_t {}; + template<> + struct row_extractor { + nullptr_t extract(const char* /*columnText*/) const { + return nullptr; + } - template - struct column_result_t> { - using type = - polyfill::detected_t>; - static_assert(!std::is_same::value, - "Compound select statements must return a common type"); - }; + nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { + return nullptr; + } - template - struct column_result_t> { - using type = typename T::result_type; - }; + nullptr_t extract(sqlite3_value*) const { + return nullptr; + } + }; + /** + * Specialization for std::vector. + */ + template<> + struct row_extractor, void> { + std::vector extract(const char* columnText) const { + return {columnText, columnText + (columnText ? std::strlen(columnText) : 0)}; + } - template - struct column_result_t, void> { - using type = std::string; - }; + 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}; + } - /** - * Result for the most simple queries like `SELECT 1` - */ - template - struct column_result_t> { - using type = T; - }; + std::vector extract(sqlite3_value* value) const { + auto bytes = static_cast(sqlite3_value_blob(value)); + auto len = static_cast(sqlite3_value_bytes(value)); + return {bytes, bytes + len}; + } + }; - /** - * Result for the most simple queries like `SELECT 'ototo'` - */ - template - struct column_result_t { - using type = std::string; - }; + /** + * Specialization for journal_mode. + */ + template<> + struct row_extractor { + journal_mode extract(const char* columnText) const { + if(columnText) { + if(auto res = internal::journal_mode_from_string(columnText)) { + return std::move(*res); + } else { + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; + } + } else { + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; + } + } - template - struct column_result_t { - using type = std::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 column_result_t, void> : column_result_t> {}; + journal_mode extract(sqlite3_value* value) const = delete; + }; - template - struct column_result_t, void> - : storage_traits::storage_mapped_columns> {}; + namespace internal { - template - struct column_result_t, void> { - using type = table_reference; - }; + /* + * Helper to extract a structure from a rowset. + */ + template + struct struct_extractor; - template - struct column_result_t, void> { - using type = T; - }; +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + /* + * Returns a value-based row extractor for an unmapped type, + * returns a structure extractor for a table reference, tuple or named struct. + */ + template + auto make_row_extractor([[maybe_unused]] const DBOs& dbObjects) { + if constexpr(polyfill::is_specialization_of_v || + polyfill::is_specialization_of_v || is_table_reference_v) { + return struct_extractor{dbObjects}; + } else { + return row_value_extractor(); + } + } +#else + /* + * Overload for an unmapped type returns a common row extractor. + */ + template< + class R, + class DBOs, + std::enable_if_t, + polyfill::is_specialization_of, + is_table_reference>>::value, + bool> = true> + auto make_row_extractor(const DBOs& /*dbObjects*/) { + return row_value_extractor(); + } - template - struct column_result_t, void> { - using type = R; - }; + /* + * Overload for a table reference, tuple or aggregate of column results returns a structure extractor. + */ + template, + polyfill::is_specialization_of, + is_table_reference>::value, + bool> = true> + struct_extractor make_row_extractor(const DBOs& dbObjects) { + return {dbObjects}; + } +#endif - template - struct column_result_t, void> { - using type = bool; - }; + /** + * Specialization for a tuple of top-level column results. + */ + template + struct struct_extractor, DBOs> { + const DBOs& db_objects; - template - struct column_result_t, void> { - using type = bool; - }; + std::tuple extract(const char* columnText) const = delete; - template - struct column_result_t, void> { - using type = bool; - }; + // note: expects to be called only from the top level, and therefore discards the index + std::tuple...> extract(sqlite3_stmt* stmt, + int&& /*nextColumnIndex*/ = 0) const { + int columnIndex = -1; + return {make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + } - template - struct column_result_t, void> : column_result_t {}; - } -} + // unused to date + std::tuple...> extract(sqlite3_stmt* stmt, int& columnIndex) const = delete; -// #include "mapped_type_proxy.h" + std::tuple extract(sqlite3_value* value) const = delete; + }; -// #include "sync_schema_result.h" + /** + * Specialization for an unmapped structure to be constructed ad-hoc from column results. + * + * This plays together with `column_result_of_t`, which returns `struct_t` as `structure` + */ + template + struct struct_extractor>, DBOs> { + const DBOs& db_objects; -// #include "table_info.h" + O extract(const char* columnText) const = delete; -// #include "storage_impl.h" + // note: expects to be called only from the top level, and therefore discards the index; + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + // see unit test tests/prepared_statement_tests/select.cpp/TEST_CASE("Prepared select")/SECTION("non-aggregate struct") + template = true> + O extract(sqlite3_stmt* stmt, int&& /*nextColumnIndex*/ = 0) const { + int columnIndex = -1; + return O{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + } -// #include "journal_mode.h" + template = true> + O extract(sqlite3_stmt* stmt, int&& /*nextColumnIndex*/ = 0) const { + int columnIndex = -1; + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + std::tuple t{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + return create_from_tuple(std::move(t), std::index_sequence_for{}); + } -// #include "mapped_view.h" + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + // see unit test tests/prepared_statement_tests/select.cpp/TEST_CASE("Prepared select")/SECTION("non-aggregate struct") + template = true> + O extract(sqlite3_stmt* stmt, int& columnIndex) const { + --columnIndex; + return O{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + } -#include -#include // std::forward, std::move + template = true> + O extract(sqlite3_stmt* stmt, int& columnIndex) const { + --columnIndex; + // note: brace-init-list initialization guarantees order of evaluation, but only for aggregates and variadic constructors it seems. + std::tuple t{make_row_extractor(this->db_objects).extract(stmt, ++columnIndex)...}; + return create_from_tuple(std::move(t), std::index_sequence_for{}); + } -// #include "row_extractor.h" + O extract(sqlite3_value* value) const = delete; + }; + } +} // #include "mapped_iterator.h" @@ -14053,7 +13724,6 @@ namespace sqlite_orm { internal::dynamic_values_t values(std::vector vector) { return {{std::move(vector)}}; } - } // #include "table_reference.h" @@ -15219,6 +14889,8 @@ namespace sqlite_orm { // #include "ast/match.h" +#include + namespace sqlite_orm { namespace internal { @@ -16191,6 +15863,7 @@ inline constexpr bool std::ranges::enable_borrowed_range // std::function, std::bind, std::bind_front #include // std::string #include // std::stringstream +#include // std::flush #include // std::move #include // std::system_error #include // std::vector @@ -16249,6 +15922,7 @@ namespace sqlite_orm { #include // std::shared_ptr #include // std::vector #include +#include // std::flush // #include "error_code.h" @@ -16267,7 +15941,7 @@ namespace sqlite_orm { #include #include #include -#include // std::exchange, std::tuple_size +#include // std::exchange, std::tuple_size, std::make_index_sequence // #include "functional/cxx_universal.h" // ::size_t @@ -17431,7 +17105,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 +17336,8 @@ namespace sqlite_orm { // #include "serializing_util.h" +// #include "table_info.h" + namespace sqlite_orm { namespace internal { @@ -18729,7 +18405,7 @@ namespace sqlite_orm { // #include "functional/cxx_optional.h" // #include "functional/cxx_universal.h" - +// ::nullptr_t, ::size_t // #include "functional/cxx_functional_polyfill.h" // #include "functional/mpl.h" @@ -18753,496 +18429,785 @@ namespace sqlite_orm { struct rank_t {}; } - inline internal::rank_t rank() { - return {}; + inline internal::rank_t rank() { + return {}; + } +} + +// #include "ast/special_keywords.h" + +// #include "core_functions.h" + +// #include "constraints.h" + +// #include "conditions.h" + +// #include "indexed_column.h" + +// #include "function.h" + +// #include "prepared_statement.h" + +// #include "rowid.h" + +// #include "pointer_value.h" + +// #include "type_printer.h" + +// #include "field_printer.h" + +// #include "literal.h" + +// #include "table_name_collector.h" + +// #include "column_names_getter.h" + +#include // std::is_base_of +#include // std::string +#include // std::vector +#include // std::reference_wrapper +#include +#include // std::move + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "error_code.h" + +// #include "mapped_type_proxy.h" + +// #include "alias_traits.h" + +// #include "select_constraints.h" + +// #include "storage_lookup.h" +// pick_table +// #include "serializer_context.h" + +// #include "util.h" + +namespace sqlite_orm { + + namespace internal { + + template + auto serialize(const T& t, const C& context); + + template + std::vector& collect_table_column_names(std::vector& collectedExpressions, + bool definedOrder, + const Ctx& context) { + if(definedOrder) { + auto& table = pick_table>(context.db_objects); + collectedExpressions.reserve(collectedExpressions.size() + table.template count_of()); + table.for_each_column([qualified = !context.skip_table_name, + &tableName = table.name, + &collectedExpressions](const column_identifier& column) { + if(is_alias::value) { + collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + "." + + quote_identifier(column.name)); + } else if(qualified) { + collectedExpressions.push_back(quote_identifier(tableName) + "." + + quote_identifier(column.name)); + } else { + collectedExpressions.push_back(quote_identifier(column.name)); + } + }); + } else { + collectedExpressions.reserve(collectedExpressions.size() + 1); + if(is_alias::value) { + collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); + } else if(!context.skip_table_name) { + const basic_table& table = pick_table>(context.db_objects); + collectedExpressions.push_back(quote_identifier(table.name) + ".*"); + } else { + collectedExpressions.emplace_back("*"); + } + } + + return collectedExpressions; + } + + /** @short Column expression collector. + */ + struct column_names_getter { + /** + * The default implementation simply serializes the passed argument. + */ + template + std::vector& operator()(const E& t, const Ctx& context) { + auto columnExpression = serialize(t, context); + if(columnExpression.empty()) { + throw std::system_error{orm_error_code::column_not_found}; + } + this->collectedExpressions.reserve(this->collectedExpressions.size() + 1); + this->collectedExpressions.push_back(std::move(columnExpression)); + return this->collectedExpressions; + } + + template + std::vector& operator()(const std::reference_wrapper& expression, const Ctx& context) { + return (*this)(expression.get(), context); + } + + template + std::vector& operator()(const asterisk_t& expression, const Ctx& context) { + return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); + } + + template + std::vector& operator()(const object_t& expression, const Ctx& context) { + return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); + } + + template + std::vector& operator()(const columns_t& cols, const Ctx& context) { + this->collectedExpressions.reserve(this->collectedExpressions.size() + cols.count); + iterate_tuple(cols.columns, [this, &context](auto& colExpr) { + (*this)(colExpr, context); + }); + // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order + if(tuple_has_template::columns_type, asterisk_t>::value && + this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + this->collectedExpressions.shrink_to_fit(); + } + return this->collectedExpressions; + } + + template + std::vector& operator()(const struct_t& cols, const Ctx& context) { + this->collectedExpressions.reserve(this->collectedExpressions.size() + cols.count); + iterate_tuple(cols.columns, [this, &context](auto& colExpr) { + (*this)(colExpr, context); + }); + // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order + if(tuple_has_template::columns_type, asterisk_t>::value && + this->collectedExpressions.capacity() > this->collectedExpressions.size()) { + this->collectedExpressions.shrink_to_fit(); + } + return this->collectedExpressions; + } + + std::vector collectedExpressions; + }; + + template + std::vector get_column_names(const T& t, const Ctx& context) { + column_names_getter serializer; + return serializer(t, context); + } } } -// #include "ast/special_keywords.h" +// #include "cte_column_names_collector.h" -// #include "core_functions.h" +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#include +#include +#include // std::reference_wrapper +#include +#endif -// #include "constraints.h" +// #include "functional/cxx_universal.h" -// #include "conditions.h" +// #include "functional/cxx_type_traits_polyfill.h" -// #include "schema/column.h" +// #include "type_traits.h" -// #include "indexed_column.h" +// #include "member_traits/member_traits.h" -// #include "function.h" +// #include "error_code.h" -// #include "prepared_statement.h" +// #include "alias.h" -// #include "rowid.h" +// #include "select_constraints.h" -// #include "pointer_value.h" +// #include "serializer_context.h" -// #include "type_printer.h" +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +namespace sqlite_orm { + namespace internal { + // collecting column names utilizes the statement serializer + template + auto serialize(const T& t, const C& context); -// #include "field_printer.h" + inline void unquote_identifier(std::string& identifier) { + if(!identifier.empty()) { + constexpr char quoteChar = '"'; + constexpr char sqlEscaped[] = {quoteChar, quoteChar}; + identifier.erase(identifier.end() - 1); + identifier.erase(identifier.begin()); + for(size_t pos = 0; (pos = identifier.find(sqlEscaped, pos, 2)) != identifier.npos; ++pos) { + identifier.erase(pos, 1); + } + } + } -// #include "literal.h" + 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(); + } + } -// #include "table_name_collector.h" + template + struct cte_column_names_collector { + using expression_type = T; -// #include "column_names_getter.h" + // Compound statements are never passed in by db_objects_for_expression() + static_assert(!is_compound_operator_v); -#include // std::is_base_of -#include // std::string -#include // std::vector -#include // std::reference_wrapper -#include -#include // std::move + template + std::vector operator()(const expression_type& t, const Ctx& context) const { + auto newContext = context; + newContext.skip_table_name = true; + std::string columnName = serialize(t, newContext); + if(columnName.empty()) { + throw std::system_error{orm_error_code::column_not_found}; + } + unquote_or_erase(columnName); + return {std::move(columnName)}; + } + }; -// #include "tuple_helper/tuple_traits.h" + template + std::vector get_cte_column_names(const T& t, const Ctx& context) { + cte_column_names_collector collector; + return collector(t, context); + } -// #include "tuple_helper/tuple_iteration.h" + template + struct cte_column_names_collector> { + using expression_type = As; -// #include "error_code.h" + template + std::vector operator()(const expression_type& /*expression*/, const Ctx& /*context*/) const { + return {alias_extractor>::extract()}; + } + }; -// #include "mapped_type_proxy.h" + template + struct cte_column_names_collector> { + using expression_type = Wrapper; -// #include "alias_traits.h" + template + std::vector operator()(const expression_type& expression, const Ctx& context) const { + return get_cte_column_names(expression.get(), context); + } + }; -// #include "select_constraints.h" + template + struct cte_column_names_collector> { + using expression_type = Asterisk; + using T = typename Asterisk::type; -// #include "storage_lookup.h" -// pick_table -// #include "serializer_context.h" + template + std::vector operator()(const expression_type&, const Ctx& context) const { + auto& table = pick_table(context.db_objects); -// #include "util.h" + std::vector columnNames; + columnNames.reserve(size_t(table.template count_of())); -namespace sqlite_orm { + table.for_each_column([&columnNames](const column_identifier& column) { + columnNames.push_back(column.name); + }); + return columnNames; + } + }; - namespace internal { + // No CTE for object expressions. + template + struct cte_column_names_collector> { + static_assert(polyfill::always_false_v, "Selecting an object in a subselect is not allowed."); + }; - template - auto serialize(const T& t, const C& context); + // 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 - std::vector& collect_table_column_names(std::vector& collectedExpressions, - bool definedOrder, - const Ctx& context) { - if(definedOrder) { - auto& table = pick_table>(context.db_objects); - collectedExpressions.reserve(collectedExpressions.size() + table.template count_of()); - table.for_each_column([qualified = !context.skip_table_name, - &tableName = table.name, - &collectedExpressions](const column_identifier& column) { - if(is_alias::value) { - collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + "." + - quote_identifier(column.name)); - } else if(qualified) { - collectedExpressions.push_back(quote_identifier(tableName) + "." + - quote_identifier(column.name)); + 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 { - collectedExpressions.push_back(quote_identifier(column.name)); + 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()); } }); - } else { - collectedExpressions.reserve(collectedExpressions.size() + 1); - if(is_alias::value) { - collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); - } else if(!context.skip_table_name) { - const basic_table& table = pick_table>(context.db_objects); - collectedExpressions.push_back(quote_identifier(table.name) + ".*"); - } else { - collectedExpressions.emplace_back("*"); - } + return columnNames; } + }; - return collectedExpressions; - } + 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); - /** @short Column expression collector. - */ - struct column_names_getter { - /** - * The default implementation simply serializes the passed argument. - */ - template - std::vector& operator()(const E& t, const Ctx& context) { - auto columnExpression = serialize(t, context); - if(columnExpression.empty()) { + // 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}; } - this->collectedExpressions.reserve(this->collectedExpressions.size() + 1); - this->collectedExpressions.push_back(std::move(columnExpression)); - return this->collectedExpressions; + + 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 std::reference_wrapper& expression, const Ctx& context) { - return (*this)(expression.get(), context); - } + return columnNames; + } + } +} +#endif - template - std::vector& operator()(const asterisk_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 object_t& expression, const Ctx& context) { - return collect_table_column_names(this->collectedExpressions, expression.defined_order, context); - } +#include // std::string +#include // std::stringstream - 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; - } +namespace sqlite_orm { - 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 internal { - std::vector collectedExpressions; - }; + 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 "../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 ? +// (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" - -#include // std::string -#include // std::stringstream + trigger_type_base_t update() { + return {timing, trigger_type::trigger_update}; + } -namespace sqlite_orm { + template + trigger_update_type_t update_of(Cs... columns) { + return {timing, trigger_type::trigger_update, std::forward(columns)...}; + } + }; - namespace internal { + struct raise_t { + enum class type_t { + ignore, + rollback, + abort, + fail, + }; - template - struct order_by_serializer; + type_t type = type_t::ignore; + std::string message; - template - std::string serialize_order_by(const T& t, const Ctx& context) { - order_by_serializer serializer; - return serializer(t, context); - } +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + raise_t(type_t type, std::string message) : type{type}, message{std::move(message)} {} +#endif + }; - template - struct order_by_serializer, void> { - using statement_type = order_by_t; + template + struct new_t { + using expression_type = T; - template - std::string operator()(const statement_type& orderBy, const Ctx& context) const { - std::stringstream ss; - auto newContext = context; - newContext.skip_table_name = false; + expression_type expression; + }; - 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 old_t { + using expression_type = T; + + 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 { @@ -22142,126 +22107,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 { @@ -22269,1355 +22593,1370 @@ 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); + } + + 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"); + + 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 /** - * 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. + * Returns a string representation of object of a class mapped to the storage. + * Type of string has json-like style. */ - template - O get(Ids... ids) { + 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)) << "'"; + }); + ss << " }"; + return ss.str(); + } + + /** + * 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 + void replace(const O& o) { + this->assert_mapped_type(); + auto statement = this->prepare(sqlite_orm::replace(std::ref(o))); + this->execute(statement); + } + + 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(std::forward(ids)...)); - return this->execute(statement); - } + if(from == to) { + return; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto get(Ids... ids) { - return this->get>(std::forward(ids)...); + auto statement = + this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); } -#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. - */ - template - std::unique_ptr get_pointer(Ids... ids) { + template + void replace_range(It from, It to, Projection project = {}) { this->assert_mapped_type(); - auto statement = this->prepare(sqlite_orm::get_pointer(std::forward(ids)...)); - return this->execute(statement); - } + if(from == to) { + return; + } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto get_pointer(Ids... ids) { - return this->get_pointer>(std::forward(ids)...); + auto statement = + this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); } -#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.) - */ - template - std::shared_ptr get_no_throw(Ids... ids) { - return std::shared_ptr(this->get_pointer(std::forward(ids)...)); + 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)); } -#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. + * 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 - std::optional get_optional(Ids... ids) { + template + int insert(const O& o) { this->assert_mapped_type(); - auto statement = this->prepare(sqlite_orm::get_optional(std::forward(ids)...)); - return this->execute(statement); + this->assert_insertable_type(); + auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); + return int(this->execute(statement)); } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template - auto get_optional(Ids... ids) { - return this->get_optional>(std::forward(ids)...); + /** + * 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); } -#endif /** - * SELECT COUNT(*) https://www.sqlite.org/lang_aggfunc.html#count - * @return Number of O object in table. + * 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 - 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(Args... args) { + auto statement = this->prepare(sqlite_orm::replace(std::forward(args)...)); + this->execute(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_WITH_CPP20_ALIASES - template - int count(Args&&... args) { - return this->count>(std::forward(args)...); + 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); } -#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. + * Change table name inside storage's schema info. This function does not + * affect 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 + 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; + /** - * 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. + * Get table's name stored in storage's schema info. This function does not call + * any SQLite queries */ - 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 + const std::string& tablename() const { + this->assert_mapped_type(); + auto& table = this->get_table(); + return table.name; } - template, is_column_pointer>::value, - bool> = true> - std::string group_concat(F field) { - return this->group_concat_internal(std::move(field), {}); + 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); } - /** - * 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)...); + template + const std::string* find_column_name(F O::*memberPointer) const { + return internal::find_column_name(this->db_objects, memberPointer); } - /** - * 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)...); + protected: + template + sync_schema_result schema_status(const virtual_table_t&, sqlite3*, bool, bool*) { + return sync_schema_result::already_in_sync; } - 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)...); + template + sync_schema_result schema_status(const trigger_t&, sqlite3*, bool, bool*) { + return sync_schema_result::already_in_sync; } - /** - * 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 + sync_schema_result schema_status(const index_t&, sqlite3*, bool, bool*) { + return sync_schema_result::already_in_sync; } - /** - * 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 {}; + 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; } - } - /** - * 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())); + 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" - 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)); - } +// #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 "../functional/cxx_universal.h" +// ::size_t +// #include "../type_printer.h" + +// #include "../schema/column.h" + +// #include "../schema/table.h" - 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)); - } +namespace sqlite_orm { + namespace internal { - 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); + 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; + } -#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 + } +} - 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 "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 - int64 execute(const prepared_statement_t>& statement) { - using object_type = statement_object_type_t; +#include // std::is_same +#include // std::stringstream +#include // std::flush +#include // std::reference_wrapper, std::cref +#include // std::find_if, std::ranges::find - sqlite3_stmt* stmt = reset_stmt(statement.stmt); +// #include "../type_traits.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 "../sqlite_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 // std::string - sqlite3_stmt* stmt = reset_stmt(statement.stmt); +// #include "schema/column.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)); - })); - }; +// #include "schema/table.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); - }); -#endif - }, - [&processObject](auto& expression) { - const object_type& o = get_object(expression); - processObject(o); - })(statement.expression); +// #include "column_pointer.h" - perform_step(stmt); - } +// #include "alias.h" - template, is_insert_range>::value, bool> = true> - int64 execute(const prepared_statement_t& statement) { - using object_type = statement_object_type_t; +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; - sqlite3_stmt* stmt = reset_stmt(statement.stmt); +#ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED + friend bool operator==(const sqlite_master&, const sqlite_master&) = default; +#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)); - } - })); - }; + 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)); + } - 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_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 - }, - [&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)); - } +// #include "../eponymous_vtabs/dbstat.h" - 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); - } +#ifdef SQLITE_ENABLE_DBSTAT_VTAB +#include // std::string +#endif - template - void execute(const prepared_statement_t>& statement) { - using object_type = statement_object_type_t; +// #include "../schema/column.h" - sqlite3_stmt* stmt = reset_stmt(statement.stmt); - auto& table = this->get_table(); +// #include "../schema/table.h" - 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); - } +namespace sqlite_orm { +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + struct dbstat { + std::string name; + std::string path; + int pageno = 0; + std::string pagetype; + int ncell = 0; + int payload = 0; + int unused = 0; + int mx_payload = 0; + int pgoffset = 0; + int pgsize = 0; + }; + + inline auto make_dbstat_table() { + return make_table("dbstat", + make_column("name", &dbstat::name), + make_column("path", &dbstat::path), + make_column("pageno", &dbstat::pageno), + make_column("pagetype", &dbstat::pagetype), + make_column("ncell", &dbstat::ncell), + make_column("payload", &dbstat::payload), + make_column("unused", &dbstat::unused), + make_column("mx_payload", &dbstat::mx_payload), + make_column("pgoffset", &dbstat::pgoffset), + make_column("pgsize", &dbstat::pgsize)); + } +#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 @@ -23801,763 +24140,419 @@ namespace sqlite_orm { template struct node_tuple, void> : node_tuple {}; - template - struct node_tuple, void> : node_tuple_for {}; - - template - struct node_tuple, void> : node_tuple_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); - } + template + struct node_tuple, void> : node_tuple_for {}; - 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. - */ +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + template + struct node_tuple, void> : node_tuple_for {}; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED -#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 + template + struct node_tuple, Wargs...>, void> : node_tuple_for {}; -// #include "pointer_value.h" + template + struct node_tuple, 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 {}; -#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_for {}; - 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 {}; - 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_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_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 {}; -// #include "schema/column.h" + template + struct node_tuple, void> : node_tuple {}; -// #include "schema/table.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 "column_pointer.h" + 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_for {}; - 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 {}; + + template + struct node_tuple, void> : node_tuple {}; + + template + struct node_tuple, void> : node_tuple_for {}; -#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 {}; + } } -#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); } -} -// #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 + 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 // std::decay_t -#include // std::move -#include // std::find_if, std::ranges::find + 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 "../functional/cxx_universal.h" -// ::size_t -// #include "../type_printer.h" + 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 "../schema/column.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/table.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); + } -namespace sqlite_orm { - namespace internal { + 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 - 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) { + 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; } - 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 55595b29..b217801e 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/third_party/amalgamate/config.json b/third_party/amalgamate/config.json index cdea2f87..ccf329ef 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" ] From b81b407d7a5eae4bd22f8adb6596b49006d98ffe Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 18 Jul 2024 08:47:43 +0200 Subject: [PATCH 04/38] Update using of internal namespace --- dev/alias.h | 2 +- include/sqlite_orm/sqlite_orm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/alias.h b/dev/alias.h index 5611415c..fd1541ee 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -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/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 22231980..9d52ff33 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -2648,7 +2648,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) { From c4f37700d2a62a9292e879d3f98a316b0d15389c Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 18 Jul 2024 08:48:14 +0200 Subject: [PATCH 05/38] Added more storage unit tests * last insert rowid * With insert * moved "With select" to storage_tests.cpp --- tests/storage_tests.cpp | 109 ++++++++++++++++++++++++++++++++++++++++ tests/tests3.cpp | 59 ---------------------- 2 files changed, 109 insertions(+), 59 deletions(-) diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index e084b57d..54e3da24 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -490,3 +490,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/tests3.cpp b/tests/tests3.cpp index 366ccc23..234a0eff 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 From b66c34875cf9e8069076f1c827bf6491f3bad856 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 19 Jul 2024 10:43:29 +0200 Subject: [PATCH 06/38] Added missing includes to dev/serializing_util.h --- dev/serializing_util.h | 3 +++ dev/table_reference.h | 2 -- dev/values.h | 2 +- include/sqlite_orm/sqlite_orm.h | 10 +++++++--- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/dev/serializing_util.h b/dev/serializing_util.h index 00dfc209..d768e786 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -9,11 +9,14 @@ #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/table_reference.h b/dev/table_reference.h index 8b20c916..c54f6d3b 100644 --- a/dev/table_reference.h +++ b/dev/table_reference.h @@ -1,8 +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/values.h b/dev/values.h index 25098cad..69329bcf 100644 --- a/dev/values.h +++ b/dev/values.h @@ -2,7 +2,7 @@ #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" diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 9d52ff33..80161ca5 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1949,8 +1949,6 @@ namespace sqlite_orm::internal { // #include "table_reference.h" #include // std::remove_const, std::type_identity -#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED -#include #endif // #include "functional/cxx_type_traits_polyfill.h" @@ -13685,7 +13683,7 @@ namespace sqlite_orm { #include // std::vector #include // std::tuple -#include // std::forward +#include // std::forward, std::move // #include "functional/cxx_universal.h" @@ -15947,8 +15945,12 @@ namespace sqlite_orm { // ::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" @@ -15957,6 +15959,8 @@ namespace sqlite_orm { // #include "util.h" +// #include "schema/column.h" + namespace sqlite_orm { namespace internal { template From d63911136f46702c45ce00f38f4ec5fb17c3d8a8 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 19 Jul 2024 10:44:11 +0200 Subject: [PATCH 07/38] Included explicitly in examples and unit tests When importing sqlite_orm as a named module, sqlite3 symbols are not exported. --- examples/blob_binding.cpp | 1 + examples/chrono_binding.cpp | 6 ++++++ examples/enum_binding.cpp | 2 +- examples/nullable_enum_binding.cpp | 2 +- tests/row_extractor.cpp | 1 + tests/static_tests/row_extractor.cpp | 1 + tests/storage_non_crud_tests.cpp | 1 + 7 files changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/blob_binding.cpp b/examples/blob_binding.cpp index 2d7e7fff..fdb4f1fe 100644 --- a/examples/blob_binding.cpp +++ b/examples/blob_binding.cpp @@ -1,3 +1,4 @@ +#include #include #include diff --git a/examples/chrono_binding.cpp b/examples/chrono_binding.cpp index d887b807..65b500d7 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/enum_binding.cpp b/examples/enum_binding.cpp index 0bee82ca..4982d4cf 100644 --- a/examples/enum_binding.cpp +++ b/examples/enum_binding.cpp @@ -1,4 +1,4 @@ - +#include #include #include diff --git a/examples/nullable_enum_binding.cpp b/examples/nullable_enum_binding.cpp index 37509714..bbd8f018 100644 --- a/examples/nullable_enum_binding.cpp +++ b/examples/nullable_enum_binding.cpp @@ -1,4 +1,4 @@ - +#include #include #include #include diff --git a/tests/row_extractor.cpp b/tests/row_extractor.cpp index 141541da..f0b98987 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 a7fc39fd..5279c21b 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 f4167dce..a4cfe0b4 100644 --- a/tests/storage_non_crud_tests.cpp +++ b/tests/storage_non_crud_tests.cpp @@ -1,3 +1,4 @@ +#include #include #include From 6a96d1cec4db5bda5136d11def3f6e51a04d5b9c Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 19 Jul 2024 10:53:00 +0200 Subject: [PATCH 08/38] Explicitly included in examples and unit tests --- examples/blob_binding.cpp | 1 + examples/synchronous.cpp | 12 ++++++------ tests/storage_tests.cpp | 1 + tests/sync_schema_tests.cpp | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/blob_binding.cpp b/examples/blob_binding.cpp index fdb4f1fe..3839c58a 100644 --- a/examples/blob_binding.cpp +++ b/examples/blob_binding.cpp @@ -1,5 +1,6 @@ #include #include +#include #include using namespace sqlite_orm; diff --git a/examples/synchronous.cpp b/examples/synchronous.cpp index 6147efff..d506bf84 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/tests/storage_tests.cpp b/tests/storage_tests.cpp index 54e3da24..e9e2244a 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -1,3 +1,4 @@ +#include #include #include diff --git a/tests/sync_schema_tests.cpp b/tests/sync_schema_tests.cpp index eb245a52..65d48945 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 From 4bdb5f3caa1bc59094fa452ce1b27b85f1fd0f39 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 20 Jul 2024 19:00:36 +0200 Subject: [PATCH 09/38] Use std-qualified `std::time_t` in examples --- examples/blob_binding.cpp | 4 ++-- examples/collate.cpp | 2 +- examples/common_table_expressions.cpp | 12 ++++++------ examples/composite_key.cpp | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/blob_binding.cpp b/examples/blob_binding.cpp index 3839c58a..af23f5e7 100644 --- a/examples/blob_binding.cpp +++ b/examples/blob_binding.cpp @@ -68,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]); @@ -112,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/collate.cpp b/examples/collate.cpp index 42679001..77748426 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 978e141d..e45120d2 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 08b77a84..2335bbfb 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 From f388350dbb2917380d011ca8fc96415237e87416 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 20 Jul 2024 19:03:37 +0200 Subject: [PATCH 10/38] Removed inclusion of `cxx_universal.h` where it's not needed --- dev/alias_traits.h | 1 - dev/ast/where.h | 1 - dev/conditions.h | 1 - dev/cte_moniker.h | 1 - dev/indexed_column.h | 1 - dev/member_traits/member_traits.h | 1 - dev/prepared_statement.h | 1 - dev/schema/column.h | 1 - dev/schema/index.h | 1 - dev/schema/triggers.h | 1 - dev/table_info.h | 2 -- dev/values.h | 1 - dev/xdestroy_handling.h | 1 - include/sqlite_orm/sqlite_orm.h | 26 -------------------------- 14 files changed, 40 deletions(-) diff --git a/dev/alias_traits.h b/dev/alias_traits.h index 5d64de53..4aaeee5f 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/ast/where.h b/dev/ast/where.h index 29ca5002..ebf80b1c 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/conditions.h b/dev/conditions.h index b5ee0fac..2a9d3be7 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -8,7 +8,6 @@ #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/cte_moniker.h b/dev/cte_moniker.h index 79e623ef..ebd065aa 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/indexed_column.h b/dev/indexed_column.h index 9426a00e..daa36d48 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 { diff --git a/dev/member_traits/member_traits.h b/dev/member_traits/member_traits.h index 652d58ad..eba94887 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/prepared_statement.h b/dev/prepared_statement.h index 9dbe7d0d..5e79dc20 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/schema/column.h b/dev/schema/column.h index 45c95a36..c3967dd8 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 a846e51d..63c6846e 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/triggers.h b/dev/schema/triggers.h index a090c401..5ec53e38 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/table_info.h b/dev/table_info.h index 087a3c6f..975b203e 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/values.h b/dev/values.h index 69329bcf..02d34725 100644 --- a/dev/values.h +++ b/dev/values.h @@ -4,7 +4,6 @@ #include // std::tuple #include // std::forward, std::move -#include "functional/cxx_universal.h" #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { diff --git a/dev/xdestroy_handling.h b/dev/xdestroy_handling.h index 0b4b40af..4d73977c 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/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 80161ca5..fa39bcb0 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -506,8 +506,6 @@ namespace sqlite_orm { #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 { @@ -1940,8 +1938,6 @@ namespace sqlite_orm::internal { #include #endif -// #include "functional/cxx_universal.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "type_traits.h" @@ -4491,8 +4487,6 @@ namespace sqlite_orm { #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" @@ -4618,8 +4612,6 @@ namespace sqlite_orm { #include // std::stringstream #include // std::flush -// #include "functional/cxx_universal.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "is_base_of_template.h" @@ -8191,8 +8183,6 @@ namespace sqlite_orm { #include #endif -// #include "functional/cxx_universal.h" - // #include "functional/cstring_literal.h" // #include "alias.h" @@ -8280,8 +8270,6 @@ namespace sqlite_orm { #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" @@ -9365,8 +9353,6 @@ namespace sqlite_orm { #include #endif -// #include "functional/cxx_universal.h" - // #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { @@ -11720,8 +11706,6 @@ namespace sqlite_orm { #include // std::string -// #include "functional/cxx_universal.h" - namespace sqlite_orm { struct table_info { @@ -11854,8 +11838,6 @@ namespace sqlite_orm { #include // std::string #include // std::forward -// #include "../functional/cxx_universal.h" - // #include "../tuple_helper/tuple_traits.h" // #include "../indexed_column.h" @@ -11863,8 +11845,6 @@ namespace sqlite_orm { #include // std::string #include // std::move -// #include "functional/cxx_universal.h" - // #include "ast/where.h" namespace sqlite_orm { @@ -13586,8 +13566,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" @@ -13685,8 +13663,6 @@ namespace sqlite_orm { #include // std::tuple #include // std::forward, std::move -// #include "functional/cxx_universal.h" - // #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { @@ -18986,8 +18962,6 @@ namespace sqlite_orm { #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 ? From 2927b2b6edf112cde94ccff76382081c0bc28bb6 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 20 Jul 2024 19:06:47 +0200 Subject: [PATCH 11/38] No more including of `cxx_universal.h` The `cxx_universal.h` exists to provide symbols that sqlite_orm depends on everywhere. It is easy to forget to include it. And more importantly there are decision macros used that need to be defined before certain files anyway, which is the reason that `cxx_universal.h` gets included by the files used for amalgamation. Hence I decided to not include it anymore. --- dev/column_result.h | 1 - dev/constraints.h | 1 - dev/cte_column_names_collector.h | 1 - dev/cte_storage.h | 1 - dev/expression.h | 1 - dev/field_printer.h | 1 - dev/function.h | 1 - dev/functional/cstring_literal.h | 2 - dev/functional/cxx_tuple_polyfill.h | 1 - dev/functional/cxx_type_traits_polyfill.h | 1 - dev/functional/index_sequence_util.h | 2 - dev/functional/mpl.h | 1 - dev/get_prepared_statement.h | 1 - dev/implementations/table_definitions.h | 1 - dev/mapped_iterator.h | 1 - dev/result_set_iterator.h | 1 - dev/row_extractor.h | 1 - dev/schema/table.h | 1 - dev/select_constraints.h | 1 - dev/serializing_util.h | 1 - dev/statement_binder.h | 1 - dev/statement_serializer.h | 1 - dev/storage.h | 1 - dev/storage_base.h | 1 - dev/storage_impl.h | 1 - dev/storage_lookup.h | 1 - dev/tuple_helper/tuple_filter.h | 1 - dev/tuple_helper/tuple_iteration.h | 2 - dev/tuple_helper/tuple_transformer.h | 1 - dev/values_to_tuple.h | 1 - include/sqlite_orm/sqlite_orm.h | 63 ----------------------- 31 files changed, 96 deletions(-) diff --git a/dev/column_result.h b/dev/column_result.h index 6ab44276..6bf9ad7d 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/constraints.h b/dev/constraints.h index 94df7f69..9a6a4188 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -6,7 +6,6 @@ #include // std::string #include // std::tuple -#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 8cee112e..69bb9b5e 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_storage.h b/dev/cte_storage.h index 61dca910..023e6ebe 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/expression.h b/dev/expression.h index a38faebf..1906f9a4 100644 --- a/dev/expression.h +++ b/dev/expression.h @@ -5,7 +5,6 @@ #include // std::move, std::forward, std::declval #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" // ::nullptr_t #include "functional/cxx_type_traits_polyfill.h" #include "tags.h" #include "operators.h" diff --git a/dev/field_printer.h b/dev/field_printer.h index 61ba6894..5cab1445 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" // ::nullptr_t #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 b26335ff..a0b4a29d 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 db1d8471..f78eefe9 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_tuple_polyfill.h b/dev/functional/cxx_tuple_polyfill.h index e77fe2ae..d8fee12c 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 e5c98569..31d99e44 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 cf51a23f..c0fec862 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 06cb4b09..61dd6edb 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 10848fa4..2f7d0a24 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/table_definitions.h b/dev/implementations/table_definitions.h index eb983961..c7f6177a 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -8,7 +8,6 @@ #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/mapped_iterator.h b/dev/mapped_iterator.h index 4b5d57ef..82a23ed3 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/result_set_iterator.h b/dev/result_set_iterator.h index e2430bc3..32a5f774 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 1516d56a..1234e9bd 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -18,7 +18,6 @@ #include #endif -#include "functional/cxx_universal.h" // ::nullptr_t, ::size_t #include "functional/cxx_functional_polyfill.h" #include "functional/static_magic.h" #include "tuple_helper/tuple_transformer.h" diff --git a/dev/schema/table.h b/dev/schema/table.h index 88d4fc02..aa569c0c 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" diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 74ad5cac..0de6af43 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -9,7 +9,6 @@ #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" diff --git a/dev/serializing_util.h b/dev/serializing_util.h index d768e786..407c3eb4 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -7,7 +7,6 @@ #include #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" diff --git a/dev/statement_binder.h b/dev/statement_binder.h index 23ad894c..8fb6cb6b 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -15,7 +15,6 @@ #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" diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index c1f0f2c2..ba5f2150 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -14,7 +14,6 @@ #include "functional/cxx_string_view.h" #include "functional/cxx_optional.h" -#include "functional/cxx_universal.h" // ::nullptr_t, ::size_t #include "functional/cxx_functional_polyfill.h" #include "functional/mpl.h" #include "tuple_helper/tuple_filter.h" diff --git a/dev/storage.h b/dev/storage.h index c45e5de8..c2370ec8 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -15,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 b51193f0..fc8d9683 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -15,7 +15,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" diff --git a/dev/storage_impl.h b/dev/storage_impl.h index d364daba..74e75b72 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" diff --git a/dev/storage_lookup.h b/dev/storage_lookup.h index 4992e602..a4f09752 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/tuple_helper/tuple_filter.h b/dev/tuple_helper/tuple_filter.h index 159a50b0..ce501760 100644 --- a/dev/tuple_helper/tuple_filter.h +++ b/dev/tuple_helper/tuple_filter.h @@ -3,7 +3,6 @@ #include // std::integral_constant, std::index_sequence, std::conditional, std::declval #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 afea3500..e1d7c966 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 a4333a0d..b96ba0f7 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/values_to_tuple.h b/dev/values_to_tuple.h index 76f7af3b..83c5be4c 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/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index fa39bcb0..833c3424 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -303,8 +303,6 @@ namespace sqlite_orm { #define SQLITE_ORM_OPTIONAL_SUPPORTED #endif -// #include "functional/cxx_universal.h" - // #include "functional/cxx_functional_polyfill.h" #include @@ -317,8 +315,6 @@ namespace sqlite_orm { #include -// #include "cxx_universal.h" - // #include "mpl/conditional.h" namespace sqlite_orm { @@ -783,8 +779,6 @@ namespace sqlite_orm { #include #endif -// #include "cxx_universal.h" -// ::size_t // #include "cxx_type_traits_polyfill.h" // #include "mpl/conditional.h" @@ -1364,17 +1358,12 @@ namespace sqlite_orm { #include // std::integral_constant, std::index_sequence, std::conditional, std::declval #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) @@ -1528,8 +1517,6 @@ namespace sqlite_orm { #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" @@ -1648,9 +1635,6 @@ namespace sqlite_orm { #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) @@ -1900,9 +1884,6 @@ namespace sqlite_orm { #include // std::copy_n #endif -// #include "cxx_universal.h" -// ::size_t - #ifdef SQLITE_ORM_WITH_CPP20_ALIASES namespace sqlite_orm::internal { /* @@ -3134,8 +3115,6 @@ namespace sqlite_orm { #include // std::string #include // std::tuple -// #include "functional/cxx_universal.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "functional/mpl.h" @@ -3851,8 +3830,6 @@ namespace sqlite_orm { #endif // #include "functional/cxx_optional.h" -// #include "functional/cxx_universal.h" -// ::nullptr_t // #include "functional/cxx_type_traits_polyfill.h" // #include "is_std_ptr.h" @@ -4436,8 +4413,6 @@ namespace sqlite_orm { #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" @@ -4678,8 +4653,6 @@ namespace sqlite_orm { #include // std::move, std::forward, std::declval // #include "functional/cxx_optional.h" -// #include "functional/cxx_universal.h" -// ::nullptr_t // #include "functional/cxx_type_traits_polyfill.h" // #include "tags.h" @@ -9308,8 +9281,6 @@ namespace sqlite_orm { #include // std::codecvt_utf8_utf16 #endif -// #include "functional/cxx_universal.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "functional/cxx_functional_polyfill.h" @@ -10244,8 +10215,6 @@ namespace sqlite_orm { #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" @@ -10453,8 +10422,6 @@ namespace sqlite_orm { #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" @@ -10663,8 +10630,6 @@ namespace sqlite_orm { #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" @@ -11757,8 +11722,6 @@ namespace sqlite_orm { #include // std::string -// #include "functional/cxx_universal.h" -// ::size_t // #include "functional/static_magic.h" // #include "functional/index_sequence_util.h" @@ -11785,8 +11748,6 @@ namespace sqlite_orm { #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" @@ -12639,8 +12600,6 @@ namespace sqlite_orm { #include #endif -// #include "functional/cxx_universal.h" -// ::nullptr_t, ::size_t // #include "functional/cxx_functional_polyfill.h" // #include "functional/static_magic.h" @@ -13192,8 +13151,6 @@ namespace sqlite_orm { #include // std::system_error #include // std::bind -// #include "functional/cxx_universal.h" -// ::ptrdiff_t // #include "statement_finalizer.h" #include @@ -15669,8 +15626,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" @@ -15847,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 @@ -15856,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 @@ -15917,8 +15868,6 @@ namespace sqlite_orm { #include #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" @@ -16888,8 +16837,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" @@ -18448,8 +18395,6 @@ namespace sqlite_orm { // #include "functional/cxx_optional.h" -// #include "functional/cxx_universal.h" -// ::nullptr_t, ::size_t // #include "functional/cxx_functional_polyfill.h" // #include "functional/mpl.h" @@ -18653,8 +18598,6 @@ namespace sqlite_orm { #include #endif -// #include "functional/cxx_universal.h" - // #include "functional/cxx_type_traits_polyfill.h" // #include "type_traits.h" @@ -21531,8 +21474,6 @@ namespace sqlite_orm { #include #endif -// #include "functional/cxx_universal.h" -// ::size_t // #include "tuple_helper/tuple_fy.h" // #include "table_type_of.h" @@ -23706,8 +23647,6 @@ namespace sqlite_orm { #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" @@ -24004,8 +23943,6 @@ namespace sqlite_orm { #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" From c08b5c8349e582bc248e4d37e43f6306a69da62e Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 20 Jul 2024 19:13:15 +0200 Subject: [PATCH 12/38] Code formatting of file `dev/ast/match.h` --- dev/ast/match.h | 3 ++- include/sqlite_orm/sqlite_orm.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dev/ast/match.h b/dev/ast/match.h index 57b499fa..b2a65c70 100644 --- a/dev/ast/match.h +++ b/dev/ast/match.h @@ -1,5 +1,6 @@ #pragma once -#include + +#include // std::move namespace sqlite_orm { namespace internal { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 833c3424..fb487380 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -14820,7 +14820,7 @@ namespace sqlite_orm { // #include "ast/match.h" -#include +#include // std::move namespace sqlite_orm { namespace internal { From faa34960c0c26bab15fad8cc634ab861be95f84f Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 20 Jul 2024 19:24:00 +0200 Subject: [PATCH 13/38] Used C string and stdlib functions again --- dev/functional/cxx_universal.h | 1 + dev/pragma.h | 15 ++++++------ dev/row_extractor.h | 12 ++++----- dev/statement_binder.h | 10 ++++---- dev/statement_serializer.h | 2 +- dev/storage_base.h | 3 ++- include/sqlite_orm/sqlite_orm.h | 43 ++++++++++++++++++--------------- 7 files changed, 46 insertions(+), 40 deletions(-) diff --git a/dev/functional/cxx_universal.h b/dev/functional/cxx_universal.h index dd2b4dc2..d1e54fc0 100644 --- a/dev/functional/cxx_universal.h +++ b/dev/functional/cxx_universal.h @@ -6,6 +6,7 @@ * - ::size_t, ::ptrdiff_t, ::nullptr_t * - C++ core language feature macros * - macros for dealing with compiler quirks + * - macros for exporting symbols from the C++ named module */ #include // alternative operator representations diff --git a/dev/pragma.h b/dev/pragma.h index 53c798d9..8f9ab7d3 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -1,6 +1,7 @@ #pragma once #include +#include // atoi #include // std::string #include // std::function #include // std::shared_ptr @@ -149,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), @@ -186,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/row_extractor.h b/dev/row_extractor.h index 1234e9bd..e18598af 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -2,8 +2,8 @@ #include #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if -#include // std::atof, std::atoi, std::atoll -#include // std::strlen +#include // atof, atoi, atoll +#include // strlen #include // std::system_error #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT @@ -174,7 +174,7 @@ namespace sqlite_orm { using tag = arithmetic_tag_t; V extract(const char* columnText, const int_or_smaller_tag&) const { - return static_cast(std::atoi(columnText)); + return static_cast(atoi(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { @@ -186,7 +186,7 @@ namespace sqlite_orm { } V extract(const char* columnText, const bigint_tag&) const { - return static_cast(std::atoll(columnText)); + return static_cast(atoll(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { @@ -198,7 +198,7 @@ namespace sqlite_orm { } V extract(const char* columnText, const real_tag&) const { - return static_cast(std::atof(columnText)); + return static_cast(atof(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { @@ -388,7 +388,7 @@ namespace sqlite_orm { template<> struct row_extractor, void> { std::vector extract(const char* columnText) const { - return {columnText, columnText + (columnText ? std::strlen(columnText) : 0)}; + return {columnText, columnText + (columnText ? strlen(columnText) : 0)}; } std::vector extract(sqlite3_stmt* stmt, int columnIndex) const { diff --git a/dev/statement_binder.h b/dev/statement_binder.h index 8fb6cb6b..a3a979c4 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -5,10 +5,10 @@ #include // std::default_delete #include // std::string, std::wstring #include // std::vector -#include // std::strncpy, std::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 @@ -139,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{}; - std::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)); } @@ -154,7 +154,7 @@ namespace sqlite_orm { } std::pair string_data(const char* s) const { - return {s, int(std::strlen(s))}; + return {s, int(strlen(s))}; } #endif }; @@ -193,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 ba5f2150..2131c127 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 diff --git a/dev/storage_base.h b/dev/storage_base.h index fc8d9683..187824aa 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -1,6 +1,7 @@ #pragma once #include +#include // atoi #include // std::allocator #include // std::function, std::bind, std::bind_front #include // std::string @@ -169,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/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index fb487380..02a83ea8 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -19,6 +19,7 @@ __pragma(push_macro("max")) * - ::size_t, ::ptrdiff_t, ::nullptr_t * - C++ core language feature macros * - macros for dealing with compiler quirks + * - macros for exporting symbols from the C++ named module */ #include // alternative operator representations @@ -9270,11 +9271,11 @@ namespace sqlite_orm { #include // std::default_delete #include // std::string, std::wstring #include // std::vector -#include // std::strncpy, std::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 @@ -9981,7 +9982,7 @@ namespace sqlite_orm { auto stringData = this->string_data(value); auto dataCopy = new char[stringData.second + 1]; constexpr auto deleter = std::default_delete{}; - std::strncpy(dataCopy, stringData.first, stringData.second + 1); + strncpy(dataCopy, stringData.first, stringData.second + 1); sqlite3_result_text(context, dataCopy, stringData.second, obtain_xdestroy_for(deleter, dataCopy)); } @@ -9996,7 +9997,7 @@ namespace sqlite_orm { } std::pair string_data(const char* s) const { - return {s, int(std::strlen(s))}; + return {s, int(strlen(s))}; } #endif }; @@ -10035,7 +10036,7 @@ namespace sqlite_orm { } std::pair string_data(const wchar_t* s) const { - return {s, int(::wcslen(s))}; + return {s, int(wcslen(s))}; } #endif }; @@ -12584,8 +12585,8 @@ namespace sqlite_orm { #include #include // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if -#include // std::atof, std::atoi, std::atoll -#include // std::strlen +#include // atof, atoi, atoll +#include // strlen #include // std::system_error #include // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT @@ -12765,7 +12766,7 @@ namespace sqlite_orm { using tag = arithmetic_tag_t; V extract(const char* columnText, const int_or_smaller_tag&) const { - return static_cast(std::atoi(columnText)); + return static_cast(atoi(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const int_or_smaller_tag&) const { @@ -12777,7 +12778,7 @@ namespace sqlite_orm { } V extract(const char* columnText, const bigint_tag&) const { - return static_cast(std::atoll(columnText)); + return static_cast(atoll(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const bigint_tag&) const { @@ -12789,7 +12790,7 @@ namespace sqlite_orm { } V extract(const char* columnText, const real_tag&) const { - return static_cast(std::atof(columnText)); + return static_cast(atof(columnText)); } V extract(sqlite3_stmt* stmt, int columnIndex, const real_tag&) const { @@ -12979,7 +12980,7 @@ namespace sqlite_orm { template<> struct row_extractor, void> { std::vector extract(const char* columnText) const { - return {columnText, columnText + (columnText ? std::strlen(columnText) : 0)}; + return {columnText, columnText + (columnText ? strlen(columnText) : 0)}; } std::vector extract(sqlite3_stmt* stmt, int columnIndex) const { @@ -15788,6 +15789,7 @@ inline constexpr bool std::ranges::enable_borrowed_range +#include // atoi #include // std::allocator #include // std::function, std::bind, std::bind_front #include // std::string @@ -15842,6 +15844,7 @@ namespace sqlite_orm { // #include "pragma.h" #include +#include // atoi #include // std::string #include // std::function #include // std::shared_ptr @@ -16434,14 +16437,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), @@ -16471,13 +16474,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; @@ -17402,7 +17405,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; }, @@ -18380,9 +18383,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 From fb7f62b896183f756aa61645f476576c3e7a1050 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 20 Jul 2024 20:18:10 +0200 Subject: [PATCH 14/38] Removed irrelevant comment in `cxx_universal.h` --- dev/functional/cxx_universal.h | 1 - include/sqlite_orm/sqlite_orm.h | 1 - 2 files changed, 2 deletions(-) diff --git a/dev/functional/cxx_universal.h b/dev/functional/cxx_universal.h index d1e54fc0..dd2b4dc2 100644 --- a/dev/functional/cxx_universal.h +++ b/dev/functional/cxx_universal.h @@ -6,7 +6,6 @@ * - ::size_t, ::ptrdiff_t, ::nullptr_t * - C++ core language feature macros * - macros for dealing with compiler quirks - * - macros for exporting symbols from the C++ named module */ #include // alternative operator representations diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 02a83ea8..13980294 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -19,7 +19,6 @@ __pragma(push_macro("max")) * - ::size_t, ::ptrdiff_t, ::nullptr_t * - C++ core language feature macros * - macros for dealing with compiler quirks - * - macros for exporting symbols from the C++ named module */ #include // alternative operator representations From c059cbdc6e5fea1c418e94eafcae33813543f4e7 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 21 Jul 2024 09:03:06 +0200 Subject: [PATCH 15/38] appveyor: Updated vcpkg environment to 2024.07.12 --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ffc1cc5f..473f66d6 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 From 5da3ba3cc83b19ce85354950e4669de125d48f47 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 21 Jul 2024 09:09:20 +0200 Subject: [PATCH 16/38] Corrected messed up #if/#endif pair in `table_reference.h` --- dev/table_reference.h | 1 - include/sqlite_orm/sqlite_orm.h | 1 - 2 files changed, 2 deletions(-) diff --git a/dev/table_reference.h b/dev/table_reference.h index c54f6d3b..0a6aba49 100644 --- a/dev/table_reference.h +++ b/dev/table_reference.h @@ -1,7 +1,6 @@ #pragma once #include // std::remove_const, std::type_identity -#endif #include "functional/cxx_type_traits_polyfill.h" diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 13980294..84f58b6d 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1926,7 +1926,6 @@ namespace sqlite_orm::internal { // #include "table_reference.h" #include // std::remove_const, std::type_identity -#endif // #include "functional/cxx_type_traits_polyfill.h" From bbf93f0cbe2b3eb0ed53f340b9d3749305fbd7cc Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 21 Jul 2024 09:22:56 +0200 Subject: [PATCH 17/38] Added feature to use a CTE in a `REPLACE` statement --- dev/storage.h | 7 +++++-- include/sqlite_orm/sqlite_orm.h | 7 +++++-- tests/storage_tests.cpp | 6 +++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index c2370ec8..c18e8f18 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1253,7 +1253,8 @@ namespace sqlite_orm { #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) template, is_insert_raw>, bool> = true> + std::enable_if_t, is_insert_raw, is_replace_raw>, + bool> = true> prepared_statement_t> prepare(with_t sel) { return this->prepare_impl>(std::move(sel)); } @@ -1380,7 +1381,9 @@ namespace sqlite_orm { } #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template = true> + template, is_replace_raw>, bool> = true> void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 84f58b6d..1a224276 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -23112,7 +23112,8 @@ namespace sqlite_orm { #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) template, is_insert_raw>, bool> = true> + std::enable_if_t, is_insert_raw, is_replace_raw>, + bool> = true> prepared_statement_t> prepare(with_t sel) { return this->prepare_impl>(std::move(sel)); } @@ -23239,7 +23240,9 @@ namespace sqlite_orm { } #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template = true> + template, is_replace_raw>, bool> = true> void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index e9e2244a..6ae45ce6 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -583,7 +583,7 @@ TEST_CASE("With clause") { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - SECTION("insert") { + SECTION("crud") { struct Object { int id; }; @@ -596,6 +596,10 @@ TEST_CASE("With clause") { storage.with(cte().as(select(2)), insert(into(), columns(&Object::id), select(data->*1_colalias))); REQUIRE(2 == storage.last_insert_rowid()); + + storage.with(cte().as(select(2)), + replace(into(), columns(&Object::id), select(data->*1_colalias))); + REQUIRE(storage.changes() == 1); } #endif } From d5980a49817ae911edf11d2efc7c73fc71176c16 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 21 Jul 2024 18:51:24 +0200 Subject: [PATCH 18/38] CTE for raw replace, update all, remove all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also: When collecting table names from column assignments that are part of an “update all” expression, we are only interested in the table name on the left-hand side of the assignment operator expression. --- dev/ast/set.h | 3 ++- dev/prepared_statement.h | 12 +++++++++++ dev/statement_serializer.h | 8 ++++--- dev/storage.h | 15 +++++++++---- include/sqlite_orm/sqlite_orm.h | 38 ++++++++++++++++++++++++++------- tests/storage_tests.cpp | 21 +++++++++++++----- 6 files changed, 76 insertions(+), 21 deletions(-) diff --git a/dev/ast/set.h b/dev/ast/set.h index e5e66f2e..b997e4c5 100644 --- a/dev/ast/set.h +++ b/dev/ast/set.h @@ -50,7 +50,8 @@ namespace sqlite_orm { void push_back(assign_t assign) { auto newContext = this->context; newContext.skip_table_name = true; - iterate_ast(assign, this->collector); + // note: we are only interested in the table name on the left-hand side of the assignment operator expression + iterate_ast(assign.lhs, this->collector); std::stringstream ss; ss << serialize(assign.lhs, newContext) << ' ' << assign.serialize() << ' ' << serialize(assign.rhs, context); diff --git a/dev/prepared_statement.h b/dev/prepared_statement.h index 5e79dc20..5b7ebb69 100644 --- a/dev/prepared_statement.h +++ b/dev/prepared_statement.h @@ -142,6 +142,12 @@ namespace sqlite_orm { conditions_type conditions; }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_update_all_v = polyfill::is_specialization_of::value; + + template + using is_update_all = polyfill::bool_constant>; + template struct remove_all_t { using type = T; @@ -150,6 +156,12 @@ namespace sqlite_orm { conditions_type conditions; }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_remove_all_v = polyfill::is_specialization_of::value; + + template + using is_remove_all = polyfill::bool_constant>; + template struct get_t { using type = T; diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 2131c127..fd3da122 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -1362,7 +1362,10 @@ namespace sqlite_orm { template std::set> collect_table_names(const set_t& set, const Ctx& ctx) { auto collector = make_table_name_collector(ctx.db_objects); - iterate_ast(set, collector); + // note: we are only interested in the table name on the left-hand side of the assignment operator expression + iterate_tuple(set.assigns, [&collector](const auto& assignmentOperator) { + iterate_ast(assignmentOperator.lhs, collector); + }); return std::move(collector.table_names); } @@ -1375,8 +1378,7 @@ namespace sqlite_orm { template = true> std::set> collect_table_names(const T& sel, const Ctx& ctx) { auto collector = make_table_name_collector(ctx.db_objects); - iterate_ast(sel.col, collector); - iterate_ast(sel.conditions, collector); + iterate_ast(sel, collector); return std::move(collector.table_names); } diff --git a/dev/storage.h b/dev/storage.h index c18e8f18..7b0d9d7d 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1253,7 +1253,11 @@ namespace sqlite_orm { #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) template, is_insert_raw, is_replace_raw>, + std::enable_if_t, + is_insert_raw, + is_replace_raw, + is_update_all, + is_remove_all>, bool> = true> prepared_statement_t> prepare(with_t sel) { return this->prepare_impl>(std::move(sel)); @@ -1381,9 +1385,12 @@ namespace sqlite_orm { } #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template, is_replace_raw>, bool> = true> + template< + class... CTEs, + class E, + std::enable_if_t< + polyfill::disjunction_v, is_replace_raw, is_update_all, is_remove_all>, + bool> = true> void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 1a224276..fb65cd9d 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13895,7 +13895,8 @@ namespace sqlite_orm { void push_back(assign_t assign) { auto newContext = this->context; newContext.skip_table_name = true; - iterate_ast(assign, this->collector); + // note: we are only interested in the table name on the left-hand side of the assignment operator expression + iterate_ast(assign.lhs, this->collector); std::stringstream ss; ss << serialize(assign.lhs, newContext) << ' ' << assign.serialize() << ' ' << serialize(assign.rhs, context); @@ -14076,6 +14077,12 @@ namespace sqlite_orm { conditions_type conditions; }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_update_all_v = polyfill::is_specialization_of::value; + + template + using is_update_all = polyfill::bool_constant>; + template struct remove_all_t { using type = T; @@ -14084,6 +14091,12 @@ namespace sqlite_orm { conditions_type conditions; }; + template + SQLITE_ORM_INLINE_VAR constexpr bool is_remove_all_v = polyfill::is_specialization_of::value; + + template + using is_remove_all = polyfill::bool_constant>; + template struct get_t { using type = T; @@ -20505,7 +20518,10 @@ namespace sqlite_orm { template std::set> collect_table_names(const set_t& set, const Ctx& ctx) { auto collector = make_table_name_collector(ctx.db_objects); - iterate_ast(set, collector); + // note: we are only interested in the table name on the left-hand side of the assignment operator expression + iterate_tuple(set.assigns, [&collector](const auto& assignmentOperator) { + iterate_ast(assignmentOperator.lhs, collector); + }); return std::move(collector.table_names); } @@ -20518,8 +20534,7 @@ namespace sqlite_orm { template = true> std::set> collect_table_names(const T& sel, const Ctx& ctx) { auto collector = make_table_name_collector(ctx.db_objects); - iterate_ast(sel.col, collector); - iterate_ast(sel.conditions, collector); + iterate_ast(sel, collector); return std::move(collector.table_names); } @@ -23112,7 +23127,11 @@ namespace sqlite_orm { #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) template, is_insert_raw, is_replace_raw>, + std::enable_if_t, + is_insert_raw, + is_replace_raw, + is_update_all, + is_remove_all>, bool> = true> prepared_statement_t> prepare(with_t sel) { return this->prepare_impl>(std::move(sel)); @@ -23240,9 +23259,12 @@ namespace sqlite_orm { } #if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template, is_replace_raw>, bool> = true> + template< + class... CTEs, + class E, + std::enable_if_t< + polyfill::disjunction_v, is_replace_raw, is_update_all, is_remove_all>, + bool> = true> void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index 6ae45ce6..37d32398 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -592,14 +592,25 @@ TEST_CASE("With clause") { storage.sync_schema(); - constexpr auto data = "data"_cte; - storage.with(cte().as(select(2)), + constexpr orm_cte_moniker auto data = "data"_cte; + storage.with(cte().as(union_all(select(2), select(3))), insert(into(), columns(&Object::id), select(data->*1_colalias))); - REQUIRE(2 == storage.last_insert_rowid()); + REQUIRE(3 == storage.last_insert_rowid()); - storage.with(cte().as(select(2)), + storage.with(cte().as(union_all(select(2), select(3))), replace(into(), columns(&Object::id), select(data->*1_colalias))); - REQUIRE(storage.changes() == 1); + REQUIRE(storage.changes() == 2); + + storage.with( + cte().as(union_all(select(2), select(3))), + update_all( + set(c(&Object::id) = select(data->*1_colalias, from(), where(data->*1_colalias == &Object::id))), + where(c(&Object::id).in(select(data->*1_colalias))))); + REQUIRE(storage.changes() == 2); + + storage.with(cte().as(union_all(select(2), select(3))), + remove_all(where(c(&Object::id).in(select(data->*1_colalias))))); + REQUIRE(storage.changes() == 2); } #endif } From cff4a64d8cc40c88010803241888f8d478a92aba Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 21 Jul 2024 21:48:13 +0200 Subject: [PATCH 19/38] More `constexpr` factory functions ... ... operators, asterisk, object, where, select, compound, cte. This makes it possible to factor out common expressions now. --- dev/ast/where.h | 4 +- dev/conditions.h | 56 +++++------ dev/core_functions.h | 10 +- dev/cte_moniker.h | 4 +- dev/expression.h | 6 +- dev/operators.h | 24 ++--- dev/select_constraints.h | 44 +++++---- dev/tuple_helper/tuple_iteration.h | 4 +- include/sqlite_orm/sqlite_orm.h | 152 +++++++++++++++-------------- 9 files changed, 154 insertions(+), 150 deletions(-) diff --git a/dev/ast/where.h b/dev/ast/where.h index ebf80b1c..c18d936f 100644 --- a/dev/ast/where.h +++ b/dev/ast/where.h @@ -26,7 +26,7 @@ namespace sqlite_orm { expression_type expression; - where_t(expression_type expression_) : expression(std::move(expression_)) {} + constexpr where_t(expression_type expression_) : expression(std::move(expression_)) {} }; template @@ -46,7 +46,7 @@ namespace sqlite_orm { * auto rows = storage.select(&Letter::name, where(greater_than(&Letter::id, 3))); */ template - internal::where_t where(C expression) { + constexpr internal::where_t where(C expression) { return {std::move(expression)}; } } diff --git a/dev/conditions.h b/dev/conditions.h index 2a9d3be7..d7ac8b93 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -113,7 +113,7 @@ namespace sqlite_orm { struct negated_condition_t : condition_t, negated_condition_string { C c; - negated_condition_t(C c_) : c(std::move(c_)) {} + constexpr negated_condition_t(C c_) : c(std::move(c_)) {} }; /** @@ -132,9 +132,9 @@ namespace sqlite_orm { left_type lhs; right_type rhs; - binary_condition() = default; + constexpr binary_condition() = default; - binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} + constexpr binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} }; template @@ -849,7 +849,7 @@ namespace sqlite_orm { class T, std::enable_if_t, is_operator_argument>::value, bool> = true> - negated_condition_t operator!(T arg) { + constexpr negated_condition_t operator!(T arg) { return {std::move(arg)}; } @@ -860,7 +860,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - less_than_t, unwrap_expression_t> operator<(L l, R r) { + constexpr less_than_t, unwrap_expression_t> operator<(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -871,7 +871,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - less_or_equal_t, unwrap_expression_t> operator<=(L l, R r) { + constexpr 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))}; } @@ -882,7 +882,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - greater_than_t, unwrap_expression_t> operator>(L l, R r) { + constexpr greater_than_t, unwrap_expression_t> operator>(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -893,7 +893,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - greater_or_equal_t, unwrap_expression_t> operator>=(L l, R r) { + constexpr 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))}; } @@ -906,7 +906,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - is_equal_t, unwrap_expression_t> operator==(L l, R r) { + constexpr is_equal_t, unwrap_expression_t> operator==(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -919,7 +919,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - is_not_equal_t, unwrap_expression_t> operator!=(L l, R r) { + constexpr 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))}; } @@ -930,7 +930,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - and_condition_t, unwrap_expression_t> operator&&(L l, R r) { + constexpr and_condition_t, unwrap_expression_t> operator&&(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -939,7 +939,7 @@ namespace sqlite_orm { std::enable_if_t< polyfill::disjunction, std::is_base_of>::value, bool> = true> - or_condition_t, unwrap_expression_t> operator||(L l, R r) { + constexpr or_condition_t, unwrap_expression_t> operator||(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -955,7 +955,7 @@ namespace sqlite_orm { polyfill::negation, std::is_base_of>>>::value, bool> = true> - conc_t, unwrap_expression_t> operator||(L l, R r) { + constexpr conc_t, unwrap_expression_t> operator||(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } } @@ -1053,14 +1053,14 @@ namespace sqlite_orm { } template - auto and_(L l, R r) { + constexpr 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))}; } template - auto or_(L l, R r) { + constexpr 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))}; @@ -1107,52 +1107,52 @@ namespace sqlite_orm { } template - internal::is_equal_t is_equal(L l, R r) { + constexpr internal::is_equal_t is_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_equal_t eq(L l, R r) { + constexpr internal::is_equal_t eq(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_equal_with_table_t is_equal(R rhs) { + constexpr internal::is_equal_with_table_t is_equal(R rhs) { return {std::move(rhs)}; } template - internal::is_not_equal_t is_not_equal(L l, R r) { + constexpr internal::is_not_equal_t is_not_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_not_equal_t ne(L l, R r) { + constexpr internal::is_not_equal_t ne(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_than_t greater_than(L l, R r) { + constexpr internal::greater_than_t greater_than(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_than_t gt(L l, R r) { + constexpr internal::greater_than_t gt(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_or_equal_t greater_or_equal(L l, R r) { + constexpr internal::greater_or_equal_t greater_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_or_equal_t ge(L l, R r) { + constexpr internal::greater_or_equal_t ge(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::less_than_t less_than(L l, R r) { + constexpr internal::less_than_t less_than(L l, R r) { return {std::move(l), std::move(r)}; } @@ -1166,12 +1166,12 @@ namespace sqlite_orm { } template - internal::less_than_t lt(L l, R r) { + constexpr internal::less_than_t lt(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::less_or_equal_t less_or_equal(L l, R r) { + constexpr internal::less_or_equal_t less_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } @@ -1185,7 +1185,7 @@ namespace sqlite_orm { } template - internal::less_or_equal_t le(L l, R r) { + constexpr internal::less_or_equal_t le(L l, R r) { return {std::move(l), std::move(r)}; } diff --git a/dev/core_functions.h b/dev/core_functions.h index 4f4c8bd8..bed10999 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -2048,7 +2048,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - add_t, unwrap_expression_t> operator+(L l, R r) { + constexpr add_t, unwrap_expression_t> operator+(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -2059,7 +2059,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - sub_t, unwrap_expression_t> operator-(L l, R r) { + constexpr sub_t, unwrap_expression_t> operator-(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -2070,7 +2070,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - mul_t, unwrap_expression_t> operator*(L l, R r) { + constexpr mul_t, unwrap_expression_t> operator*(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -2081,7 +2081,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - div_t, unwrap_expression_t> operator/(L l, R r) { + constexpr div_t, unwrap_expression_t> operator/(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -2092,7 +2092,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - mod_t, unwrap_expression_t> operator%(L l, R r) { + constexpr mod_t, unwrap_expression_t> operator%(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } } diff --git a/dev/cte_moniker.h b/dev/cte_moniker.h index ebd065aa..f2358462 100644 --- a/dev/cte_moniker.h +++ b/dev/cte_moniker.h @@ -50,7 +50,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto operator()(ExplicitCols... explicitColumns) const; + constexpr auto operator()(ExplicitCols... explicitColumns) const; #else template>, std::is_convertible>...>, bool> = true> - auto operator()(ExplicitCols... explicitColumns) const; + constexpr auto operator()(ExplicitCols... explicitColumns) const; #endif }; } diff --git a/dev/expression.h b/dev/expression.h index 1906f9a4..d40b4c63 100644 --- a/dev/expression.h +++ b/dev/expression.h @@ -68,12 +68,12 @@ namespace sqlite_orm { is_operator_argument_v::value>> = true; template - T get_from_expression(T value) { + constexpr T get_from_expression(T value) { return std::move(value); } template - T get_from_expression(expression_t expression) { + constexpr T get_from_expression(expression_t expression) { return std::move(expression.value); } @@ -86,7 +86,7 @@ namespace sqlite_orm { * `storage.update(set(c(&User::name) = "Dua Lipa")); */ template - internal::expression_t c(T value) { + constexpr internal::expression_t c(T value) { return {std::move(value)}; } } diff --git a/dev/operators.h b/dev/operators.h index 180747db..85d0e349 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -20,7 +20,7 @@ namespace sqlite_orm { left_type lhs; right_type rhs; - binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} + constexpr binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} }; template @@ -197,7 +197,7 @@ namespace sqlite_orm { * name || '@gmail.com' FROM users */ template - internal::conc_t conc(L l, R r) { + constexpr internal::conc_t conc(L l, R r) { return {std::move(l), std::move(r)}; } @@ -205,7 +205,7 @@ namespace sqlite_orm { * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users */ template - internal::add_t add(L l, R r) { + constexpr internal::add_t add(L l, R r) { return {std::move(l), std::move(r)}; } @@ -213,7 +213,7 @@ namespace sqlite_orm { * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users */ template - internal::sub_t sub(L l, R r) { + constexpr internal::sub_t sub(L l, R r) { return {std::move(l), std::move(r)}; } @@ -221,7 +221,7 @@ namespace sqlite_orm { * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users */ template - internal::mul_t mul(L l, R r) { + constexpr internal::mul_t mul(L l, R r) { return {std::move(l), std::move(r)}; } @@ -231,7 +231,7 @@ namespace sqlite_orm { * 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) { + constexpr internal::div_t div(L l, R r) { return {std::move(l), std::move(r)}; } @@ -239,32 +239,32 @@ namespace sqlite_orm { * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users */ template - internal::mod_t mod(L l, R r) { + constexpr 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) { + constexpr 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) { + constexpr 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) { + constexpr 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) { + constexpr 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) { + constexpr internal::bitwise_not_t bitwise_not(T t) { return {std::move(t)}; } diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 0de6af43..e159bef5 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -146,7 +146,7 @@ namespace sqlite_orm { expressions_tuple compound; - compound_operator(expressions_tuple compound) : compound{std::move(compound)} { + constexpr compound_operator(expressions_tuple compound) : compound{std::move(compound)} { iterate_tuple(this->compound, [](auto& expression) { expression.highest_level = true; }); @@ -182,7 +182,7 @@ namespace sqlite_orm { struct union_t : public compound_operator, union_base { using typename compound_operator::expressions_tuple; - union_t(expressions_tuple compound, bool all) : + constexpr union_t(expressions_tuple compound, bool all) : compound_operator{std::move(compound)}, union_base{all} {} }; @@ -263,7 +263,7 @@ namespace sqlite_orm { explicit_colrefs_tuple explicitColumns; expression_type subselect; - common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : + constexpr common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : explicitColumns{std::move(explicitColumns)}, subselect{std::move(subselect)} { this->subselect.highest_level = true; } @@ -278,23 +278,25 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) template = true> - common_table_expression, Select> as(Select sel) && { + constexpr common_table_expression, Select> + as(Select sel) && { return {std::move(this->explicitColumns), std::move(sel)}; } template = true> - common_table_expression, select_t> + constexpr common_table_expression, select_t> as(Compound sel) && { return {std::move(this->explicitColumns), {std::move(sel)}}; } #else template = true> - common_table_expression, Select> as(Select sel) && { + constexpr common_table_expression, Select> as(Select sel) && { return {std::move(this->explicitColumns), std::move(sel)}; } template = true> - common_table_expression, select_t> as(Compound sel) && { + constexpr common_table_expression, select_t> + as(Compound sel) && { return {std::move(this->explicitColumns), {std::move(sel)}}; } #endif @@ -416,7 +418,7 @@ namespace sqlite_orm { }; template - void validate_conditions() { + constexpr void validate_conditions() { static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); @@ -483,7 +485,7 @@ namespace sqlite_orm { * Public function for subselect query. Is useful in UNION queries. */ template - internal::select_t select(T t, Args... args) { + constexpr internal::select_t select(T t, Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); return {std::move(t), {std::forward(args)...}}; @@ -495,7 +497,7 @@ namespace sqlite_orm { * Look through example in examples/union.cpp */ template - internal::union_t union_(E... expressions) { + constexpr internal::union_t union_(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, false}; } @@ -506,7 +508,7 @@ namespace sqlite_orm { * Look through example in examples/union.cpp */ template - internal::union_t union_all(E... expressions) { + constexpr internal::union_t union_all(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, true}; } @@ -517,13 +519,13 @@ namespace sqlite_orm { * Look through example in examples/except.cpp */ template - internal::except_t except(E... expressions) { + constexpr internal::except_t except(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } template - internal::intersect_t intersect(E... expressions) { + constexpr internal::intersect_t intersect(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } @@ -577,7 +579,7 @@ namespace sqlite_orm { std::is_same>, std::is_convertible>...>, bool> = true> - auto cte(ExplicitCols... explicitColumns) { + constexpr auto cte(ExplicitCols... explicitColumns) { using namespace ::sqlite_orm::internal; static_assert(is_cte_moniker_v, "Moniker must be a CTE moniker"); static_assert((!is_builtin_numeric_column_alias_v && ...), @@ -595,7 +597,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto cte(ExplicitCols... explicitColumns) { + constexpr auto cte(ExplicitCols... explicitColumns) { using namespace ::sqlite_orm::internal; static_assert((!is_builtin_numeric_column_alias_v && ...), "Numeric column aliases are reserved for referencing columns locally within a single CTE."); @@ -614,7 +616,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + constexpr auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { return cte>(std::forward(explicitColumns)...); } #else @@ -626,7 +628,7 @@ namespace sqlite_orm { std::is_same>, std::is_convertible>...>, bool>> - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + constexpr auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { return cte>(std::forward(explicitColumns)...); } #endif @@ -763,7 +765,7 @@ namespace sqlite_orm { * If you need to fetch results as objects instead of tuples please use `object()`. */ template - internal::asterisk_t asterisk(bool definedOrder = false) { + constexpr internal::asterisk_t asterisk(bool definedOrder = false) { return {definedOrder}; } @@ -775,7 +777,7 @@ namespace sqlite_orm { * storage.select(asterisk(), inner_join(on(m->*&Employee::reportsTo == &Employee::employeeId))); */ template - auto asterisk(bool definedOrder = false) { + constexpr auto asterisk(bool definedOrder = false) { return asterisk>(definedOrder); } #endif @@ -792,13 +794,13 @@ namespace sqlite_orm { * If you need to fetch results as tuples instead of objects please use `asterisk()`. */ template - internal::object_t object(bool definedOrder = false) { + constexpr internal::object_t object(bool definedOrder = false) { return {definedOrder}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template - auto object(bool definedOrder = false) { + constexpr auto object(bool definedOrder = false) { return object>(definedOrder); } #endif diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index e1d7c966..e696cc9a 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -8,7 +8,7 @@ namespace sqlite_orm { namespace internal { #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) template - void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { + constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr(reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= int sink; @@ -34,7 +34,7 @@ namespace sqlite_orm { } #endif template - void iterate_tuple(Tpl&& tpl, L&& lambda) { + constexpr void iterate_tuple(Tpl&& tpl, L&& lambda) { iterate_tuple(tpl, std::make_index_sequence>::value>{}, std::forward(lambda)); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index fb65cd9d..07207af3 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1639,7 +1639,7 @@ 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) { + constexpr void iterate_tuple(Tpl& tpl, std::index_sequence, L&& lambda) { if constexpr(reversed) { // nifty fold expression trick: make use of guaranteed right-to-left evaluation order when folding over operator= int sink; @@ -1665,7 +1665,7 @@ namespace sqlite_orm { } #endif template - void iterate_tuple(Tpl&& tpl, L&& lambda) { + constexpr void iterate_tuple(Tpl&& tpl, L&& lambda) { iterate_tuple(tpl, std::make_index_sequence>::value>{}, std::forward(lambda)); @@ -4147,7 +4147,7 @@ namespace sqlite_orm { left_type lhs; right_type rhs; - binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} + constexpr binary_operator(left_type lhs_, right_type rhs_) : lhs(std::move(lhs_)), rhs(std::move(rhs_)) {} }; template @@ -4324,7 +4324,7 @@ namespace sqlite_orm { * name || '@gmail.com' FROM users */ template - internal::conc_t conc(L l, R r) { + constexpr internal::conc_t conc(L l, R r) { return {std::move(l), std::move(r)}; } @@ -4332,7 +4332,7 @@ namespace sqlite_orm { * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users */ template - internal::add_t add(L l, R r) { + constexpr internal::add_t add(L l, R r) { return {std::move(l), std::move(r)}; } @@ -4340,7 +4340,7 @@ namespace sqlite_orm { * Public interface for - operator. Example: `select(sub(&User::age, 1));` => SELECT age - 1 FROM users */ template - internal::sub_t sub(L l, R r) { + constexpr internal::sub_t sub(L l, R r) { return {std::move(l), std::move(r)}; } @@ -4348,7 +4348,7 @@ namespace sqlite_orm { * Public interface for * operator. Example: `select(mul(&User::salary, 2));` => SELECT salary * 2 FROM users */ template - internal::mul_t mul(L l, R r) { + constexpr internal::mul_t mul(L l, R r) { return {std::move(l), std::move(r)}; } @@ -4358,7 +4358,7 @@ namespace sqlite_orm { * 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) { + constexpr internal::div_t div(L l, R r) { return {std::move(l), std::move(r)}; } @@ -4366,32 +4366,32 @@ namespace sqlite_orm { * Public interface for % operator. Example: `select(mod(&User::age, 5));` => SELECT age % 5 FROM users */ template - internal::mod_t mod(L l, R r) { + constexpr 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) { + constexpr 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) { + constexpr 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) { + constexpr 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) { + constexpr 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) { + constexpr internal::bitwise_not_t bitwise_not(T t) { return {std::move(t)}; } @@ -4485,7 +4485,7 @@ namespace sqlite_orm { expression_type expression; - where_t(expression_type expression_) : expression(std::move(expression_)) {} + constexpr where_t(expression_type expression_) : expression(std::move(expression_)) {} }; template @@ -4505,7 +4505,7 @@ namespace sqlite_orm { * auto rows = storage.select(&Letter::name, where(greater_than(&Letter::id, 3))); */ template - internal::where_t where(C expression) { + constexpr internal::where_t where(C expression) { return {std::move(expression)}; } } @@ -4717,12 +4717,12 @@ namespace sqlite_orm { is_operator_argument_v::value>> = true; template - T get_from_expression(T value) { + constexpr T get_from_expression(T value) { return std::move(value); } template - T get_from_expression(expression_t expression) { + constexpr T get_from_expression(expression_t expression) { return std::move(expression.value); } @@ -4735,7 +4735,7 @@ namespace sqlite_orm { * `storage.update(set(c(&User::name) = "Dua Lipa")); */ template - internal::expression_t c(T value) { + constexpr internal::expression_t c(T value) { return {std::move(value)}; } } @@ -4852,7 +4852,7 @@ namespace sqlite_orm { struct negated_condition_t : condition_t, negated_condition_string { C c; - negated_condition_t(C c_) : c(std::move(c_)) {} + constexpr negated_condition_t(C c_) : c(std::move(c_)) {} }; /** @@ -4871,9 +4871,9 @@ namespace sqlite_orm { left_type lhs; right_type rhs; - binary_condition() = default; + constexpr binary_condition() = default; - binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} + constexpr binary_condition(left_type l_, right_type r_) : lhs(std::move(l_)), rhs(std::move(r_)) {} }; template @@ -5588,7 +5588,7 @@ namespace sqlite_orm { class T, std::enable_if_t, is_operator_argument>::value, bool> = true> - negated_condition_t operator!(T arg) { + constexpr negated_condition_t operator!(T arg) { return {std::move(arg)}; } @@ -5599,7 +5599,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - less_than_t, unwrap_expression_t> operator<(L l, R r) { + constexpr less_than_t, unwrap_expression_t> operator<(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5610,7 +5610,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - less_or_equal_t, unwrap_expression_t> operator<=(L l, R r) { + constexpr 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))}; } @@ -5621,7 +5621,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - greater_than_t, unwrap_expression_t> operator>(L l, R r) { + constexpr greater_than_t, unwrap_expression_t> operator>(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5632,7 +5632,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - greater_or_equal_t, unwrap_expression_t> operator>=(L l, R r) { + constexpr 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))}; } @@ -5645,7 +5645,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - is_equal_t, unwrap_expression_t> operator==(L l, R r) { + constexpr is_equal_t, unwrap_expression_t> operator==(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5658,7 +5658,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - is_not_equal_t, unwrap_expression_t> operator!=(L l, R r) { + constexpr 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))}; } @@ -5669,7 +5669,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - and_condition_t, unwrap_expression_t> operator&&(L l, R r) { + constexpr and_condition_t, unwrap_expression_t> operator&&(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5678,7 +5678,7 @@ namespace sqlite_orm { std::enable_if_t< polyfill::disjunction, std::is_base_of>::value, bool> = true> - or_condition_t, unwrap_expression_t> operator||(L l, R r) { + constexpr or_condition_t, unwrap_expression_t> operator||(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -5694,7 +5694,7 @@ namespace sqlite_orm { polyfill::negation, std::is_base_of>>>::value, bool> = true> - conc_t, unwrap_expression_t> operator||(L l, R r) { + constexpr conc_t, unwrap_expression_t> operator||(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } } @@ -5792,14 +5792,14 @@ namespace sqlite_orm { } template - auto and_(L l, R r) { + constexpr 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))}; } template - auto or_(L l, R r) { + constexpr 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))}; @@ -5846,52 +5846,52 @@ namespace sqlite_orm { } template - internal::is_equal_t is_equal(L l, R r) { + constexpr internal::is_equal_t is_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_equal_t eq(L l, R r) { + constexpr internal::is_equal_t eq(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_equal_with_table_t is_equal(R rhs) { + constexpr internal::is_equal_with_table_t is_equal(R rhs) { return {std::move(rhs)}; } template - internal::is_not_equal_t is_not_equal(L l, R r) { + constexpr internal::is_not_equal_t is_not_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::is_not_equal_t ne(L l, R r) { + constexpr internal::is_not_equal_t ne(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_than_t greater_than(L l, R r) { + constexpr internal::greater_than_t greater_than(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_than_t gt(L l, R r) { + constexpr internal::greater_than_t gt(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_or_equal_t greater_or_equal(L l, R r) { + constexpr internal::greater_or_equal_t greater_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::greater_or_equal_t ge(L l, R r) { + constexpr internal::greater_or_equal_t ge(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::less_than_t less_than(L l, R r) { + constexpr internal::less_than_t less_than(L l, R r) { return {std::move(l), std::move(r)}; } @@ -5905,12 +5905,12 @@ namespace sqlite_orm { } template - internal::less_than_t lt(L l, R r) { + constexpr internal::less_than_t lt(L l, R r) { return {std::move(l), std::move(r)}; } template - internal::less_or_equal_t less_or_equal(L l, R r) { + constexpr internal::less_or_equal_t less_or_equal(L l, R r) { return {std::move(l), std::move(r)}; } @@ -5924,7 +5924,7 @@ namespace sqlite_orm { } template - internal::less_or_equal_t le(L l, R r) { + constexpr internal::less_or_equal_t le(L l, R r) { return {std::move(l), std::move(r)}; } @@ -8086,7 +8086,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - add_t, unwrap_expression_t> operator+(L l, R r) { + constexpr add_t, unwrap_expression_t> operator+(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -8097,7 +8097,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - sub_t, unwrap_expression_t> operator-(L l, R r) { + constexpr sub_t, unwrap_expression_t> operator-(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -8108,7 +8108,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - mul_t, unwrap_expression_t> operator*(L l, R r) { + constexpr mul_t, unwrap_expression_t> operator*(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -8119,7 +8119,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - div_t, unwrap_expression_t> operator/(L l, R r) { + constexpr div_t, unwrap_expression_t> operator/(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } @@ -8130,7 +8130,7 @@ namespace sqlite_orm { is_operator_argument, is_operator_argument>::value, bool> = true> - mod_t, unwrap_expression_t> operator%(L l, R r) { + constexpr mod_t, unwrap_expression_t> operator%(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } } @@ -8196,7 +8196,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto operator()(ExplicitCols... explicitColumns) const; + constexpr auto operator()(ExplicitCols... explicitColumns) const; #else template>, std::is_convertible>...>, bool> = true> - auto operator()(ExplicitCols... explicitColumns) const; + constexpr auto operator()(ExplicitCols... explicitColumns) const; #endif }; } @@ -8600,7 +8600,7 @@ namespace sqlite_orm { expressions_tuple compound; - compound_operator(expressions_tuple compound) : compound{std::move(compound)} { + constexpr compound_operator(expressions_tuple compound) : compound{std::move(compound)} { iterate_tuple(this->compound, [](auto& expression) { expression.highest_level = true; }); @@ -8636,7 +8636,7 @@ namespace sqlite_orm { struct union_t : public compound_operator, union_base { using typename compound_operator::expressions_tuple; - union_t(expressions_tuple compound, bool all) : + constexpr union_t(expressions_tuple compound, bool all) : compound_operator{std::move(compound)}, union_base{all} {} }; @@ -8717,7 +8717,7 @@ namespace sqlite_orm { explicit_colrefs_tuple explicitColumns; expression_type subselect; - common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : + constexpr common_table_expression(explicit_colrefs_tuple explicitColumns, expression_type subselect) : explicitColumns{std::move(explicitColumns)}, subselect{std::move(subselect)} { this->subselect.highest_level = true; } @@ -8732,23 +8732,25 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3035000 && defined(SQLITE_ORM_WITH_CPP20_ALIASES) template = true> - common_table_expression, Select> as(Select sel) && { + constexpr common_table_expression, Select> + as(Select sel) && { return {std::move(this->explicitColumns), std::move(sel)}; } template = true> - common_table_expression, select_t> + constexpr common_table_expression, select_t> as(Compound sel) && { return {std::move(this->explicitColumns), {std::move(sel)}}; } #else template = true> - common_table_expression, Select> as(Select sel) && { + constexpr common_table_expression, Select> as(Select sel) && { return {std::move(this->explicitColumns), std::move(sel)}; } template = true> - common_table_expression, select_t> as(Compound sel) && { + constexpr common_table_expression, select_t> + as(Compound sel) && { return {std::move(this->explicitColumns), {std::move(sel)}}; } #endif @@ -8870,7 +8872,7 @@ namespace sqlite_orm { }; template - void validate_conditions() { + constexpr void validate_conditions() { static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 WHERE blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 GROUP BY blocks"); static_assert(count_tuple::value <= 1, "a single query cannot contain > 1 ORDER BY blocks"); @@ -8937,7 +8939,7 @@ namespace sqlite_orm { * Public function for subselect query. Is useful in UNION queries. */ template - internal::select_t select(T t, Args... args) { + constexpr internal::select_t select(T t, Args... args) { using args_tuple = std::tuple; internal::validate_conditions(); return {std::move(t), {std::forward(args)...}}; @@ -8949,7 +8951,7 @@ namespace sqlite_orm { * Look through example in examples/union.cpp */ template - internal::union_t union_(E... expressions) { + constexpr internal::union_t union_(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, false}; } @@ -8960,7 +8962,7 @@ namespace sqlite_orm { * Look through example in examples/union.cpp */ template - internal::union_t union_all(E... expressions) { + constexpr internal::union_t union_all(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, true}; } @@ -8971,13 +8973,13 @@ namespace sqlite_orm { * Look through example in examples/except.cpp */ template - internal::except_t except(E... expressions) { + constexpr internal::except_t except(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } template - internal::intersect_t intersect(E... expressions) { + constexpr internal::intersect_t intersect(E... expressions) { static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } @@ -9031,7 +9033,7 @@ namespace sqlite_orm { std::is_same>, std::is_convertible>...>, bool> = true> - auto cte(ExplicitCols... explicitColumns) { + constexpr auto cte(ExplicitCols... explicitColumns) { using namespace ::sqlite_orm::internal; static_assert(is_cte_moniker_v, "Moniker must be a CTE moniker"); static_assert((!is_builtin_numeric_column_alias_v && ...), @@ -9049,7 +9051,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto cte(ExplicitCols... explicitColumns) { + constexpr auto cte(ExplicitCols... explicitColumns) { using namespace ::sqlite_orm::internal; static_assert((!is_builtin_numeric_column_alias_v && ...), "Numeric column aliases are reserved for referencing columns locally within a single CTE."); @@ -9068,7 +9070,7 @@ namespace sqlite_orm { std::same_as> || std::convertible_to) && ...) - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + constexpr auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { return cte>(std::forward(explicitColumns)...); } #else @@ -9080,7 +9082,7 @@ namespace sqlite_orm { std::is_same>, std::is_convertible>...>, bool>> - auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { + constexpr auto cte_moniker::operator()(ExplicitCols... explicitColumns) const { return cte>(std::forward(explicitColumns)...); } #endif @@ -9217,7 +9219,7 @@ namespace sqlite_orm { * If you need to fetch results as objects instead of tuples please use `object()`. */ template - internal::asterisk_t asterisk(bool definedOrder = false) { + constexpr internal::asterisk_t asterisk(bool definedOrder = false) { return {definedOrder}; } @@ -9229,7 +9231,7 @@ namespace sqlite_orm { * storage.select(asterisk(), inner_join(on(m->*&Employee::reportsTo == &Employee::employeeId))); */ template - auto asterisk(bool definedOrder = false) { + constexpr auto asterisk(bool definedOrder = false) { return asterisk>(definedOrder); } #endif @@ -9246,13 +9248,13 @@ namespace sqlite_orm { * If you need to fetch results as tuples instead of objects please use `asterisk()`. */ template - internal::object_t object(bool definedOrder = false) { + constexpr internal::object_t object(bool definedOrder = false) { return {definedOrder}; } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template - auto object(bool definedOrder = false) { + constexpr auto object(bool definedOrder = false) { return object>(definedOrder); } #endif From 31103782b3ea08e31f3fe1b95b777b34c5f10792 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 21 Jul 2024 21:51:19 +0200 Subject: [PATCH 20/38] Unit tests for serializing the With clause of CRUD expressions --- .../statements/insert_replace.cpp | 34 +++++++++++++++++++ .../statements/remove_all.cpp | 28 +++++++++++---- .../statements/update_all.cpp | 33 +++++++++++++++--- tests/storage_tests.cpp | 13 ++++--- 4 files changed, 90 insertions(+), 18 deletions(-) diff --git a/tests/statement_serializer_tests/statements/insert_replace.cpp b/tests/statement_serializer_tests/statements/insert_replace.cpp index e30be740..b3882fe2 100644 --- a/tests/statement_serializer_tests/statements/insert_replace.cpp +++ b/tests/statement_serializer_tests/statements/insert_replace.cpp @@ -80,6 +80,23 @@ TEST_CASE("statement_serializer insert/replace") { expected = R"(REPLACE INTO "users" SELECT "users_backup"."id", "users_backup"."name" FROM "users_backup")"; } +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("With clause") { + constexpr orm_cte_moniker auto data = "data"_cte; + constexpr auto cteExpression = cte().as(select(asterisk())); + auto dbObjects2 = + internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + using context_t = internal::serializer_context; + context_t context2{dbObjects2}; + + auto expression = with(cteExpression, replace(into(), select(asterisk()))); + value = serialize(expression, context2); + expected = + R"(WITH "data"("id", "name") AS (SELECT "users_backup".* FROM "users_backup") REPLACE INTO "users" SELECT "data".* FROM "data")"; + } +#endif +#endif } SECTION("range") { context.replace_bindable_with_question = false; @@ -356,6 +373,23 @@ TEST_CASE("statement_serializer insert/replace") { R"(INSERT OR ROLLBACK INTO "users" SELECT "users_backup"."id", "users_backup"."name" FROM "users_backup")"; } } + SECTION("With clause") { +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr orm_cte_moniker auto data = "data"_cte; + constexpr auto cteExpression = cte().as(select(asterisk())); + auto dbObjects2 = + internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + using context_t = internal::serializer_context; + context_t context2{dbObjects2}; + + auto expression = with(cteExpression, insert(into(), select(asterisk()))); + value = serialize(expression, context2); + expected = + R"(WITH "data"("id", "name") AS (SELECT "users_backup".* FROM "users_backup") INSERT INTO "users" SELECT "data".* FROM "data")"; +#endif +#endif + } } SECTION("range") { context.replace_bindable_with_question = false; diff --git a/tests/statement_serializer_tests/statements/remove_all.cpp b/tests/statement_serializer_tests/statements/remove_all.cpp index 1bfd6c3c..ba30b9c0 100644 --- a/tests/statement_serializer_tests/statements/remove_all.cpp +++ b/tests/statement_serializer_tests/statements/remove_all.cpp @@ -19,19 +19,35 @@ TEST_CASE("statement_serializer remove_all") { std::string expected; SECTION("all") { - auto statement = remove_all(); - value = serialize(statement, context); + auto expression = remove_all(); + value = serialize(expression, context); expected = R"(DELETE FROM "users")"; } SECTION("where") { - auto statement = remove_all(where(&User::id == c(1))); - value = serialize(statement, context); + auto expression = remove_all(where(&User::id == c(1))); + value = serialize(expression, context); expected = R"(DELETE FROM "users" WHERE ("id" = 1))"; } SECTION("conditions") { - auto statement = remove_all(where(&User::id == c(1)), limit(1)); - value = serialize(statement, context); + auto expression = remove_all(where(&User::id == c(1)), limit(1)); + value = serialize(expression, context); expected = R"(DELETE FROM "users" WHERE ("id" = 1) LIMIT 1)"; } +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("With clause") { + constexpr orm_cte_moniker auto data = "data"_cte; + constexpr auto cteExpression = cte().as(select(1)); + auto dbObjects2 = internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + using context_t = internal::serializer_context; + context_t context2{dbObjects2}; + + auto expression = with(cteExpression, remove_all(where(c(&User::id).in(select(data->*1_colalias))))); + value = serialize(expression, context2); + expected = + R"(WITH "data"("1") AS (SELECT 1) DELETE FROM "users" WHERE ("id" IN (SELECT "data"."1" FROM "data")))"; + } +#endif +#endif REQUIRE(value == expected); } diff --git a/tests/statement_serializer_tests/statements/update_all.cpp b/tests/statement_serializer_tests/statements/update_all.cpp index 4110a1db..006255c8 100644 --- a/tests/statement_serializer_tests/statements/update_all.cpp +++ b/tests/statement_serializer_tests/statements/update_all.cpp @@ -51,10 +51,33 @@ TEST_CASE("statement_serializer update_all") { using context_t = internal::serializer_context; context_t context{dbObjects}; - auto statement = - update_all(set(c(&Contact::phone) = select(&Customer::phone, from(), where(c(&Customer::id) == 1)))); - auto value = serialize(statement, context); - decltype(value) expected = - R"(UPDATE "contacts" SET "phone" = (SELECT "customers"."Phone" FROM "customers" WHERE ("customers"."CustomerId" = 1)))"; + std::string value; + std::string expected; + SECTION("select") { + auto expression = update_all(set(c(&Contact::phone) = select(&Customer::phone, where(c(&Customer::id) == 1))), + where(c(&Contact::id) == 1)); + value = serialize(expression, context); + expected = + R"(UPDATE "contacts" SET "phone" = (SELECT "customers"."Phone" FROM "customers" WHERE ("customers"."CustomerId" = 1)) WHERE ("contact_id" = 1))"; + } +#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("With clause") { + constexpr orm_cte_moniker auto data = "data"_cte; + constexpr auto cteExpression = cte().as(select(&Customer::phone, where(c(&Customer::id) == 1))); + auto dbObjects2 = internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); + using context_t = internal::serializer_context; + context_t context2{dbObjects2}; + + auto expression = + with(cteExpression, + update_all(set(c(&Contact::phone) = select(data->*&Customer::phone)), where(c(&Contact::id) == 1))); + + value = serialize(expression, context2); + expected = + R"(WITH "data"("Phone") AS (SELECT "customers"."Phone" FROM "customers" WHERE ("customers"."CustomerId" = 1)) UPDATE "contacts" SET "phone" = (SELECT "data"."Phone" FROM "data") WHERE ("contact_id" = 1))"; + } +#endif +#endif REQUIRE(value == expected); } diff --git a/tests/storage_tests.cpp b/tests/storage_tests.cpp index 37d32398..b241d78e 100644 --- a/tests/storage_tests.cpp +++ b/tests/storage_tests.cpp @@ -593,23 +593,22 @@ TEST_CASE("With clause") { storage.sync_schema(); constexpr orm_cte_moniker auto data = "data"_cte; - storage.with(cte().as(union_all(select(2), select(3))), - insert(into(), columns(&Object::id), select(data->*1_colalias))); + constexpr auto cteExpression = cte().as(union_all(select(2), select(3))); + + storage.with(cteExpression, insert(into(), columns(&Object::id), select(data->*1_colalias))); REQUIRE(3 == storage.last_insert_rowid()); - storage.with(cte().as(union_all(select(2), select(3))), - replace(into(), columns(&Object::id), select(data->*1_colalias))); + storage.with(cteExpression, replace(into(), columns(&Object::id), select(data->*1_colalias))); REQUIRE(storage.changes() == 2); storage.with( - cte().as(union_all(select(2), select(3))), + cteExpression, update_all( set(c(&Object::id) = select(data->*1_colalias, from(), where(data->*1_colalias == &Object::id))), where(c(&Object::id).in(select(data->*1_colalias))))); REQUIRE(storage.changes() == 2); - storage.with(cte().as(union_all(select(2), select(3))), - remove_all(where(c(&Object::id).in(select(data->*1_colalias))))); + storage.with(cteExpression, remove_all(where(c(&Object::id).in(select(data->*1_colalias))))); REQUIRE(storage.changes() == 2); } #endif From 388ed523733b1044d27d57eeabc81d74a1ed3025 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 23 Jul 2024 12:03:09 +0200 Subject: [PATCH 21/38] Added missing `node_tuple` specialization for bitwise not --- dev/node_tuple.h | 3 +++ include/sqlite_orm/sqlite_orm.h | 3 +++ tests/static_tests/node_tuple.cpp | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/dev/node_tuple.h b/dev/node_tuple.h index 1cf4729e..d82825af 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -199,6 +199,9 @@ namespace sqlite_orm { template struct node_tuple, void> : node_tuple {}; + template + struct node_tuple, void> : node_tuple {}; + template struct node_tuple, void> : node_tuple_for {}; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 07207af3..9adac345 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -24197,6 +24197,9 @@ namespace sqlite_orm { template struct node_tuple, void> : node_tuple {}; + template + struct node_tuple, void> : node_tuple {}; + template struct node_tuple, void> : node_tuple_for {}; diff --git a/tests/static_tests/node_tuple.cpp b/tests/static_tests/node_tuple.cpp index 59b4e6c9..9f10491d 100644 --- a/tests/static_tests/node_tuple.cpp +++ b/tests/static_tests/node_tuple.cpp @@ -209,6 +209,24 @@ TEST_CASE("Node tuple") { using AssignTuple = node_tuple_t>; static_assert(is_same>::value, "assign_t"); } + SECTION("bitwise operator") { + using namespace internal; + + using BitwiseNotTuple = node_tuple_t>; + STATIC_REQUIRE(is_same>::value); + + using BitwiseShiftLeftTuple = node_tuple_t>; + STATIC_REQUIRE(is_same>::value); + + using BitwiseShiftRightTuple = node_tuple_t>; + STATIC_REQUIRE(is_same>::value); + + using BitwiseAndTuple = node_tuple_t>; + STATIC_REQUIRE(is_same>::value); + + using BitwiseOrTuple = node_tuple_t>; + STATIC_REQUIRE(is_same>::value); + } SECTION("columns") { auto cols = columns(&User::id, &User::name); using Cols = decltype(cols); From 766861e8cb70358cfd1429e6f9ac1deb705d617f Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 23 Jul 2024 12:30:25 +0200 Subject: [PATCH 22/38] C++ operator overloads for bitwise operators The bitwise operators were missing C++ operator equivalents. Also: * Added serialization unit tests for bitwise operators * Added missing column result type unit tests for bitwise operators * Added missing serialization unit tests for logical not * Improved parenthesizing logical and bitwise "not" expressions --- dev/conditions.h | 10 +- dev/core_functions.h | 52 ++++++++ dev/expression.h | 14 ++- dev/statement_serializer.h | 41 ++++-- include/sqlite_orm/sqlite_orm.h | 118 +++++++++++++++--- tests/CMakeLists.txt | 6 +- .../{bitwise.cpp => bitwise_operators.cpp} | 24 ++-- .../bitwise_operators.cpp | 71 +++++++++++ .../logical_operators.cpp | 14 +++ .../schema/trigger.cpp | 2 +- tests/static_tests/column_result_t.cpp | 7 +- tests/static_tests/operators_adl.cpp | 11 ++ 12 files changed, 327 insertions(+), 43 deletions(-) rename tests/operators/{bitwise.cpp => bitwise_operators.cpp} (50%) create mode 100644 tests/statement_serializer_tests/bitwise_operators.cpp diff --git a/dev/conditions.h b/dev/conditions.h index d7ac8b93..7ee3dcff 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -111,9 +111,11 @@ namespace sqlite_orm { */ template struct negated_condition_t : condition_t, negated_condition_string { - C c; + using argument_type = C; - constexpr negated_condition_t(C c_) : c(std::move(c_)) {} + argument_type c; + + constexpr negated_condition_t(argument_type arg) : c(std::move(arg)) {} }; /** @@ -153,7 +155,7 @@ namespace sqlite_orm { * Result of and operator */ template - struct and_condition_t : binary_condition { + struct and_condition_t : binary_condition, negatable_t { using super = binary_condition; using super::super; @@ -169,7 +171,7 @@ namespace sqlite_orm { * Result of or operator */ template - struct or_condition_t : binary_condition { + struct or_condition_t : binary_condition, negatable_t { using super = binary_condition; using super::super; diff --git a/dev/core_functions.h b/dev/core_functions.h index bed10999..3c4136b5 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -2095,6 +2095,58 @@ namespace sqlite_orm { constexpr mod_t, unwrap_expression_t> operator%(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } + + template< + class T, + std::enable_if_t, is_operator_argument>::value, + bool> = true> + constexpr bitwise_not_t> operator~(T arg) { + return {get_from_expression(std::forward(arg))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_shift_left_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> + constexpr bitwise_shift_right_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> + constexpr bitwise_and_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> + constexpr bitwise_or_t, unwrap_expression_t> operator|(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } } template diff --git a/dev/expression.h b/dev/expression.h index d40b4c63..39a86aec 100644 --- a/dev/expression.h +++ b/dev/expression.h @@ -68,15 +68,25 @@ namespace sqlite_orm { is_operator_argument_v::value>> = true; template - constexpr T get_from_expression(T value) { + constexpr T get_from_expression(T&& value) { return std::move(value); } template - constexpr T get_from_expression(expression_t expression) { + constexpr const T& get_from_expression(const T& value) { + return value; + } + + template + constexpr T get_from_expression(expression_t&& expression) { return std::move(expression.value); } + template + constexpr const T& get_from_expression(const expression_t& expression) { + return expression.value; + } + template using unwrap_expression_t = decltype(get_from_expression(std::declval())); } diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index fd3da122..60cf5586 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -35,6 +35,7 @@ #include "type_printer.h" #include "field_printer.h" #include "literal.h" +#include "expression.h" #include "table_name_collector.h" #include "column_names_getter.h" #include "cte_column_names_collector.h" @@ -721,11 +722,23 @@ namespace sqlite_orm { using statement_type = bitwise_not_t; template - std::string operator()(const statement_type& statement, const Ctx& context) const { + std::string operator()(const statement_type& expression, const Ctx& context) const { + // subqueries should always use parentheses in binary expressions + auto subCtx = context; + subCtx.use_parentheses = true; + // parentheses for sub-trees to ensure the order of precedence + constexpr bool parenthesize = is_binary_condition::value || + is_binary_operator::value; + std::stringstream ss; - ss << statement.serialize() << " "; - auto cString = serialize(statement.argument, context); - ss << " (" << cString << " )"; + ss << expression.serialize(); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << "("; + } + ss << serialize(get_from_expression(expression.argument), subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << ")"; + } return ss.str(); } }; @@ -735,11 +748,23 @@ namespace sqlite_orm { using statement_type = negated_condition_t; template - std::string operator()(const statement_type& c, const Ctx& context) const { + std::string operator()(const statement_type& expression, const Ctx& context) const { + // subqueries should always use parentheses in binary expressions + auto subCtx = context; + subCtx.use_parentheses = true; + // parentheses for sub-trees to ensure the order of precedence + constexpr bool parenthesize = is_binary_condition::value || + is_binary_operator::value; + std::stringstream ss; - ss << static_cast(c) << " "; - auto cString = serialize(c.c, context); - ss << " (" << cString << " )"; + ss << static_cast(expression) << " "; + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << "("; + } + ss << serialize(get_from_expression(expression.c), subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << ")"; + } return ss.str(); } }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 9adac345..ff4507de 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -4717,15 +4717,25 @@ namespace sqlite_orm { is_operator_argument_v::value>> = true; template - constexpr T get_from_expression(T value) { + constexpr T get_from_expression(T&& value) { return std::move(value); } template - constexpr T get_from_expression(expression_t expression) { + constexpr const T& get_from_expression(const T& value) { + return value; + } + + template + constexpr T get_from_expression(expression_t&& expression) { return std::move(expression.value); } + template + constexpr const T& get_from_expression(const expression_t& expression) { + return expression.value; + } + template using unwrap_expression_t = decltype(get_from_expression(std::declval())); } @@ -4850,9 +4860,11 @@ namespace sqlite_orm { */ template struct negated_condition_t : condition_t, negated_condition_string { - C c; + using argument_type = C; + + argument_type c; - constexpr negated_condition_t(C c_) : c(std::move(c_)) {} + constexpr negated_condition_t(argument_type arg) : c(std::move(arg)) {} }; /** @@ -4892,7 +4904,7 @@ namespace sqlite_orm { * Result of and operator */ template - struct and_condition_t : binary_condition { + struct and_condition_t : binary_condition, negatable_t { using super = binary_condition; using super::super; @@ -4908,7 +4920,7 @@ namespace sqlite_orm { * Result of or operator */ template - struct or_condition_t : binary_condition { + struct or_condition_t : binary_condition, negatable_t { using super = binary_condition; using super::super; @@ -8133,6 +8145,58 @@ namespace sqlite_orm { constexpr mod_t, unwrap_expression_t> operator%(L l, R r) { return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; } + + template< + class T, + std::enable_if_t, is_operator_argument>::value, + bool> = true> + constexpr bitwise_not_t> operator~(T arg) { + return {get_from_expression(std::forward(arg))}; + } + + template, + std::is_base_of, + is_operator_argument, + is_operator_argument>::value, + bool> = true> + constexpr bitwise_shift_left_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> + constexpr bitwise_shift_right_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> + constexpr bitwise_and_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> + constexpr bitwise_or_t, unwrap_expression_t> operator|(L l, R r) { + return {get_from_expression(std::forward(l)), get_from_expression(std::forward(r))}; + } } template @@ -18463,6 +18527,8 @@ namespace sqlite_orm { // #include "literal.h" +// #include "expression.h" + // #include "table_name_collector.h" // #include "column_names_getter.h" @@ -19879,11 +19945,23 @@ namespace sqlite_orm { using statement_type = bitwise_not_t; template - std::string operator()(const statement_type& statement, const Ctx& context) const { + std::string operator()(const statement_type& expression, const Ctx& context) const { + // subqueries should always use parentheses in binary expressions + auto subCtx = context; + subCtx.use_parentheses = true; + // parentheses for sub-trees to ensure the order of precedence + constexpr bool parenthesize = is_binary_condition::value || + is_binary_operator::value; + std::stringstream ss; - ss << statement.serialize() << " "; - auto cString = serialize(statement.argument, context); - ss << " (" << cString << " )"; + ss << expression.serialize(); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << "("; + } + ss << serialize(get_from_expression(expression.argument), subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << ")"; + } return ss.str(); } }; @@ -19893,11 +19971,23 @@ namespace sqlite_orm { using statement_type = negated_condition_t; template - std::string operator()(const statement_type& c, const Ctx& context) const { + std::string operator()(const statement_type& expression, const Ctx& context) const { + // subqueries should always use parentheses in binary expressions + auto subCtx = context; + subCtx.use_parentheses = true; + // parentheses for sub-trees to ensure the order of precedence + constexpr bool parenthesize = is_binary_condition::value || + is_binary_operator::value; + std::stringstream ss; - ss << static_cast(c) << " "; - auto cString = serialize(c.c, context); - ss << " (" << cString << " )"; + ss << static_cast(expression) << " "; + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << "("; + } + ss << serialize(get_from_expression(expression.c), subCtx); + if SQLITE_ORM_CONSTEXPR_IF(parenthesize) { + ss << ")"; + } return ss.str(); } }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index db24c65f..d3ebf450 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -57,15 +57,15 @@ add_executable(unit_tests built_in_functions_tests/math_functions.cpp user_defined_functions.cpp constraints/composite_key.cpp - operators/arithmetic_operators.cpp operators/like.cpp operators/glob.cpp operators/in.cpp operators/cast.cpp operators/is_null.cpp - operators/not_operator.cpp - operators/bitwise.cpp + operators/arithmetic_operators.cpp + operators/bitwise_operators.cpp operators/binary_operators.cpp + operators/not_operator.cpp prepared_statement_tests/select.cpp prepared_statement_tests/get_all.cpp prepared_statement_tests/get_all_pointer.cpp diff --git a/tests/operators/bitwise.cpp b/tests/operators/bitwise_operators.cpp similarity index 50% rename from tests/operators/bitwise.cpp rename to tests/operators/bitwise_operators.cpp index 7492a2c1..c6524155 100644 --- a/tests/operators/bitwise.cpp +++ b/tests/operators/bitwise_operators.cpp @@ -18,46 +18,50 @@ TEST_CASE("bitwise operators") { storage.sync_schema(); { - auto rows = storage.select(bitwise_or(60, 13)); + auto rows = storage.select(union_(select(bitwise_or(60, 13)), select(c(60) | 13))); REQUIRE(rows == std::vector{61}); } { - auto rows = storage.select(bitwise_and(60, 13)); + auto rows = storage.select(union_(select(bitwise_and(60, 13)), select(c(60) & 13))); REQUIRE(rows == std::vector{12}); } { - auto rows = storage.select(bitwise_shift_left(60, 2)); + auto rows = storage.select(union_(select(bitwise_shift_left(60, 2)), select(c(60) << 2))); REQUIRE(rows == std::vector{240}); } { - auto rows = storage.select(bitwise_shift_right(60, 2)); + auto rows = storage.select(union_(select(bitwise_shift_right(60, 2)), select(c(60) >> 2))); REQUIRE(rows == std::vector{15}); } { - auto rows = storage.select(bitwise_not(60)); + auto rows = storage.select(union_(select(bitwise_not(60)), select(~c(60)))); REQUIRE(rows == std::vector{-61}); } storage.insert(Entry{60, 13}); { - auto rows = storage.select(bitwise_or(&Entry::lhs, &Entry::rhs)); + auto rows = + storage.select(union_(select(bitwise_or(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) | &Entry::rhs))); REQUIRE(rows == std::vector{61}); } { - auto rows = storage.select(bitwise_and(&Entry::lhs, &Entry::rhs)); + auto rows = + storage.select(union_(select(bitwise_and(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) & &Entry::rhs))); REQUIRE(rows == std::vector{12}); } storage.remove_all(); storage.insert(Entry{60, 2}); { - auto rows = storage.select(bitwise_shift_left(&Entry::lhs, &Entry::rhs)); + auto rows = storage.select( + union_(select(bitwise_shift_left(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) << &Entry::rhs))); REQUIRE(rows == std::vector{240}); } { - auto rows = storage.select(bitwise_shift_right(&Entry::lhs, &Entry::rhs)); + auto rows = storage.select( + union_(select(bitwise_shift_right(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) >> &Entry::rhs))); REQUIRE(rows == std::vector{15}); } { - auto rows = storage.select(bitwise_not(&Entry::lhs)); + auto rows = storage.select(union_(select(bitwise_not(&Entry::lhs)), select(~c(&Entry::lhs)))); REQUIRE(rows == std::vector{-61}); } } diff --git a/tests/statement_serializer_tests/bitwise_operators.cpp b/tests/statement_serializer_tests/bitwise_operators.cpp new file mode 100644 index 00000000..b5b119c9 --- /dev/null +++ b/tests/statement_serializer_tests/bitwise_operators.cpp @@ -0,0 +1,71 @@ +#include +#include + +using namespace sqlite_orm; + +TEST_CASE("statement_serializer bitwise operators") { + internal::db_objects_tuple<> storage; + internal::serializer_context> context{storage}; + std::string value; + decltype(value) expected; + SECTION("bitwise_or") { + SECTION("func") { + value = serialize(bitwise_or(3, 5), context); + } + SECTION("operator") { + value = serialize(c(3) | 5, context); + } + expected = "3 | 5"; + } + SECTION("bitwise_and") { + SECTION("func") { + value = serialize(bitwise_and(5, -9), context); + } + SECTION("operator") { + value = serialize(c(5) & -9, context); + } + expected = "5 & -9"; + } + SECTION("bitwise_shift_left") { + SECTION("func") { + value = serialize(bitwise_shift_left(10, 1), context); + } + SECTION("operator") { + value = serialize(c(10) << 1, context); + } + expected = "10 << 1"; + } + SECTION("bitwise_shift_right") { + SECTION("func") { + value = serialize(bitwise_shift_right(10, 2), context); + } + SECTION("operator") { + value = serialize(c(10) >> 2, context); + } + expected = "10 >> 2"; + } + SECTION("bitwise_not") { + SECTION("func") { + value = serialize(bitwise_not(20), context); + } + SECTION("operator") { + value = serialize(~c(20), context); + } + expected = "~20"; + } + SECTION("parentheses keeping order of precedence") { + SECTION("1") { + value = serialize(c(4) << 5 << 3, context); + expected = "(4 << 5) << 3"; + } + SECTION("2") { + value = serialize(4 << (c(5) << 3), context); + expected = "4 << (5 << 3)"; + } + SECTION("3") { + value = serialize(4 | ~c(5) & 3 | 1, context); + expected = "(4 | (~5 & 3)) | 1"; + } + } + REQUIRE(value == expected); +} diff --git a/tests/statement_serializer_tests/logical_operators.cpp b/tests/statement_serializer_tests/logical_operators.cpp index 312f4c29..00fdae9c 100644 --- a/tests/statement_serializer_tests/logical_operators.cpp +++ b/tests/statement_serializer_tests/logical_operators.cpp @@ -63,6 +63,20 @@ TEST_CASE("statement_serializer logical operators") { expected = R"(("id" = 5) OR ("name" = 'Ariana'))"; } } + SECTION("not") { + SECTION("simple") { + SECTION("operator") { + stringValue = serialize(!c(20), context); + } + expected = "NOT 20"; + } + SECTION("complex") { + SECTION("operator") { + stringValue = serialize(!(c(20) and 1), context); + } + expected = "NOT (20 AND 1)"; + } + } SECTION("in") { SECTION("static in") { auto inValue = c(&User::id).in(1, 2, 3); diff --git a/tests/statement_serializer_tests/schema/trigger.cpp b/tests/statement_serializer_tests/schema/trigger.cpp index 28c9d00f..99d66980 100644 --- a/tests/statement_serializer_tests/schema/trigger.cpp +++ b/tests/statement_serializer_tests/schema/trigger.cpp @@ -37,7 +37,7 @@ TEST_CASE("statement_serializer trigger") { value = serialize(expression, context); expected = R"(CREATE TRIGGER IF NOT EXISTS "validate_email_before_insert_leads" BEFORE INSERT ON "leads" BEGIN SELECT )" - R"(CASE WHEN NOT (NEW."email" LIKE '%_@__%.__%' ) THEN RAISE(ABORT, 'Invalid email address') END; END)"; + R"(CASE WHEN NOT NEW."email" LIKE '%_@__%.__%' THEN RAISE(ABORT, 'Invalid email address') END; END)"; } REQUIRE(value == expected); } diff --git a/tests/static_tests/column_result_t.cpp b/tests/static_tests/column_result_t.cpp index 0a914afa..25b19491 100644 --- a/tests/static_tests/column_result_t.cpp +++ b/tests/static_tests/column_result_t.cpp @@ -105,11 +105,16 @@ TEST_CASE("column_result_of_t") { runTest(c(&User::id) / 5); runTest(mod(&User::id, 5)); runTest(c(&User::id) % 5); + runTest(bitwise_not(&User::id)); + runTest(~c(&User::id)); runTest(bitwise_shift_left(&User::id, 4)); + runTest(c(&User::id) << 4); runTest(bitwise_shift_right(&User::id, 4)); + runTest(c(&User::id) >> 4); runTest(bitwise_and(&User::id, 4)); + runTest(c(&User::id) & 4); runTest(bitwise_or(&User::id, 4)); - runTest(bitwise_not(&User::id)); + runTest(c(&User::id) | 4); runTest(rowid()); runTest(oid()); runTest(_rowid_()); diff --git a/tests/static_tests/operators_adl.cpp b/tests/static_tests/operators_adl.cpp index 74c5df20..cdfdb63c 100644 --- a/tests/static_tests/operators_adl.cpp +++ b/tests/static_tests/operators_adl.cpp @@ -15,6 +15,11 @@ using sqlite_orm::get; using sqlite_orm::or_; using sqlite_orm::internal::and_condition_t; using sqlite_orm::internal::binary_operator; +using sqlite_orm::internal::bitwise_and_t; +using sqlite_orm::internal::bitwise_not_t; +using sqlite_orm::internal::bitwise_or_t; +using sqlite_orm::internal::bitwise_shift_left_t; +using sqlite_orm::internal::bitwise_shift_right_t; using sqlite_orm::internal::greater_or_equal_t; using sqlite_orm::internal::greater_than_t; using sqlite_orm::internal::is_equal_t; @@ -106,6 +111,12 @@ void runTests(E expression) { // conc_t + condition_t yield or_condition_t STATIC_REQUIRE(is_specialization_of_v); STATIC_REQUIRE(is_specialization_of_v); + + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v> expression), binary_operator>); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); } TEST_CASE("inline namespace literals expressions") { From fcf86d10ac7a2a8ba39a1876e93c819c6065615e Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 23 Jul 2024 12:39:48 +0200 Subject: [PATCH 23/38] Provided a table reference for the "dbstat" eponymous virtual table --- dev/eponymous_vtabs/dbstat.h | 5 +++++ include/sqlite_orm/sqlite_orm.h | 6 ++++++ tests/builtin_tables.cpp | 4 ++++ tests/prepared_statement_tests/get_all.cpp | 4 ++-- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/dev/eponymous_vtabs/dbstat.h b/dev/eponymous_vtabs/dbstat.h index 1b674ed9..3c56b44b 100644 --- a/dev/eponymous_vtabs/dbstat.h +++ b/dev/eponymous_vtabs/dbstat.h @@ -6,6 +6,7 @@ #include "../schema/column.h" #include "../schema/table.h" +#include "../column_pointer.h" namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB @@ -35,5 +36,9 @@ namespace sqlite_orm { make_column("pgoffset", &dbstat::pgoffset), make_column("pgsize", &dbstat::pgsize)); } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + inline constexpr orm_table_reference auto dbstat_table = c(); +#endif #endif // SQLITE_ENABLE_DBSTAT_VTAB } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 84f58b6d..bc860c2d 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -23768,6 +23768,8 @@ namespace sqlite_orm { // #include "../schema/table.h" +// #include "../column_pointer.h" + namespace sqlite_orm { #ifdef SQLITE_ENABLE_DBSTAT_VTAB struct dbstat { @@ -23796,6 +23798,10 @@ namespace sqlite_orm { make_column("pgoffset", &dbstat::pgoffset), make_column("pgsize", &dbstat::pgsize)); } + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + inline constexpr orm_table_reference auto dbstat_table = c(); +#endif #endif // SQLITE_ENABLE_DBSTAT_VTAB } diff --git a/tests/builtin_tables.cpp b/tests/builtin_tables.cpp index 41fe960a..e82d8b7a 100644 --- a/tests/builtin_tables.cpp +++ b/tests/builtin_tables.cpp @@ -50,6 +50,10 @@ TEST_CASE("builtin tables") { auto dbstatRows = storage.get_all(); std::ignore = dbstatRows; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + dbstatRows = storage.get_all(); +#endif } #endif // SQLITE_ENABLE_DBSTAT_VTAB } diff --git a/tests/prepared_statement_tests/get_all.cpp b/tests/prepared_statement_tests/get_all.cpp index ff7dd3d1..26906a12 100644 --- a/tests/prepared_statement_tests/get_all.cpp +++ b/tests/prepared_statement_tests/get_all.cpp @@ -217,8 +217,8 @@ TEST_CASE("Prepared get all") { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES SECTION("from table reference") { - constexpr auto schema = c(); - auto statement = storage.prepare(get_all(where(schema->*&sqlite_master::type == "table"))); + auto statement = + storage.prepare(get_all(where(sqlite_master_table->*&sqlite_master::type == "table"))); auto str = storage.dump(statement); testSerializing(statement); } From dd2040efdfbc3c71d337f21db57bd832357a1031 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 23 Jul 2024 18:22:57 +0200 Subject: [PATCH 24/38] Added the "unary minus" operator --- dev/column_result.h | 5 ++ dev/core_functions.h | 8 ++++ dev/node_tuple.h | 3 ++ dev/operators.h | 25 +++++++++- dev/statement_serializer.h | 7 ++- include/sqlite_orm/sqlite_orm.h | 48 +++++++++++++++++-- .../arithmetic_operators.cpp | 17 +++++-- tests/static_tests/column_result_t.cpp | 2 + tests/static_tests/node_tuple.cpp | 17 ++++--- tests/static_tests/operators_adl.cpp | 5 ++ 10 files changed, 120 insertions(+), 17 deletions(-) diff --git a/dev/column_result.h b/dev/column_result.h index 6bf9ad7d..725546b3 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -149,6 +149,11 @@ namespace sqlite_orm { using type = std::string; }; + template + struct column_result_t, void> { + using type = double; + }; + template struct column_result_t, void> { using type = double; diff --git a/dev/core_functions.h b/dev/core_functions.h index 3c4136b5..918135ca 100644 --- a/dev/core_functions.h +++ b/dev/core_functions.h @@ -2041,6 +2041,14 @@ namespace sqlite_orm { // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace // to facilitate ADL (Argument Dependent Lookup) namespace internal { + template< + class T, + std::enable_if_t, is_operator_argument>::value, + bool> = true> + constexpr unary_minus_t> operator-(T arg) { + return {get_from_expression(std::forward(arg))}; + } + template, diff --git a/dev/node_tuple.h b/dev/node_tuple.h index d82825af..e551b04b 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -199,6 +199,9 @@ namespace sqlite_orm { template struct node_tuple, void> : node_tuple {}; + template + struct node_tuple, void> : node_tuple {}; + template struct node_tuple, void> : node_tuple {}; diff --git a/dev/operators.h b/dev/operators.h index 85d0e349..d656a0ec 100644 --- a/dev/operators.h +++ b/dev/operators.h @@ -41,6 +41,24 @@ namespace sqlite_orm { template using conc_t = binary_operator; + struct unary_minus_string { + serialize_result_type serialize() const { + return "-"; + } + }; + + /** + * Result of unary minus - operator + */ + template + struct unary_minus_t : unary_minus_string, arithmetic_t, negatable_t { + using argument_type = T; + + argument_type argument; + + unary_minus_t(argument_type argument_) : argument(std::move(argument_)) {} + }; + struct add_string { serialize_result_type serialize() const { return "+"; @@ -60,7 +78,7 @@ namespace sqlite_orm { }; /** - * Result of substitute - operator + * Result of substraction - operator */ template using sub_t = binary_operator; @@ -201,6 +219,11 @@ namespace sqlite_orm { return {std::move(l), std::move(r)}; } + template + constexpr internal::unary_minus_t minus(T t) { + return {std::move(t)}; + } + /** * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users */ diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 60cf5586..c616d906 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -718,8 +718,11 @@ namespace sqlite_orm { }; template - struct statement_serializer, void> { - using statement_type = bitwise_not_t; + struct statement_serializer< + T, + std::enable_if_t, + polyfill::is_specialization_of>::value>> { + using statement_type = T; template std::string operator()(const statement_type& expression, const Ctx& context) const { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index ff4507de..dc32f84f 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -4168,6 +4168,24 @@ namespace sqlite_orm { template using conc_t = binary_operator; + struct unary_minus_string { + serialize_result_type serialize() const { + return "-"; + } + }; + + /** + * Result of unary minus - operator + */ + template + struct unary_minus_t : unary_minus_string, arithmetic_t, negatable_t { + using argument_type = T; + + argument_type argument; + + unary_minus_t(argument_type argument_) : argument(std::move(argument_)) {} + }; + struct add_string { serialize_result_type serialize() const { return "+"; @@ -4187,7 +4205,7 @@ namespace sqlite_orm { }; /** - * Result of substitute - operator + * Result of substraction - operator */ template using sub_t = binary_operator; @@ -4328,6 +4346,11 @@ namespace sqlite_orm { return {std::move(l), std::move(r)}; } + template + constexpr internal::unary_minus_t minus(T t) { + return {std::move(t)}; + } + /** * Public interface for + operator. Example: `select(add(&User::age, 100));` => SELECT age + 100 FROM users */ @@ -8091,6 +8114,14 @@ namespace sqlite_orm { // Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace // to facilitate ADL (Argument Dependent Lookup) namespace internal { + template< + class T, + std::enable_if_t, is_operator_argument>::value, + bool> = true> + constexpr unary_minus_t> operator-(T arg) { + return {get_from_expression(std::forward(arg))}; + } + template, @@ -11474,6 +11505,11 @@ namespace sqlite_orm { using type = std::string; }; + template + struct column_result_t, void> { + using type = double; + }; + template struct column_result_t, void> { using type = double; @@ -19941,8 +19977,11 @@ namespace sqlite_orm { }; template - struct statement_serializer, void> { - using statement_type = bitwise_not_t; + struct statement_serializer< + T, + std::enable_if_t, + polyfill::is_specialization_of>::value>> { + using statement_type = T; template std::string operator()(const statement_type& expression, const Ctx& context) const { @@ -24287,6 +24326,9 @@ namespace sqlite_orm { template struct node_tuple, void> : node_tuple {}; + template + struct node_tuple, void> : node_tuple {}; + template struct node_tuple, void> : node_tuple {}; diff --git a/tests/statement_serializer_tests/arithmetic_operators.cpp b/tests/statement_serializer_tests/arithmetic_operators.cpp index 764b062b..842f9ec2 100644 --- a/tests/statement_serializer_tests/arithmetic_operators.cpp +++ b/tests/statement_serializer_tests/arithmetic_operators.cpp @@ -8,6 +8,15 @@ TEST_CASE("statement_serializer arithmetic operators") { internal::serializer_context> context{storage}; std::string value; decltype(value) expected; + SECTION("unary minus") { + SECTION("func") { + value = serialize(minus(20), context); + } + SECTION("operator") { + value = serialize(-c(20), context); + } + expected = "-20"; + } SECTION("add") { SECTION("func") { value = serialize(add(3, 5), context); @@ -55,12 +64,12 @@ TEST_CASE("statement_serializer arithmetic operators") { } SECTION("parentheses keeping order of precedence") { SECTION("1") { - value = serialize(c(4) + 5 + 3, context); - expected = "(4 + 5) + 3"; + value = serialize(c(4) + 5 + -c(3), context); + expected = "(4 + 5) + -3"; } SECTION("2") { - value = serialize(4 + (c(5) + 3), context); - expected = "4 + (5 + 3)"; + value = serialize(4 + -(c(5) + 3), context); + expected = "4 + -(5 + 3)"; } SECTION("3") { value = serialize(4 + c(5) * 3 + 1, context); diff --git a/tests/static_tests/column_result_t.cpp b/tests/static_tests/column_result_t.cpp index 25b19491..cbfef385 100644 --- a/tests/static_tests/column_result_t.cpp +++ b/tests/static_tests/column_result_t.cpp @@ -95,6 +95,8 @@ TEST_CASE("column_result_of_t") { runTest(all(&User::name)); runTest(conc(&User::name, &User::id)); runTest(c(&User::name) || &User::id); + runTest(minus(&User::id)); + runTest(-c(&User::id)); runTest(add(&User::id, 5)); runTest(c(&User::id) + 5); runTest(sub(&User::id, 5)); diff --git a/tests/static_tests/node_tuple.cpp b/tests/static_tests/node_tuple.cpp index 9f10491d..4a9a07bc 100644 --- a/tests/static_tests/node_tuple.cpp +++ b/tests/static_tests/node_tuple.cpp @@ -189,25 +189,28 @@ TEST_CASE("Node tuple") { using namespace internal; using CondTuple = node_tuple_t>; - static_assert(is_same>::value, "conc_t"); + STATIC_REQUIRE(is_same>::value); + + using MinusTuple = node_tuple_t>; + STATIC_REQUIRE(is_same>::value); using AddTuple = node_tuple_t>; - static_assert(is_same>::value, "add_t"); + STATIC_REQUIRE(is_same>::value); using SubTuple = node_tuple_t>; - static_assert(is_same>::value, "sub_t"); + STATIC_REQUIRE(is_same>::value); using MulTuple = node_tuple_t>; - static_assert(is_same>::value, "mul_t"); + STATIC_REQUIRE(is_same>::value); using DivTuple = node_tuple_t>; - static_assert(is_same>::value, "div_t"); + STATIC_REQUIRE(is_same>::value); using ModTuple = node_tuple_t>; - static_assert(is_same>::value, "mod_t"); + STATIC_REQUIRE(is_same>::value); using AssignTuple = node_tuple_t>; - static_assert(is_same>::value, "assign_t"); + STATIC_REQUIRE(is_same>::value); } SECTION("bitwise operator") { using namespace internal; diff --git a/tests/static_tests/operators_adl.cpp b/tests/static_tests/operators_adl.cpp index cdfdb63c..111358ab 100644 --- a/tests/static_tests/operators_adl.cpp +++ b/tests/static_tests/operators_adl.cpp @@ -28,6 +28,7 @@ using sqlite_orm::internal::less_or_equal_t; using sqlite_orm::internal::less_than_t; using sqlite_orm::internal::negated_condition_t; using sqlite_orm::internal::or_condition_t; +using sqlite_orm::internal::unary_minus_t; using sqlite_orm::polyfill::is_specialization_of_v; template @@ -68,6 +69,10 @@ void runTests(E expression) { STATIC_REQUIRE(is_specialization_of_v); STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); + STATIC_REQUIRE(is_specialization_of_v); STATIC_REQUIRE(is_specialization_of_v); STATIC_REQUIRE(is_specialization_of_v); From f51188c8ef5fd8d1059672876e536d79a3184170 Mon Sep 17 00:00:00 2001 From: stevenwdv Date: Mon, 2 Sep 2024 11:29:51 +0200 Subject: [PATCH 25/38] Fix `mapped_view` with condition Closes #1346 --- include/sqlite_orm/sqlite_orm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a710a5cc..7e18b652 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -15685,7 +15685,7 @@ namespace sqlite_orm { get_all_t expression; mapped_view(storage_type& storage, connection_ref conn, Args&&... args) : - storage(storage), connection(std::move(conn)), expression{std::forward(args)...} {} + storage(storage), connection(std::move(conn)), expression{{std::forward(args)...}} {} size_t size() const { return this->storage.template count(); From 2d089d4ce16f243d16c6ec0e6224816f9133d2fa Mon Sep 17 00:00:00 2001 From: stevenwdv Date: Tue, 3 Sep 2024 19:46:29 +0200 Subject: [PATCH 26/38] Also update mapped_view.h for #1346 --- dev/mapped_view.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/mapped_view.h b/dev/mapped_view.h index b2b61543..45f90c1c 100644 --- a/dev/mapped_view.h +++ b/dev/mapped_view.h @@ -37,7 +37,7 @@ namespace sqlite_orm { get_all_t expression; mapped_view(storage_type& storage, connection_ref conn, Args&&... args) : - storage(storage), connection(std::move(conn)), expression{std::forward(args)...} {} + storage(storage), connection(std::move(conn)), expression{{std::forward(args)...}} {} size_t size() const { return this->storage.template count(); From ce2a49125b613e693becd319cfda1626e31904f7 Mon Sep 17 00:00:00 2001 From: Vladislav Potachin Date: Wed, 18 Sep 2024 19:24:12 +0300 Subject: [PATCH 27/38] add setter and getter for max_page_count pragma --- dev/pragma.h | 8 ++++++++ include/sqlite_orm/sqlite_orm.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/dev/pragma.h b/dev/pragma.h index 8f9ab7d3..394af430 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -113,6 +113,14 @@ namespace sqlite_orm { this->set_pragma("auto_vacuum", value); } + int max_page_count() { + return this->get_pragma("max_page_count"); + } + + void max_page_count(int value) { + this->set_pragma("max_page_count", value); + } + std::vector integrity_check() { return this->get_pragma>("integrity_check"); } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 7e18b652..410b2c0a 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -16513,6 +16513,14 @@ namespace sqlite_orm { this->set_pragma("auto_vacuum", value); } + int max_page_count() { + return this->get_pragma("max_page_count"); + } + + void max_page_count(int value) { + this->set_pragma("max_page_count", value); + } + std::vector integrity_check() { return this->get_pragma>("integrity_check"); } From 1231e84a45bdb927118fd4ad66643d63d7f611cc Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Fri, 8 Nov 2024 20:01:55 +0500 Subject: [PATCH 28/38] added column_pointer support to fk --- dev/constraints.h | 6 ++ include/sqlite_orm/sqlite_orm.h | 7 +++ tests/constraints/foreign_key.cpp | 95 +++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) diff --git a/dev/constraints.h b/dev/constraints.h index 9a6a4188..e9cf651c 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -16,6 +16,7 @@ #include "error_code.h" #include "table_type_of.h" #include "type_printer.h" +#include "column_pointer.h" namespace sqlite_orm { @@ -375,6 +376,11 @@ namespace sqlite_orm { foreign_key_t, std::tuple> references(Rs... refs) { return {std::move(this->columns), {std::forward(refs)...}}; } + + template + foreign_key_t, std::tuple...>> references(Rs... refs) { + return {std::move(this->columns), {sqlite_orm::column(refs)...}}; + } }; #endif diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 410b2c0a..ff679345 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -3191,6 +3191,8 @@ namespace sqlite_orm { // #include "type_printer.h" +// #include "column_pointer.h" + namespace sqlite_orm { namespace internal { @@ -3549,6 +3551,11 @@ namespace sqlite_orm { foreign_key_t, std::tuple> references(Rs... refs) { return {std::move(this->columns), {std::forward(refs)...}}; } + + template + foreign_key_t, std::tuple...>> references(Rs... refs) { + return {std::move(this->columns), {sqlite_orm::column(refs)...}}; + } }; #endif diff --git a/tests/constraints/foreign_key.cpp b/tests/constraints/foreign_key.cpp index 5163baea..416d1a0d 100644 --- a/tests/constraints/foreign_key.cpp +++ b/tests/constraints/foreign_key.cpp @@ -128,4 +128,99 @@ TEST_CASE("Foreign key 2") { storage.update(t2); } + +TEST_CASE("Foreign key with inheritance") { + struct Person { + int id; + std::string name; + int age; + }; + + // Define derived class Student + struct Student : public Person { + std::string school_name; + + Student(int id, std::string name, int age, std::string school_name) : + Person{id, std::move(name), age}, school_name(std::move(school_name)) {} + }; + + // Define derived class Teacher + struct Teacher : public Person { + std::string subject; + double salary; + + Teacher(int id, std::string name, int age, std::string subject, double salary) : + Person{id, std::move(name), age}, subject(subject), salary(salary) {} + }; + + // Define Classroom class referencing Teacher and Student + struct Classroom { + int id; + int teacher_id; // Foreign key referencing Teacher + int student_id; // Foreign key referencing Student + std::string room_name; + }; + + auto storage = + make_storage("", + + // Define the Person table as a base, though it is not used directly + make_table("persons", + make_column("id", &Person::id, primary_key().autoincrement()), + make_column("name", &Person::name), + make_column("age", &Person::age)), + + // Define the Student table with foreign key inheritance + make_table("students", + make_column("id", &Student::id, primary_key()), + make_column("name", &Student::name), + make_column("age", &Student::age), + make_column("school_name", &Student::school_name)), + + // Define the Teacher table with foreign key inheritance + make_table("teachers", + make_column("id", &Teacher::id, primary_key()), + make_column("name", &Teacher::name), + make_column("age", &Teacher::age), + make_column("subject", &Teacher::subject), + make_column("salary", &Teacher::salary)), + + // Define the Classroom table with foreign keys to Teacher and Student + make_table("classrooms", + make_column("id", &Classroom::id, primary_key().autoincrement()), + make_column("teacher_id", &Classroom::teacher_id), + make_column("student_id", &Classroom::student_id), + make_column("room_name", &Classroom::room_name), + foreign_key(&Classroom::teacher_id).references(&Teacher::id), + foreign_key(&Classroom::student_id).references(&Student::id))); + // Sync schema (create tables) + storage.sync_schema(); + + // Create and insert a Teacher record + Teacher teacher{0, "Mr. Smith", 45, "Mathematics", 55000.00}; + int teacher_id = storage.insert(teacher); + + // Create and insert a Student record + Student student = {0, "Alice", 20, "High School"}; + int student_id = storage.insert(student); + + // try + // { + + // Create and insert a Classroom record with foreign keys + Classroom classroom = {0, teacher_id, student_id, "Room A"}; + storage.insert(classroom); + // } + // catch (std::exception& ex) + // { + // auto s = ex.what(); + // } + + // Fetch and display inserted Classroom with foreign key references + auto classrooms = storage.get_all(); + for(const auto& c: classrooms) { + // std::cout << "Classroom: " << c.room_name << ", Teacher ID: " << c.teacher_id + // << ", Student ID: " << c.student_id << "\n"; + } +} #endif From 9a7a7600e156403b8339112dca1c65d8fa32d634 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Fri, 8 Nov 2024 20:03:52 +0500 Subject: [PATCH 29/38] test simplified --- tests/constraints/foreign_key.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/constraints/foreign_key.cpp b/tests/constraints/foreign_key.cpp index 416d1a0d..203f6ea1 100644 --- a/tests/constraints/foreign_key.cpp +++ b/tests/constraints/foreign_key.cpp @@ -204,23 +204,8 @@ TEST_CASE("Foreign key with inheritance") { Student student = {0, "Alice", 20, "High School"}; int student_id = storage.insert(student); - // try - // { - // Create and insert a Classroom record with foreign keys Classroom classroom = {0, teacher_id, student_id, "Room A"}; storage.insert(classroom); - // } - // catch (std::exception& ex) - // { - // auto s = ex.what(); - // } - - // Fetch and display inserted Classroom with foreign key references - auto classrooms = storage.get_all(); - for(const auto& c: classrooms) { - // std::cout << "Classroom: " << c.room_name << ", Teacher ID: " << c.teacher_id - // << ", Student ID: " << c.student_id << "\n"; - } } #endif From e4b075bcc31f81669c8029841883e5656684e4d0 Mon Sep 17 00:00:00 2001 From: uuiid <957714080@qq.com> Date: Wed, 4 Dec 2024 15:47:10 +0800 Subject: [PATCH 30/38] fix: null() and not_null() will cause an error when running sync_schema(true) on a table with a foreign key constraint --- dev/implementations/table_definitions.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dev/implementations/table_definitions.h b/dev/implementations/table_definitions.h index c7f6177a..3bb5b314 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -25,10 +25,17 @@ namespace sqlite_orm { if(auto d = column.default_value()) { dft = std::move(*d); } + using constraints_tuple = decltype(column.constraints); + constexpr bool hasExplicitNull = + mpl::invoke_t>, constraints_tuple>::value; + constexpr bool hasExplicitNotNull = + mpl::invoke_t>, constraints_tuple>::value; res.emplace_back(-1, column.name, type_printer().print(), - column.is_not_null(), + !hasExplicitNull && !hasExplicitNotNull + ? column.is_not_null() + : (hasExplicitNull ? hasExplicitNull : hasExplicitNotNull), std::move(dft), column.template is(), column.template is()); From 2a430e1b22652728294f46e9e9d19758175a0b24 Mon Sep 17 00:00:00 2001 From: uuiid <957714080@qq.com> Date: Wed, 4 Dec 2024 16:12:15 +0800 Subject: [PATCH 31/38] run amalgamate.py and clang-format --- include/sqlite_orm/sqlite_orm.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index ff679345..2e88f2ee 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -23838,10 +23838,17 @@ namespace sqlite_orm { if(auto d = column.default_value()) { dft = std::move(*d); } + using constraints_tuple = decltype(column.constraints); + constexpr bool hasExplicitNull = + mpl::invoke_t>, constraints_tuple>::value; + constexpr bool hasExplicitNotNull = + mpl::invoke_t>, constraints_tuple>::value; res.emplace_back(-1, column.name, type_printer().print(), - column.is_not_null(), + !hasExplicitNull && !hasExplicitNotNull + ? column.is_not_null() + : (hasExplicitNull ? hasExplicitNull : hasExplicitNotNull), std::move(dft), column.template is(), column.template is()); From 6c9575262792a55662e9f67e2b03d90aa723f3f9 Mon Sep 17 00:00:00 2001 From: uuiid <957714080@qq.com> Date: Wed, 4 Dec 2024 17:07:20 +0800 Subject: [PATCH 32/38] add issue1357 test --- tests/unique_cases/issue1357.cpp | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/unique_cases/issue1357.cpp diff --git a/tests/unique_cases/issue1357.cpp b/tests/unique_cases/issue1357.cpp new file mode 100644 index 00000000..8934065b --- /dev/null +++ b/tests/unique_cases/issue1357.cpp @@ -0,0 +1,51 @@ +#include +#include + +#if SQLITE_VERSION_NUMBER >= 3006019 +using namespace sqlite_orm; + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + +TEST_CASE("issue1357") { + struct Employee { + int m_empno; + std::string m_ename; + std::string m_job; + std::optional m_mgr; + std::string m_hiredate; + double m_salary; + std::optional m_commission; + int m_depno; + }; + + struct Department { + int m_deptno; + std::string m_deptname; + std::string m_loc; + }; + + using namespace sqlite_orm; + + auto storage = make_storage("", + make_table("Emp", + make_column("empno", &Employee::m_empno, primary_key().autoincrement()), + make_column("ename", &Employee::m_ename), + make_column("job", &Employee::m_job), + make_column("mgr", &Employee::m_mgr), + make_column("hiredate", &Employee::m_hiredate), + make_column("salary", &Employee::m_salary), + make_column("comm", &Employee::m_commission), + make_column("depno", &Employee::m_depno), + foreign_key(&Employee::m_depno).references(&Department::m_deptno)), + make_table("Dept", + make_column("deptno", &Department::m_deptno, primary_key().autoincrement()), + make_column("deptname", &Department::m_deptname), + make_column("loc", &Department::m_loc, null()))); + storage.sync_schema(true); + storage.insert(Department{10, "Accounts", "New York"}); + storage.insert(Employee{1, "Paul", "Salesman", 2, "2002-02-12", 20000.0, 0.0, 1}); + storage.insert(Employee{2, "Allen", "Salesman", 2, "2002-02-12", 20000.0, 0.0, 1}); + REQUIRE_NOTHROW(storage.sync_schema(true)); +} +#endif +#endif From 5db03516fe6eb85abb6c874295ff63c1756bc4e8 Mon Sep 17 00:00:00 2001 From: uuiid <957714080@qq.com> Date: Wed, 4 Dec 2024 17:08:44 +0800 Subject: [PATCH 33/38] add issue1357.cpp to cmake file --- tests/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d3ebf450..99ebafa3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -144,6 +144,7 @@ add_executable(unit_tests unique_cases/issue86.cpp unique_cases/issue937.cpp unique_cases/issue663.cpp + unique_cases/issue1357.cpp get_all_custom_containers.cpp select_constraints_tests.cpp backup_tests.cpp From 886beac769f2c1970a96406ae77cfc09acaa83ff Mon Sep 17 00:00:00 2001 From: uuiid <957714080@qq.com> Date: Thu, 5 Dec 2024 09:20:15 +0800 Subject: [PATCH 34/38] fix: the issue of reverse results when checking null constraints --- dev/implementations/table_definitions.h | 2 +- include/sqlite_orm/sqlite_orm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/implementations/table_definitions.h b/dev/implementations/table_definitions.h index 3bb5b314..f68e21c9 100644 --- a/dev/implementations/table_definitions.h +++ b/dev/implementations/table_definitions.h @@ -35,7 +35,7 @@ namespace sqlite_orm { type_printer().print(), !hasExplicitNull && !hasExplicitNotNull ? column.is_not_null() - : (hasExplicitNull ? hasExplicitNull : hasExplicitNotNull), + : (hasExplicitNull ? !hasExplicitNull : hasExplicitNotNull), std::move(dft), column.template is(), column.template is()); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 2e88f2ee..8e953841 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -23848,7 +23848,7 @@ namespace sqlite_orm { type_printer().print(), !hasExplicitNull && !hasExplicitNotNull ? column.is_not_null() - : (hasExplicitNull ? hasExplicitNull : hasExplicitNotNull), + : (hasExplicitNull ? !hasExplicitNull : hasExplicitNotNull), std::move(dft), column.template is(), column.template is()); From 9d539a4fb273a579f9b3446081fbb0b2a722dc98 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Tue, 10 Dec 2024 22:41:59 +0500 Subject: [PATCH 35/38] added locking mode --- .vscode/launch.json | 29 +++++++ .vscode/settings.json | 78 +++++++++++++++++++ .vscode/tasks.json | 70 +++++++++++++++++ dev/error_code.h | 1 + dev/journal_mode.h | 24 +++--- dev/locking_mode.h | 40 ++++++++++ dev/pragma.h | 35 ++++++--- dev/row_extractor.h | 32 +++++++- include/sqlite_orm/sqlite_orm.h | 133 ++++++++++++++++++++++++++------ tests/pragma_tests.cpp | 17 +++- 10 files changed, 412 insertions(+), 47 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 dev/locking_mode.h diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..810d989b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run unit_tests", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/tests/unit_tests", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "environment": [], + "externalConsole": false, + "MIMode": "lldb" + }, + { + "name": "Run amalgamate script", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/third_party/amalgamate/amalgamate.py", + "args": [ + "-c", "${workspaceFolder}/third_party/amalgamate/config.json", + "-s", "${workspaceFolder}" + ], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..1e6e6100 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,78 @@ +{ + "files.associations": { + "*.c": "c", + "string": "cpp", + "__bit_reference": "cpp", + "__hash_table": "cpp", + "__split_buffer": "cpp", + "__tree": "cpp", + "array": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "initializer_list": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "span": "cpp", + "string_view": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "chrono": "cpp", + "format": "cpp", + "ranges": "cpp", + "text_encoding": "cpp", + "__locale": "cpp", + "__node_handle": "cpp", + "__verbose_abort": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "memory": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "locale": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "print": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "tuple": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "variant": "cpp", + "algorithm": "cpp", + "clocale": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "compare": "cpp", + "concepts": "cpp", + "exception": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory_resource": "cpp", + "random": "cpp", + "system_error": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "__config": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..42cee96e --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,70 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Run unit_tests", + "type": "shell", + "command": "${workspaceFolder}/build/tests/unit_tests", + "options": { + "cwd": "${workspaceFolder}/build/tests" + }, + "group": { + "kind": "test", + "isDefault": true + }, + "problemMatcher": [], + "presentation": { + "reveal": "always", + "panel": "shared" + } + }, + { + "label": "Run amalgamate script", + "type": "shell", + "command": "python3", + "args": [ + "third_party/amalgamate/amalgamate.py", + "-c", + "third_party/amalgamate/config.json", + "-s", + "." + ], + "group": { + "kind": "build", + "isDefault": false + }, + "problemMatcher": [], + "presentation": { + "reveal": "always", + "panel": "shared" + } + }, + { + "label": "Run clang-format", + "type": "shell", + "command": "clang-format", + "args": [ + "-i", + "-style=file", + "include/sqlite_orm/*.h", + "tests/*.cpp", + "tests/*/*.cpp", + "tests/*/*/*.cpp", + "dev/*.h", + "dev/*/*.h", + "tests/*/*.h", + "examples/*.cpp", + "examples/*/src/*.cpp" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "problemMatcher": [], + "presentation": { + "reveal": "always", + "panel": "shared" + } + } + ] +} \ No newline at end of file diff --git a/dev/error_code.h b/dev/error_code.h index 662de285..58568621 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -28,6 +28,7 @@ namespace sqlite_orm { cannot_start_a_transaction_within_a_transaction, no_active_transaction, incorrect_journal_mode_string, + incorrect_locking_mode_string, invalid_collate_argument_enum, failed_to_init_a_backup, unknown_member_value, diff --git a/dev/journal_mode.h b/dev/journal_mode.h index b72defda..5432225b 100644 --- a/dev/journal_mode.h +++ b/dev/journal_mode.h @@ -7,6 +7,8 @@ #include // std::transform #include // std::toupper +#include "serialize_result_type.h" + #if defined(_WINNT_) // DELETE is a macro defined in the Windows SDK (winnt.h) #pragma push_macro("DELETE") @@ -33,8 +35,8 @@ namespace sqlite_orm { namespace internal { - inline const std::string& to_string(journal_mode j) { - static std::string res[] = { + inline const serialize_result_type& to_string(journal_mode value) { + static const std::array res = { "DELETE", "TRUNCATE", "PERSIST", @@ -42,15 +44,15 @@ namespace sqlite_orm { "WAL", "OFF", }; - return res[static_cast(j)]; + return res.at(static_cast(value)); } - 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) { + inline std::pair journal_mode_from_string(const std::string& string) { + std::string upperString; + std::transform(string.begin(), string.end(), std::back_inserter(upperString), [](char c) { return static_cast(std::toupper(static_cast(c))); }); - static std::array all = {{ + static const std::array allValues = {{ journal_mode::DELETE, journal_mode::TRUNCATE, journal_mode::PERSIST, @@ -58,12 +60,12 @@ namespace sqlite_orm { journal_mode::WAL, journal_mode::OFF, }}; - for(auto j: all) { - if(to_string(j) == upper_str) { - return std::make_unique(j); + for(auto journalMode: allValues) { + if(to_string(journalMode) == upperString) { + return {true, journalMode}; } } - return {}; + return {false, journal_mode::OFF}; } } } diff --git a/dev/locking_mode.h b/dev/locking_mode.h new file mode 100644 index 00000000..1f40265a --- /dev/null +++ b/dev/locking_mode.h @@ -0,0 +1,40 @@ +#include // std::array +#include // std::string +#include // std::pair +#include // std::back_inserter + +#include "serialize_result_type.h" + +namespace sqlite_orm { + enum class locking_mode : signed char { + NORMAL = 0, + EXCLUSIVE = 1, + }; + + namespace internal { + inline const serialize_result_type& to_string(locking_mode value) { + static const std::array res = { + "NORMAL", + "EXCLUSIVE", + }; + return res.at(static_cast(value)); + } + + inline std::pair locking_mode_from_string(const std::string& string) { + std::string upperString; + std::transform(string.begin(), string.end(), std::back_inserter(upperString), [](char c) { + return static_cast(std::toupper(static_cast(c))); + }); + static const std::array allValues = {{ + locking_mode::NORMAL, + locking_mode::EXCLUSIVE, + }}; + for(auto lockingMode: allValues) { + if(to_string(lockingMode) == upperString) { + return {true, lockingMode}; + } + } + return {false, locking_mode::NORMAL}; + } + } +} \ No newline at end of file diff --git a/dev/pragma.h b/dev/pragma.h index 394af430..3d608dbb 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -12,6 +12,7 @@ #include "error_code.h" #include "row_extractor.h" #include "journal_mode.h" +#include "locking_mode.h" #include "connection_holder.h" #include "util.h" #include "serializing_util.h" @@ -63,6 +64,14 @@ namespace sqlite_orm { return this->get_pragma("busy_timeout"); } + sqlite_orm::locking_mode locking_mode() { + return this->get_pragma("locking_mode"); + } + + void locking_mode(sqlite_orm::locking_mode value) { + this->set_pragma("locking_mode", value); + } + sqlite_orm::journal_mode journal_mode() { return this->get_pragma("journal_mode"); } @@ -231,23 +240,29 @@ namespace sqlite_orm { */ template void set_pragma(const std::string& name, const T& value, sqlite3* db = nullptr) { - auto con = this->get_connection(); - if(!db) { - db = con.get(); - } std::stringstream ss; - ss << "PRAGMA " << name << " = " << value << std::flush; - perform_void_exec(db, ss.str()); + ss << "PRAGMA " << name << " = " << value; + this->set_pragma_impl(ss.str(), db); } void set_pragma(const std::string& name, const sqlite_orm::journal_mode& value, sqlite3* db = nullptr) { + std::stringstream ss; + ss << "PRAGMA " << name << " = " << to_string(value); + this->set_pragma_impl(ss.str(), db); + } + + void set_pragma(const std::string& name, const sqlite_orm::locking_mode& value, sqlite3* db = nullptr) { + std::stringstream ss; + ss << "PRAGMA " << name << " = " << to_string(value); + this->set_pragma_impl(ss.str(), db); + } + + void set_pragma_impl(const std::string& query, sqlite3* db = nullptr) { auto con = this->get_connection(); - if(!db) { + if(db == nullptr) { db = con.get(); } - std::stringstream ss; - ss << "PRAGMA " << name << " = " << to_string(value) << std::flush; - perform_void_exec(db, ss.str()); + perform_void_exec(db, query); } }; } diff --git a/dev/row_extractor.h b/dev/row_extractor.h index e18598af..21fcabf7 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -25,6 +25,7 @@ #include "arithmetic_tag.h" #include "pointer_value.h" #include "journal_mode.h" +#include "locking_mode.h" #include "error_code.h" #include "is_std_ptr.h" #include "type_traits.h" @@ -404,6 +405,32 @@ namespace sqlite_orm { } }; + /** + * Specialization for locking_mode. + */ + template<> + struct row_extractor { + locking_mode extract(const char* columnText) const { + if(columnText) { + auto resultPair = internal::locking_mode_from_string(columnText); + if(resultPair.first) { + return resultPair.second; + } else { + throw std::system_error{orm_error_code::incorrect_locking_mode_string}; + } + } else { + throw std::system_error{orm_error_code::incorrect_locking_mode_string}; + } + } + + locking_mode extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); + return this->extract(cStr); + } + + locking_mode extract(sqlite3_value* value) const = delete; + }; + /** * Specialization for journal_mode. */ @@ -411,8 +438,9 @@ namespace sqlite_orm { 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); + auto resultPair = internal::journal_mode_from_string(columnText); + if(resultPair.first) { + return resultPair.second; } else { throw std::system_error{orm_error_code::incorrect_journal_mode_string}; } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 8e953841..d7d9085f 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -2852,6 +2852,7 @@ namespace sqlite_orm { cannot_start_a_transaction_within_a_transaction, no_active_transaction, incorrect_journal_mode_string, + incorrect_locking_mode_string, invalid_collate_argument_enum, failed_to_init_a_backup, unknown_member_value, @@ -12618,6 +12619,8 @@ namespace sqlite_orm { #include // std::transform #include // std::toupper +// #include "serialize_result_type.h" + #if defined(_WINNT_) // DELETE is a macro defined in the Windows SDK (winnt.h) #pragma push_macro("DELETE") @@ -12644,8 +12647,8 @@ namespace sqlite_orm { namespace internal { - inline const std::string& to_string(journal_mode j) { - static std::string res[] = { + inline const serialize_result_type& to_string(journal_mode value) { + static const std::array res = { "DELETE", "TRUNCATE", "PERSIST", @@ -12653,15 +12656,15 @@ namespace sqlite_orm { "WAL", "OFF", }; - return res[static_cast(j)]; + return res.at(static_cast(value)); } - 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) { + inline std::pair journal_mode_from_string(const std::string& string) { + std::string upperString; + std::transform(string.begin(), string.end(), std::back_inserter(upperString), [](char c) { return static_cast(std::toupper(static_cast(c))); }); - static std::array all = {{ + static const std::array allValues = {{ journal_mode::DELETE, journal_mode::TRUNCATE, journal_mode::PERSIST, @@ -12669,12 +12672,12 @@ namespace sqlite_orm { journal_mode::WAL, journal_mode::OFF, }}; - for(auto j: all) { - if(to_string(j) == upper_str) { - return std::make_unique(j); + for(auto journalMode: allValues) { + if(to_string(journalMode) == upperString) { + return {true, journalMode}; } } - return {}; + return {false, journal_mode::OFF}; } } } @@ -12722,6 +12725,47 @@ namespace sqlite_orm { // #include "journal_mode.h" +// #include "locking_mode.h" +#include // std::array +#include // std::string +#include // std::pair +#include // std::back_inserter + +// #include "serialize_result_type.h" + +namespace sqlite_orm { + enum class locking_mode : signed char { + NORMAL = 0, + EXCLUSIVE = 1, + }; + + namespace internal { + inline const serialize_result_type& to_string(locking_mode value) { + static const std::array res = { + "NORMAL", + "EXCLUSIVE", + }; + return res.at(static_cast(value)); + } + + inline std::pair locking_mode_from_string(const std::string& string) { + std::string upperString; + std::transform(string.begin(), string.end(), std::back_inserter(upperString), [](char c) { + return static_cast(std::toupper(static_cast(c))); + }); + static const std::array allValues = {{ + locking_mode::NORMAL, + locking_mode::EXCLUSIVE, + }}; + for(auto lockingMode: allValues) { + if(to_string(lockingMode) == upperString) { + return {true, lockingMode}; + } + } + return {false, locking_mode::NORMAL}; + } + } +} // #include "error_code.h" // #include "is_std_ptr.h" @@ -13103,6 +13147,32 @@ namespace sqlite_orm { } }; + /** + * Specialization for locking_mode. + */ + template<> + struct row_extractor { + locking_mode extract(const char* columnText) const { + if(columnText) { + auto resultPair = internal::locking_mode_from_string(columnText); + if(resultPair.first) { + return resultPair.second; + } else { + throw std::system_error{orm_error_code::incorrect_locking_mode_string}; + } + } else { + throw std::system_error{orm_error_code::incorrect_locking_mode_string}; + } + } + + locking_mode extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); + return this->extract(cStr); + } + + locking_mode extract(sqlite3_value* value) const = delete; + }; + /** * Specialization for journal_mode. */ @@ -13110,8 +13180,9 @@ namespace sqlite_orm { 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); + auto resultPair = internal::journal_mode_from_string(columnText); + if(resultPair.first) { + return resultPair.second; } else { throw std::system_error{orm_error_code::incorrect_journal_mode_string}; } @@ -15978,6 +16049,8 @@ namespace sqlite_orm { // #include "journal_mode.h" +// #include "locking_mode.h" + // #include "connection_holder.h" // #include "util.h" @@ -16470,6 +16543,14 @@ namespace sqlite_orm { return this->get_pragma("busy_timeout"); } + sqlite_orm::locking_mode locking_mode() { + return this->get_pragma("locking_mode"); + } + + void locking_mode(sqlite_orm::locking_mode value) { + this->set_pragma("locking_mode", value); + } + sqlite_orm::journal_mode journal_mode() { return this->get_pragma("journal_mode"); } @@ -16638,23 +16719,29 @@ namespace sqlite_orm { */ template void set_pragma(const std::string& name, const T& value, sqlite3* db = nullptr) { - auto con = this->get_connection(); - if(!db) { - db = con.get(); - } std::stringstream ss; - ss << "PRAGMA " << name << " = " << value << std::flush; - perform_void_exec(db, ss.str()); + ss << "PRAGMA " << name << " = " << value; + this->set_pragma_impl(ss.str(), db); } void set_pragma(const std::string& name, const sqlite_orm::journal_mode& value, sqlite3* db = nullptr) { + std::stringstream ss; + ss << "PRAGMA " << name << " = " << to_string(value); + this->set_pragma_impl(ss.str(), db); + } + + void set_pragma(const std::string& name, const sqlite_orm::locking_mode& value, sqlite3* db = nullptr) { + std::stringstream ss; + ss << "PRAGMA " << name << " = " << to_string(value); + this->set_pragma_impl(ss.str(), db); + } + + void set_pragma_impl(const std::string& query, sqlite3* db = nullptr) { auto con = this->get_connection(); - if(!db) { + if(db == nullptr) { db = con.get(); } - std::stringstream ss; - ss << "PRAGMA " << name << " = " << to_string(value) << std::flush; - perform_void_exec(db, ss.str()); + perform_void_exec(db, query); } }; } diff --git a/tests/pragma_tests.cpp b/tests/pragma_tests.cpp index 21298643..21bcd76c 100644 --- a/tests/pragma_tests.cpp +++ b/tests/pragma_tests.cpp @@ -20,7 +20,22 @@ TEST_CASE("recursive_triggers") { REQUIRE(result); } -TEST_CASE("Journal mode") { +TEST_CASE("locking_mode") { + auto storage = make_storage(""); + + SECTION("EXCLUSIVE") { + storage.pragma.locking_mode(locking_mode::EXCLUSIVE); + const auto result = storage.pragma.locking_mode(); + REQUIRE(result == locking_mode::EXCLUSIVE); + } + SECTION("NORMAL") { + storage.pragma.locking_mode(locking_mode::NORMAL); + const auto result = storage.pragma.locking_mode(); + REQUIRE(result == locking_mode::NORMAL); + } +} + +TEST_CASE("journal_mode") { auto filename = "journal_mode.sqlite"; ::remove(filename); auto storage = make_storage(filename); From a6d8df0e945ca3a9b430c3b1e1ca2880e6b6a2c9 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Sat, 14 Dec 2024 21:38:06 +0500 Subject: [PATCH 36/38] added any example --- .vscode/settings.json | 3 +- examples/CMakeLists.txt | 23 ++++- examples/any.cpp | 155 ++++++++++++++++++++++++++++++++++ examples/blob_binding.cpp | 1 - examples/generated_column.cpp | 9 +- 5 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 examples/any.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 1e6e6100..7216b3ba 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -73,6 +73,7 @@ "system_error": "cpp", "type_traits": "cpp", "utility": "cpp", - "__config": "cpp" + "__config": "cpp", + "any": "cpp" } } \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c9268a7c..96621736 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,11 +1,32 @@ -cmake_minimum_required (VERSION 3.16) +cmake_minimum_required(VERSION 3.16) # note: find_package(SQLite3 REQUIRED) already done in top-level CMakeLists +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + file(GLOB files "*.cpp") + +set(run_example_targets) + foreach(file ${files}) get_filename_component(file_basename ${file} NAME_WE) + add_executable(${file_basename} ${file}) # note: sqlite3 already linked in top-level CMakeLists + target_link_libraries(${file_basename} PRIVATE sqlite_orm) + + add_custom_target(run_${file_basename} + COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${file_basename} + DEPENDS ${file_basename} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Running example: ${file_basename}" + VERBATIM + ) + + list(APPEND run_example_targets run_${file_basename}) endforeach() + +add_custom_target(run_all_examples + DEPENDS ${run_example_targets} +) diff --git a/examples/any.cpp b/examples/any.cpp new file mode 100644 index 00000000..f49ba955 --- /dev/null +++ b/examples/any.cpp @@ -0,0 +1,155 @@ +/** + * This example demonstrates how to use std::any as a mapped type. + * It is a bit more complex than the other examples, because it + * uses custom row_extractor and statement_binder. + * Please note that this implementation is not the one and only + * option of implementation of std:any bindong to sqlite_orm. + * It is just an example of how to use std::any as a mapped type. + * Implementation is based on 5 types SQLite supports: + * NULL, INTEGER, REAL, TEXT, BLOB. + * NULL is mapped to std::nullopt. + * INTEGER is mapped to int. + * REAL is mapped to double. + * TEXT is mapped to std::string. + * BLOB is mapped to std::vector. +*/ +#include +#include +#include +#include + +using namespace sqlite_orm; +using std::cout; +using std::endl; + +namespace sqlite_orm { + + template<> + struct row_extractor { + std::any extract(sqlite3_stmt* stmt, int columnIndex) const { + const int type = sqlite3_column_type(stmt, columnIndex); + switch(type) { + case SQLITE_NULL: + return std::nullopt; + case SQLITE_INTEGER: + return sqlite3_column_int(stmt, columnIndex); + case SQLITE_FLOAT: + return sqlite3_column_double(stmt, columnIndex); + case SQLITE_TEXT: { + const unsigned char* text = sqlite3_column_text(stmt, columnIndex); + return std::string(reinterpret_cast(text)); + } + case SQLITE_BLOB: { + const void* blob = sqlite3_column_blob(stmt, columnIndex); + const int size = sqlite3_column_bytes(stmt, columnIndex); + return std::vector(reinterpret_cast(blob), + reinterpret_cast(blob) + size); + } + default: + throw std::runtime_error("Unsupported SQLite column type for std::any"); + } + } + + std::any extract(sqlite3_value* value) const { + const int type = sqlite3_value_type(value); + switch(type) { + case SQLITE_NULL: + return std::nullopt; + case SQLITE_INTEGER: + return sqlite3_value_int(value); + case SQLITE_FLOAT: + return sqlite3_value_double(value); + case SQLITE_TEXT: { + const unsigned char* text = sqlite3_value_text(value); + return std::string(reinterpret_cast(text)); + } + case SQLITE_BLOB: { + const void* blob = sqlite3_value_blob(value); + const int size = sqlite3_value_bytes(value); + return std::vector(reinterpret_cast(blob), + reinterpret_cast(blob) + size); // Handle BLOB + } + default: + throw std::runtime_error("Unsupported SQLite value type for std::any"); + } + } + }; + + template<> + struct statement_binder { + int bind(sqlite3_stmt* stmt, int index, const std::any& value) const { + if(!value.has_value()) { + return sqlite3_bind_null(stmt, index); + } + + if(value.type() == typeid(int)) { + return sqlite3_bind_int(stmt, index, std::any_cast(value)); + } else if(value.type() == typeid(double)) { + return sqlite3_bind_double(stmt, index, std::any_cast(value)); + } else if(value.type() == typeid(std::string)) { + const auto& text = std::any_cast(value); + return sqlite3_bind_text(stmt, index, text.c_str(), static_cast(text.size()), SQLITE_TRANSIENT); + } else if(value.type() == typeid(std::vector)) { + const auto& blob = std::any_cast>(value); + return sqlite3_bind_blob(stmt, index, blob.data(), static_cast(blob.size()), SQLITE_TRANSIENT); + } + + return SQLITE_MISMATCH; + } + }; + + template<> + struct type_printer : public text_printer {}; + + template<> + struct field_printer { + std::string operator()(const std::any& value) const { + if(!value.has_value()) { + return "NULL"; + } + + if(value.type() == typeid(int)) { + return std::to_string(std::any_cast(value)); + } else if(value.type() == typeid(double)) { + return std::to_string(std::any_cast(value)); + } else if(value.type() == typeid(std::string)) { + return std::any_cast(value); + } else if(value.type() == typeid(std::vector)) { + const auto& blob = std::any_cast>(value); + std::ostringstream oss; + oss << "0x"; + for(unsigned char c: blob) { + oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(c); + } + return oss.str(); + } + + throw std::runtime_error("Unsupported type in std::any field_printer"); + } + }; +} + +int main() { + struct Value { + int id = 0; + std::any value; + }; + auto filename = "any.sqlite"; + ::remove(filename); + auto storage = make_storage( + filename, + make_table("test", make_column("id", &Value::id, primary_key()), make_column("value", &Value::value))); + storage.sync_schema(); + storage.replace(Value{1, std::any{1}}); + storage.replace(Value{2, std::any{2.5}}); + storage.replace(Value{3, std::any{std::string("Hello, world!")}}); + storage.replace( + Value{4, std::any{std::vector{'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'}}}); + + cout << "Test:" << endl; + for(auto& test: storage.iterate()) { + cout << storage.dump(test) << endl; + } + cout << endl; + return 0; +} diff --git a/examples/blob_binding.cpp b/examples/blob_binding.cpp index af23f5e7..a4caaf8e 100644 --- a/examples/blob_binding.cpp +++ b/examples/blob_binding.cpp @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/examples/generated_column.cpp b/examples/generated_column.cpp index 699cf396..7a3a0ef3 100644 --- a/examples/generated_column.cpp +++ b/examples/generated_column.cpp @@ -6,12 +6,17 @@ #include #if SQLITE_VERSION_NUMBER >= 3031000 +#define ENABLE_THIS_EXAMPLE +#endif +#ifdef ENABLE_THIS_EXAMPLE using namespace sqlite_orm; using std::cout; using std::endl; +#endif // ENABLE_THIS_EXAMPLE int main() { +#ifdef ENABLE_THIS_EXAMPLE struct Product { int id = 0; std::string name; @@ -57,8 +62,6 @@ int main() { cout << storage.dump(product) << endl; } cout << endl; - +#endif // ENABLE_THIS_EXAMPLE return 0; } - -#endif // SQLITE_VERSION_NUMBER >= 3031000 From 49f5e52439b950d35e428548512720c9466716f0 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Mon, 16 Dec 2024 18:15:41 +0500 Subject: [PATCH 37/38] added pragma once --- dev/locking_mode.h | 2 ++ include/sqlite_orm/sqlite_orm.h | 1 + 2 files changed, 3 insertions(+) diff --git a/dev/locking_mode.h b/dev/locking_mode.h index 1f40265a..52257d2f 100644 --- a/dev/locking_mode.h +++ b/dev/locking_mode.h @@ -1,3 +1,5 @@ +#pragma once + #include // std::array #include // std::string #include // std::pair diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index d7d9085f..e7709575 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -12726,6 +12726,7 @@ namespace sqlite_orm { // #include "journal_mode.h" // #include "locking_mode.h" + #include // std::array #include // std::string #include // std::pair From 4d4a76c4d4a3aed1997070cd079abd18db8b0f29 Mon Sep 17 00:00:00 2001 From: Juan Dent Date: Fri, 20 Dec 2024 12:50:51 -0600 Subject: [PATCH 38/38] Testing clang-format 17.0.1 --- dev/util.h | 1 + include/sqlite_orm/sqlite_orm.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/dev/util.h b/dev/util.h index f162eeda..68b72dfd 100644 --- a/dev/util.h +++ b/dev/util.h @@ -5,6 +5,7 @@ #include // std::move #include "error_code.h" +// Before clang-format 17 namespace sqlite_orm { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index e7709575..ca77365b 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13447,6 +13447,8 @@ namespace sqlite_orm { // #include "error_code.h" +// Before clang-format 17 + namespace sqlite_orm { /**