From 3f6f6ebe10f079409553e6e79626369368711e67 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 26 Sep 2025 20:08:34 +0300 Subject: [PATCH 1/7] Renamed `storage_t<>::sync_table()` -> `storage_t<>::sync_dbo()` --- dev/implementations/storage_definitions.h | 6 +++--- dev/storage.h | 10 +++++----- include/sqlite_orm/sqlite_orm.h | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/dev/implementations/storage_definitions.h b/dev/implementations/storage_definitions.h index 2bb22103f..74a180141 100644 --- a/dev/implementations/storage_definitions.h +++ b/dev/implementations/storage_definitions.h @@ -24,9 +24,9 @@ namespace sqlite_orm { namespace internal { template template> - sync_schema_result storage_t::sync_table([[maybe_unused]] const Table& table, - [[maybe_unused]] sqlite3* db, - [[maybe_unused]] bool preserve) { + sync_schema_result storage_t::sync_dbo([[maybe_unused]] const Table& table, + [[maybe_unused]] sqlite3* db, + [[maybe_unused]] bool preserve) { if constexpr ( #ifdef SQLITE_ENABLE_DBSTAT_VTAB std::is_same, dbstat>::value || diff --git a/dev/storage.h b/dev/storage.h index e4f39cca5..080d4d577 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1214,7 +1214,7 @@ namespace sqlite_orm { } template - sync_schema_result sync_table(const virtual_table_t& virtualTable, sqlite3* db, bool) { + sync_schema_result sync_dbo(const virtual_table_t& virtualTable, sqlite3* db, bool) { using context_t = serializer_context; const auto res = sync_schema_result::already_in_sync; @@ -1225,7 +1225,7 @@ namespace sqlite_orm { } template - sync_schema_result sync_table(const index_t& index, sqlite3* db, bool) { + sync_schema_result sync_dbo(const index_t& index, sqlite3* db, bool) { using context_t = serializer_context; const auto res = sync_schema_result::already_in_sync; @@ -1236,7 +1236,7 @@ namespace sqlite_orm { } template - sync_schema_result sync_table(const trigger_t& trigger, sqlite3* db, bool) { + sync_schema_result sync_dbo(const trigger_t& trigger, sqlite3* db, bool) { using context_t = serializer_context; const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly @@ -1247,7 +1247,7 @@ namespace sqlite_orm { } template = true> - sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); + sync_schema_result sync_dbo(const Table& table, sqlite3* db, bool preserve); template = true> sync_schema_result sync_regular_table(const Table& table, sqlite3* db, bool preserve); @@ -1353,7 +1353,7 @@ namespace sqlite_orm { 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); + sync_schema_result status = this->sync_dbo(schemaObject, db, preserve); result.emplace(schemaObject.name, status); }); return result; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 214b3559a..a524ed7ea 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -24328,7 +24328,7 @@ namespace sqlite_orm { } template - sync_schema_result sync_table(const virtual_table_t& virtualTable, sqlite3* db, bool) { + sync_schema_result sync_dbo(const virtual_table_t& virtualTable, sqlite3* db, bool) { using context_t = serializer_context; const auto res = sync_schema_result::already_in_sync; @@ -24339,7 +24339,7 @@ namespace sqlite_orm { } template - sync_schema_result sync_table(const index_t& index, sqlite3* db, bool) { + sync_schema_result sync_dbo(const index_t& index, sqlite3* db, bool) { using context_t = serializer_context; const auto res = sync_schema_result::already_in_sync; @@ -24350,7 +24350,7 @@ namespace sqlite_orm { } template - sync_schema_result sync_table(const trigger_t& trigger, sqlite3* db, bool) { + sync_schema_result sync_dbo(const trigger_t& trigger, sqlite3* db, bool) { using context_t = serializer_context; const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly @@ -24361,7 +24361,7 @@ namespace sqlite_orm { } template = true> - sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); + sync_schema_result sync_dbo(const Table& table, sqlite3* db, bool preserve); template = true> sync_schema_result sync_regular_table(const Table& table, sqlite3* db, bool preserve); @@ -24467,7 +24467,7 @@ namespace sqlite_orm { 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); + sync_schema_result status = this->sync_dbo(schemaObject, db, preserve); result.emplace(schemaObject.name, status); }); return result; @@ -25416,9 +25416,9 @@ namespace sqlite_orm { namespace internal { template template> - sync_schema_result storage_t::sync_table([[maybe_unused]] const Table& table, - [[maybe_unused]] sqlite3* db, - [[maybe_unused]] bool preserve) { + sync_schema_result storage_t::sync_dbo([[maybe_unused]] const Table& table, + [[maybe_unused]] sqlite3* db, + [[maybe_unused]] bool preserve) { if constexpr ( #ifdef SQLITE_ENABLE_DBSTAT_VTAB std::is_same, dbstat>::value || From 33277776a67b94defcd318bb0d6b02ad0269b759 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 26 Sep 2025 20:10:38 +0300 Subject: [PATCH 2/7] Used `c_str()` instead of `data()` for executing SQL string `c_str()` expresses intent better than `data()` --- dev/storage.h | 6 +++--- dev/storage_base.h | 12 ++++++------ include/sqlite_orm/sqlite_orm.h | 18 +++++++++--------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index 080d4d577..f7ef42d95 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1220,7 +1220,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; const auto sql = serialize(virtualTable, context); - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); return res; } @@ -1231,7 +1231,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; const auto sql = serialize(index, context); - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); return res; } @@ -1242,7 +1242,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly context_t context{this->db_objects}; const auto sql = serialize(trigger, context); - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); return res; } diff --git a/dev/storage_base.h b/dev/storage_base.h index b60ede12a..ecb7417dc 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -156,7 +156,7 @@ namespace sqlite_orm { << streaming_identifier(newName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); } /** @@ -745,7 +745,7 @@ namespace sqlite_orm { void begin_transaction_internal(const std::string& sql) { this->connection->retain(); sqlite3* db = this->connection->get(); - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); } connection_ref get_connection() { @@ -781,7 +781,7 @@ namespace sqlite_orm { ss << "PRAGMA foreign_keys = " << value << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); } bool foreign_keys(sqlite3* db) { @@ -1004,7 +1004,7 @@ namespace sqlite_orm { ss << ' ' << streaming_identifier(tableName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); } void drop_index_internal(const std::string& indexName, bool ifExists) { @@ -1019,7 +1019,7 @@ namespace sqlite_orm { sql = ss.str(); } auto connection = this->get_connection(); - this->executor.perform_void_exec(connection.get(), sql.data()); + this->executor.perform_void_exec(connection.get(), sql.c_str()); } void drop_trigger_internal(const std::string& triggerName, bool ifExists) { @@ -1034,7 +1034,7 @@ namespace sqlite_orm { sql = ss.str(); } auto connection = this->get_connection(); - this->executor.perform_void_exec(connection.get(), sql.data()); + this->executor.perform_void_exec(connection.get(), sql.c_str()); } static int diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a524ed7ea..8496e4e5e 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -18274,7 +18274,7 @@ namespace sqlite_orm { << streaming_identifier(newName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); } /** @@ -18863,7 +18863,7 @@ namespace sqlite_orm { void begin_transaction_internal(const std::string& sql) { this->connection->retain(); sqlite3* db = this->connection->get(); - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); } connection_ref get_connection() { @@ -18899,7 +18899,7 @@ namespace sqlite_orm { ss << "PRAGMA foreign_keys = " << value << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); } bool foreign_keys(sqlite3* db) { @@ -19122,7 +19122,7 @@ namespace sqlite_orm { ss << ' ' << streaming_identifier(tableName) << std::flush; sql = ss.str(); } - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); } void drop_index_internal(const std::string& indexName, bool ifExists) { @@ -19137,7 +19137,7 @@ namespace sqlite_orm { sql = ss.str(); } auto connection = this->get_connection(); - this->executor.perform_void_exec(connection.get(), sql.data()); + this->executor.perform_void_exec(connection.get(), sql.c_str()); } void drop_trigger_internal(const std::string& triggerName, bool ifExists) { @@ -19152,7 +19152,7 @@ namespace sqlite_orm { sql = ss.str(); } auto connection = this->get_connection(); - this->executor.perform_void_exec(connection.get(), sql.data()); + this->executor.perform_void_exec(connection.get(), sql.c_str()); } static int @@ -24334,7 +24334,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; const auto sql = serialize(virtualTable, context); - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); return res; } @@ -24345,7 +24345,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; context_t context{this->db_objects}; const auto sql = serialize(index, context); - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); return res; } @@ -24356,7 +24356,7 @@ namespace sqlite_orm { const auto res = sync_schema_result::already_in_sync; // TODO Change accordingly context_t context{this->db_objects}; const auto sql = serialize(trigger, context); - this->executor.perform_void_exec(db, sql.data()); + this->executor.perform_void_exec(db, sql.c_str()); return res; } From c741a95d68111ad7ba6ddb9d5c1ae771eb01209c Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 26 Sep 2025 20:15:10 +0300 Subject: [PATCH 3/7] Alias template that filters columns of a table Also increased unit test coverage. --- dev/schema/column.h | 3 + dev/schema/table.h | 6 +- dev/storage.h | 4 +- dev/storage_impl.h | 2 +- include/sqlite_orm/sqlite_orm.h | 15 +++-- tests/static_tests/table_static_tests.cpp | 79 +++++++++++++++++++---- 6 files changed, 84 insertions(+), 25 deletions(-) diff --git a/dev/schema/column.h b/dev/schema/column.h index 4d7b02a87..9f1750ff0 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -118,6 +118,9 @@ namespace sqlite_orm { template using is_column = polyfill::bool_constant>; + template + using col_index_sequence_of = filter_tuple_sequence_t; + template using col_index_sequence_with_field_type = filter_tuple_sequence_t void for_each_column(L&& lambda) const { - using col_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->elements, col_index_sequence{}, lambda); + iterate_tuple(this->elements, col_index_sequence_of{}, lambda); } /** @@ -379,8 +378,7 @@ namespace sqlite_orm { */ template void for_each_column(L&& lambda) const { - using col_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->columns, col_index_sequence{}, lambda); + iterate_tuple(this->columns, col_index_sequence_of{}, lambda); } template = true> diff --git a/dev/storage.h b/dev/storage.h index f7ef42d95..bc571732c 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -221,7 +221,7 @@ namespace sqlite_orm { void assert_updatable_type() const { using table_type = storage_pick_table_t; using elements_type = elements_type_t; - using col_index_sequence = filter_tuple_sequence_t; + using column_index_sequence = col_index_sequence_of; using pk_index_sequence = filter_tuple_sequence_t; using pkcol_index_sequence = col_index_sequence_with; constexpr size_t dedicatedPrimaryKeyColumnsCount = @@ -229,7 +229,7 @@ namespace sqlite_orm { constexpr size_t primaryKeyColumnsCount = dedicatedPrimaryKeyColumnsCount + pkcol_index_sequence::size(); - constexpr ptrdiff_t nonPrimaryKeysColumnsCount = col_index_sequence::size() - primaryKeyColumnsCount; + constexpr ptrdiff_t nonPrimaryKeysColumnsCount = column_index_sequence::size() - primaryKeyColumnsCount; static_assert(primaryKeyColumnsCount > 0, "A table without primary keys cannot be updated"); static_assert( nonPrimaryKeysColumnsCount > 0, diff --git a/dev/storage_impl.h b/dev/storage_impl.h index 27e51c72e..e35c7a8d9 100644 --- a/dev/storage_impl.h +++ b/dev/storage_impl.h @@ -103,7 +103,7 @@ namespace sqlite_orm { const column_pointer>&) { using table_type = storage_pick_table_t; using cte_colrefs_tuple = typename cte_mapper_type_t::final_colrefs_tuple; - using column_index_sequence = filter_tuple_sequence_t, is_column>; + using column_index_sequence = col_index_sequence_of>; // note: even though the columns contain the [`aliased_field<>::*`] we perform the lookup using the column references. // lookup ColAlias in the final column references diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 8496e4e5e..86874fcfd 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -8770,6 +8770,9 @@ namespace sqlite_orm { template using is_column = polyfill::bool_constant>; + template + using col_index_sequence_of = filter_tuple_sequence_t; + template using col_index_sequence_with_field_type = filter_tuple_sequence_t void for_each_column(L&& lambda) const { - using col_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->elements, col_index_sequence{}, lambda); + iterate_tuple(this->elements, col_index_sequence_of{}, lambda); } /** @@ -12740,8 +12742,7 @@ namespace sqlite_orm { */ template void for_each_column(L&& lambda) const { - using col_index_sequence = filter_tuple_sequence_t; - iterate_tuple(this->columns, col_index_sequence{}, lambda); + iterate_tuple(this->columns, col_index_sequence_of{}, lambda); } template = true> @@ -12945,7 +12946,7 @@ namespace sqlite_orm { const column_pointer>&) { using table_type = storage_pick_table_t; using cte_colrefs_tuple = typename cte_mapper_type_t::final_colrefs_tuple; - using column_index_sequence = filter_tuple_sequence_t, is_column>; + using column_index_sequence = col_index_sequence_of>; // note: even though the columns contain the [`aliased_field<>::*`] we perform the lookup using the column references. // lookup ColAlias in the final column references @@ -23335,7 +23336,7 @@ namespace sqlite_orm { void assert_updatable_type() const { using table_type = storage_pick_table_t; using elements_type = elements_type_t; - using col_index_sequence = filter_tuple_sequence_t; + using column_index_sequence = col_index_sequence_of; using pk_index_sequence = filter_tuple_sequence_t; using pkcol_index_sequence = col_index_sequence_with; constexpr size_t dedicatedPrimaryKeyColumnsCount = @@ -23343,7 +23344,7 @@ namespace sqlite_orm { constexpr size_t primaryKeyColumnsCount = dedicatedPrimaryKeyColumnsCount + pkcol_index_sequence::size(); - constexpr ptrdiff_t nonPrimaryKeysColumnsCount = col_index_sequence::size() - primaryKeyColumnsCount; + constexpr ptrdiff_t nonPrimaryKeysColumnsCount = column_index_sequence::size() - primaryKeyColumnsCount; static_assert(primaryKeyColumnsCount > 0, "A table without primary keys cannot be updated"); static_assert( nonPrimaryKeysColumnsCount > 0, diff --git a/tests/static_tests/table_static_tests.cpp b/tests/static_tests/table_static_tests.cpp index 7020a7969..d35d9318f 100644 --- a/tests/static_tests/table_static_tests.cpp +++ b/tests/static_tests/table_static_tests.cpp @@ -2,6 +2,8 @@ #include using namespace sqlite_orm; +using internal::col_index_sequence_of, internal::col_index_sequence_excluding, internal::col_index_sequence_with, + internal::col_index_sequence_with_field_type; using internal::is_column; using internal::is_primary_key; @@ -18,84 +20,139 @@ TEST_CASE("table static count_of()") { }; { // 1 column no pk auto table = make_table("users", make_column("id", &User::id)); + using elements_type = decltype(table.elements); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 0); - STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); + STATIC_REQUIRE(col_index_sequence_of::size() == 1); + STATIC_REQUIRE(col_index_sequence_with::size() == 0); + STATIC_REQUIRE(col_index_sequence_excluding::size() == 1); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 1); + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); } { // 1 column with 1 inline pk auto table = make_table("users", make_column("id", &User::id, primary_key())); + using elements_type = decltype(table.elements); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); - STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); + STATIC_REQUIRE(col_index_sequence_of::size() == 1); + STATIC_REQUIRE(col_index_sequence_with::size() == 1); + STATIC_REQUIRE(col_index_sequence_excluding::size() == 0); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 1); + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); } { // 1 column with 1 inline pk autoincrement auto table = make_table("users", make_column("id", &User::id, primary_key().autoincrement())); + using elements_type = decltype(table.elements); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); - STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); + STATIC_REQUIRE(col_index_sequence_of::size() == 1); + STATIC_REQUIRE(col_index_sequence_with::size() == 1); + STATIC_REQUIRE(col_index_sequence_excluding::size() == 0); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 1); + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); } { // 1 column with 1 dedicated pk auto table = make_table("users", make_column("id", &User::id), primary_key(&User::id)); + using elements_type = decltype(table.elements); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); - STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); + STATIC_REQUIRE(col_index_sequence_of::size() == 1); + STATIC_REQUIRE(col_index_sequence_with::size() == 0); + STATIC_REQUIRE(col_index_sequence_excluding::size() == 1); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 1); } { // 1 column with 1 dedicated pk autoincrement auto table = make_table("users", make_column("id", &User::id), primary_key(&User::id).autoincrement()); + using elements_type = decltype(table.elements); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); - STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); + STATIC_REQUIRE(col_index_sequence_of::size() == 1); + STATIC_REQUIRE(col_index_sequence_with::size() == 0); + STATIC_REQUIRE(col_index_sequence_excluding::size() == 1); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 1); + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); } { // 2 columns no pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name)); + using elements_type = decltype(table.elements); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 0); - STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); + STATIC_REQUIRE(col_index_sequence_of::size() == 2); + STATIC_REQUIRE(col_index_sequence_with::size() == 0); + STATIC_REQUIRE(col_index_sequence_excluding::size() == 2); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 1); + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); } { // 2 columns with 1 inline id pk auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("id", &User::name)); + using elements_type = decltype(table.elements); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); - STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); + STATIC_REQUIRE(col_index_sequence_of::size() == 2); + STATIC_REQUIRE(col_index_sequence_with::size() == 1); + STATIC_REQUIRE(col_index_sequence_excluding::size() == 1); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 1); + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); } { // 2 columns with 1 inline name pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name, primary_key())); + using elements_type = decltype(table.elements); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 0); STATIC_REQUIRE(table.count_of_columns_with() == 1); - STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); + STATIC_REQUIRE(col_index_sequence_of::size() == 2); + STATIC_REQUIRE(col_index_sequence_with::size() == 1); + STATIC_REQUIRE(col_index_sequence_excluding::size() == 1); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 1); + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 0); } { // 2 columns with 1 dedicated id pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name), primary_key(&User::id)); + using elements_type = decltype(table.elements); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); - STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); + STATIC_REQUIRE(col_index_sequence_of::size() == 2); + STATIC_REQUIRE(col_index_sequence_with::size() == 0); + STATIC_REQUIRE(col_index_sequence_excluding::size() == 2); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 1); + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); } { // 2 columns with 1 dedicated name pk auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name), primary_key(&User::name)); + using elements_type = decltype(table.elements); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); - STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); + STATIC_REQUIRE(col_index_sequence_of::size() == 2); + STATIC_REQUIRE(col_index_sequence_with::size() == 0); + STATIC_REQUIRE(col_index_sequence_excluding::size() == 2); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 1); + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 1); } { // 2 columns with 2 dedicated pks auto table = make_table("users", make_column("id", &User::id), make_column("id", &User::name), primary_key(&User::id, &User::name)); + using elements_type = decltype(table.elements); STATIC_REQUIRE(table.count_of() == 2); STATIC_REQUIRE(table.count_of() == 1); STATIC_REQUIRE(table.count_of_columns_with() == 0); - STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 2); + STATIC_REQUIRE(col_index_sequence_of::size() == 2); + STATIC_REQUIRE(col_index_sequence_with::size() == 0); + STATIC_REQUIRE(col_index_sequence_excluding::size() == 2); + STATIC_REQUIRE(col_index_sequence_with_field_type::size() == 1); + STATIC_REQUIRE(dedicated_pk_columns_count_t::value == 2); } } From 8dae3ff8aafd63cd49e17ffd76cc857f67b98710 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 26 Sep 2025 20:29:36 +0300 Subject: [PATCH 4/7] Used new facility to access the main select to reduce lines of code --- dev/select_constraints.h | 43 +++++++++++ dev/storage.h | 88 +++++++-------------- include/sqlite_orm/sqlite_orm.h | 131 +++++++++++++++++--------------- 3 files changed, 142 insertions(+), 120 deletions(-) diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 2d74ece6c..9772a23d2 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -318,8 +318,17 @@ namespace sqlite_orm { } } }; + + template + inline constexpr bool is_with_clause_v = polyfill::is_specialization_of::value; +#else + template + inline constexpr bool is_with_clause_v = false; #endif + template + using is_with_clause = polyfill::bool_constant>; + template struct asterisk_t { using type = T; @@ -388,6 +397,40 @@ namespace sqlite_orm { } }; + /** + * Specialize if a type is a select statement expression. + */ + template + inline constexpr bool is_select_expression_v = false; + + template + using is_select_expression = polyfill::bool_constant>; + + template + inline constexpr bool is_select_expression_v>> = true; + + template + inline constexpr bool is_select_expression_v< + With, + std::enable_if_t, is_select>>>> = true; + + /* + * Access the main select expression of a with clause or the passed in select expression. + */ + template = true> + constexpr decltype(auto) access_main_select(const Select& select) { + if constexpr (is_with_clause_v) { + return select; + } else { + static_assert(polyfill::always_false_v) +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires (is_select_expression_v, + "SQL expression must be a select expression or a with-clause with a select expression"); +#endif + if constexpr (is_select_v) + requires (is_select_expression_v prepare(Select statement) { + if constexpr (is_select_v& statement) { + using ExprDBOs = polyfill::remove_cvref_tdb_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 - auto execute(const prepared_statement_t>& statement) { - using ColResult = column_result_of_t; + using ColResult = column_result_of_t>; return this->execute_select(statement); } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 86874fcfd..fdf4082a3 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -9141,8 +9141,17 @@ namespace sqlite_orm { } } }; + + template + inline constexpr bool is_with_clause_v = polyfill::is_specialization_of::value; +#else + template + inline constexpr bool is_with_clause_v = false; #endif + template + using is_with_clause = polyfill::bool_constant>; + template struct asterisk_t { using type = T; @@ -9211,6 +9220,40 @@ namespace sqlite_orm { } }; + /** + * Specialize if a type is a select statement expression. + */ + template + inline constexpr bool is_select_expression_v = false; + + template + using is_select_expression = polyfill::bool_constant>; + + template + inline constexpr bool is_select_expression_v>> = true; + + template + inline constexpr bool is_select_expression_v< + With, + std::enable_if_t, is_select>>>> = true; + + /* + * Access the main select expression of a with clause or the passed in select expression. + */ + template = true> + constexpr decltype(auto) access_main_select(const Select& select) { + if constexpr (is_with_clause_v) { + return select; + } else { + static_assert(polyfill::always_false_v) +#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED + requires (is_select_expression_v, + "SQL expression must be a select expression or a with-clause with a select expression"); +#endif + if constexpr (is_select_v) + requires (is_select_expression_v prepare(Select statement) { + if constexpr (is_select_v& statement) { + using ExprDBOs = polyfill::remove_cvref_tdb_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 - auto execute(const prepared_statement_t>& statement) { - using ColResult = column_result_of_t; + using ColResult = column_result_of_t>; return this->execute_select(statement); } From 7abcebf6b664cda125afd1e5abeab6981b548870 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 26 Sep 2025 20:30:50 +0300 Subject: [PATCH 5/7] Generalized preparing and executing 'raw' DML statements --- dev/prepared_statement.h | 42 +++++++++ dev/select_constraints.h | 4 +- dev/storage.h | 114 +---------------------- include/sqlite_orm/sqlite_orm.h | 160 ++++++++++---------------------- 4 files changed, 96 insertions(+), 224 deletions(-) diff --git a/dev/prepared_statement.h b/dev/prepared_statement.h index a3b687812..9cfd8477f 100644 --- a/dev/prepared_statement.h +++ b/dev/prepared_statement.h @@ -310,6 +310,48 @@ namespace sqlite_orm { template using is_insert_constraint = std::is_same; + + /** + * Specialize if a type is a DML statement expression. + */ + template + inline constexpr bool is_raw_dml_expression_v = false; + + template + using is_raw_dml_expression = polyfill::bool_constant>; + + template + inline constexpr bool is_raw_dml_expression_v< + DML, + std::enable_if_t< + polyfill:: + disjunction_v, is_replace_raw, is_update_all, is_remove_all>>> = + true; + + template + inline constexpr bool is_raw_dml_expression_v< + With, + std::enable_if_t, + polyfill::disjunction>, + is_replace_raw>, + is_update_all>, + is_remove_all>>>>> = + true; + + /* + * Access the main select expression of a with clause or the passed in select expression. + */ + template = true> + constexpr decltype(auto) access_main_dml(const DML& dml) { + if constexpr (is_with_clause_v) { + return (dml.expression); + } else { + return dml; + } + } + + template + using main_dml_t = polyfill::remove_cvref_t()))>; } } diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 9772a23d2..b374f6e65 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -421,10 +421,8 @@ namespace sqlite_orm { constexpr decltype(auto) access_main_select(const Select& select) { if constexpr (is_with_clause_v) { - return select; } else { - static_assert(polyfill::always_false_v prepare(Select statement) { @@ -1381,16 +1374,6 @@ namespace sqlite_orm { return this->prepare_impl(std::move(statement)); } - template - prepared_statement_t> prepare(replace_raw_t statement) { - return this->prepare_impl(std::move(statement)); - } - - template - prepared_statement_t> prepare(insert_raw_t statement) { - return this->prepare_impl(std::move(statement)); - } - #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template prepared_statement_t> @@ -1399,16 +1382,6 @@ namespace sqlite_orm { } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template - prepared_statement_t> prepare(update_all_t statement) { - return this->prepare_impl(std::move(statement)); - } - - template - prepared_statement_t> prepare(remove_all_t statement) { - return this->prepare_impl(std::move(statement)); - } - template prepared_statement_t> prepare(get_t statement) { return this->prepare_impl(std::move(statement)); @@ -1484,49 +1457,8 @@ namespace sqlite_orm { return this->prepare_impl(std::move(statement)); } - template - void execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); - iterate_ast(statement.expression, conditional_binder{stmt}); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } - } - -#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - 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}); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } - } -#endif - - template - void execute(const prepared_statement_t>& statement) { + template, bool> = true> + void execute(const prepared_statement_t& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); std::string sql; @@ -1833,42 +1765,6 @@ namespace sqlite_orm { #endif } - template - void execute(const prepared_statement_t>& statement) { - sqlite3_stmt* stmt = reset_stmt(statement.stmt); - iterate_ast(statement.expression.conditions, conditional_binder{stmt}); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } - } - - 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); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } - } - template = true> auto execute(const prepared_statement_t) { return (select.expression); - } else if constexpr (is_select_v); + return select; } } @@ -15059,6 +15057,48 @@ namespace sqlite_orm { template using is_insert_constraint = std::is_same; + + /** + * Specialize if a type is a DML statement expression. + */ + template + inline constexpr bool is_raw_dml_expression_v = false; + + template + using is_raw_dml_expression = polyfill::bool_constant>; + + template + inline constexpr bool is_raw_dml_expression_v< + DML, + std::enable_if_t< + polyfill:: + disjunction_v, is_replace_raw, is_update_all, is_remove_all>>> = + true; + + template + inline constexpr bool is_raw_dml_expression_v< + With, + std::enable_if_t, + polyfill::disjunction>, + is_replace_raw>, + is_update_all>, + is_remove_all>>>>> = + true; + + /* + * Access the main select expression of a with clause or the passed in select expression. + */ + template = true> + constexpr decltype(auto) access_main_dml(const DML& dml) { + if constexpr (is_with_clause_v) { + return (dml.expression); + } else { + return dml; + } + } + + template + using main_dml_t = polyfill::remove_cvref_t()))>; } } @@ -24509,17 +24549,10 @@ namespace sqlite_orm { using storage_base::table_exists; // now that it is in storage_base make it into overload set -#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) - template< - class... CTEs, - class E, - std::enable_if_t< - polyfill::disjunction_v, 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)); + template, bool> = true> + prepared_statement_t prepare(DML statement) { + return this->prepare_impl(std::move(statement)); } -#endif template = true> prepared_statement_t& statement) { using ExprDBOs = polyfill::remove_cvref_tdb_objects, From d3ab62a8695580b44a2ba0a47f3b746ffdfa47a0 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 26 Sep 2025 20:32:08 +0300 Subject: [PATCH 6/7] Encapsulated logging executes SQL statements in all locations ... while step functions are kept in their respective functions for greater clarity. --- dev/storage.h | 142 ++++------------ dev/util.h | 145 ++++++++++------ include/sqlite_orm/sqlite_orm.h | 287 ++++++++++++++------------------ 3 files changed, 248 insertions(+), 326 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index a2077f87c..e06313083 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1461,17 +1461,7 @@ namespace sqlite_orm { void execute(const prepared_statement_t& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + this->executor.perform_single_step(stmt); } /** @@ -1489,17 +1479,8 @@ namespace sqlite_orm { [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { return table.object_field_value(object, memberPointer); }); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + + this->executor.perform_single_step(stmt); return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -1537,17 +1518,8 @@ namespace sqlite_orm { const object_type& object = get_object(statement.expression); processObject(object); }; - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + + this->executor.perform_single_step(stmt); } /** @@ -1594,17 +1566,8 @@ namespace sqlite_orm { const object_type& object = get_object(statement.expression); processObject(object); } - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + + this->executor.perform_single_step(stmt); return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -1612,17 +1575,7 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.ids, conditional_binder{stmt}); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + this->executor.perform_single_step(stmt); } template @@ -1645,17 +1598,8 @@ namespace sqlite_orm { bindValue(polyfill::invoke(column.member_pointer, object)); } }); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + + this->executor.perform_single_step(stmt); } template @@ -1665,21 +1609,11 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::unique_ptr res; - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.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); }); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } return res; } @@ -1691,20 +1625,10 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::optional res; - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.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 (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -1717,26 +1641,17 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED std::optional res; - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.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 (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } if (!res.has_value()) { throw std::system_error{orm_error_code::not_found}; } return std::move(res).value(); #else auto& table = this->get_table(); + std::string sql; if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); @@ -1744,24 +1659,27 @@ namespace sqlite_orm { if (this->executor.will_run_query) { this->executor.will_run_query(sql); } - const auto stepRes = sqlite3_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } - switch (stepRes) { - case SQLITE_ROW: { - T res; - object_from_column_builder builder{res, stmt}; - table.for_each_column(builder); - return res; - } break; + + switch (int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: + break; case SQLITE_DONE: { throw std::system_error{orm_error_code::not_found}; - } break; + } default: { - throw_translated_sqlite_error(rc); + throw_translated_sqlite_error(stmt); } } + + T res; + object_from_column_builder builder{res, stmt}; + table.for_each_column(builder); + + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } + + return res; #endif } diff --git a/dev/util.h b/dev/util.h index 7e727f352..1b88f5892 100644 --- a/dev/util.h +++ b/dev/util.h @@ -5,9 +5,6 @@ #include // std::string #include // std::move #include // std::function -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED -#include // std::string_view -#endif #endif #include "functional/gsl.h" @@ -59,6 +56,28 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { namespace sqlite_orm { namespace internal { + // Wrapper to reduce boiler-plate code + inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { + sqlite3_reset(stmt); + return stmt; + } + + inline sqlite3_stmt* prepare_stmt(sqlite3* db, serialize_arg_type query) { + sqlite3_stmt* stmt; + const int rc = sqlite3_prepare_v2(db, query.data(), int(query.size()), &stmt, nullptr); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(rc); + } + return stmt; + } + + template + void perform_single_step(sqlite3_stmt* stmt) { + const int rc = sqlite3_step(stmt); + if (rc != expected) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(rc); + } + } template void perform_step(sqlite3_stmt* stmt, L&& lambda) { @@ -68,8 +87,26 @@ namespace sqlite_orm { } break; case SQLITE_DONE: return; - default: { - throw_translated_sqlite_error(stmt); + default: + SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(stmt); + } + } + } + + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) { + for (;;) { + switch (int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + return; + default: + SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(stmt); + } } } } @@ -78,44 +115,48 @@ namespace sqlite_orm { std::function will_run_query; std::function did_run_query; - inline void perform_void_exec(sqlite3* db, orm_gsl::czstring sql) const { + void perform_void_exec(sqlite3* db, orm_gsl::czstring sql) const { if (this->will_run_query) { this->will_run_query(sql); } + const int rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr); - if (rc != SQLITE_OK) { + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { throw_translated_sqlite_error(rc); } + if (this->did_run_query) { this->did_run_query(sql); } } - inline void perform_exec(sqlite3* db, - orm_gsl::czstring sql, - int (*callback)(void*, int, orm_gsl::zstring*, orm_gsl::zstring*), - void* user_data) const { + void perform_exec(sqlite3* db, + orm_gsl::czstring sql, + int (*callback)(void*, int, orm_gsl::zstring*, orm_gsl::zstring*), + void* user_data) const { if (this->will_run_query) { this->will_run_query(sql); } + const int rc = sqlite3_exec(db, sql, callback, user_data, nullptr); - if (rc != SQLITE_OK) { + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { throw_translated_sqlite_error(rc); } + if (this->did_run_query) { this->did_run_query(sql); } } - inline void perform_exec(sqlite3* db, - const std::string& query, - int (*callback)(void*, int, orm_gsl::zstring*, orm_gsl::zstring*), - void* user_data) const { + void perform_exec(sqlite3* db, + const std::string& query, + int (*callback)(void*, int, orm_gsl::zstring*, orm_gsl::zstring*), + void* user_data) const { return perform_exec(db, query.c_str(), callback, user_data); } - template - void perform_steps(sqlite3_stmt* stmt, L&& lambda) const { + template + void perform_single_step(sqlite3_stmt* stmt) const { orm_gsl::czstring sql = nullptr; if (this->will_run_query || this->did_run_query) { sql = sqlite3_sql(stmt); @@ -123,45 +164,47 @@ namespace sqlite_orm { if (this->will_run_query) { this->will_run_query(sql); } - for (;;) { - switch (int rc = sqlite3_step(stmt)) { - case SQLITE_ROW: { - lambda(stmt); - } break; - case SQLITE_DONE: - if (this->did_run_query) { - this->did_run_query(sql); - } - return; - default: { - throw_translated_sqlite_error(stmt); - } - } + + internal::perform_single_step(stmt); + + if (this->did_run_query) { + this->did_run_query(sql); } } - }; - // Wrapper to reduce boiler-plate code - inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { - sqlite3_reset(stmt); - return stmt; - } + template + void perform_step(sqlite3_stmt* stmt, L&& lambda) const { + orm_gsl::czstring sql = nullptr; + if (this->will_run_query || this->did_run_query) { + sql = sqlite3_sql(stmt); + } + if (this->will_run_query) { + this->will_run_query(sql); + } - inline sqlite3_stmt* prepare_stmt(sqlite3* db, serialize_arg_type query) { - sqlite3_stmt* stmt; - const int rc = sqlite3_prepare_v2(db, query.data(), int(query.size()), &stmt, nullptr); - if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { - throw_translated_sqlite_error(rc); + internal::perform_step(stmt, lambda); + + if (this->did_run_query) { + this->did_run_query(sql); + } } - return stmt; - } - template - void perform_step(sqlite3_stmt* stmt) { - const int rc = sqlite3_step(stmt); - if (rc != expected) { - throw_translated_sqlite_error(rc); + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) const { + orm_gsl::czstring sql = nullptr; + if (this->will_run_query || this->did_run_query) { + sql = sqlite3_sql(stmt); + } + if (this->will_run_query) { + this->will_run_query(sql); + } + + internal::perform_steps(stmt, lambda); + + if (this->did_run_query) { + this->did_run_query(sql); + } } - } + }; } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 2fb1dd42a..9bf748ac9 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13858,9 +13858,6 @@ namespace sqlite_orm { #include // std::string #include // std::move #include // std::function -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED -#include // std::string_view -#endif #endif // #include "functional/gsl.h" @@ -13914,6 +13911,28 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { namespace sqlite_orm { namespace internal { + // Wrapper to reduce boiler-plate code + inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { + sqlite3_reset(stmt); + return stmt; + } + + inline sqlite3_stmt* prepare_stmt(sqlite3* db, serialize_arg_type query) { + sqlite3_stmt* stmt; + const int rc = sqlite3_prepare_v2(db, query.data(), int(query.size()), &stmt, nullptr); + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(rc); + } + return stmt; + } + + template + void perform_single_step(sqlite3_stmt* stmt) { + const int rc = sqlite3_step(stmt); + if (rc != expected) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(rc); + } + } template void perform_step(sqlite3_stmt* stmt, L&& lambda) { @@ -13923,8 +13942,26 @@ namespace sqlite_orm { } break; case SQLITE_DONE: return; - default: { - throw_translated_sqlite_error(stmt); + default: + SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(stmt); + } + } + } + + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) { + for (;;) { + switch (int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + return; + default: + SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { + throw_translated_sqlite_error(stmt); + } } } } @@ -13933,44 +13970,48 @@ namespace sqlite_orm { std::function will_run_query; std::function did_run_query; - inline void perform_void_exec(sqlite3* db, orm_gsl::czstring sql) const { + void perform_void_exec(sqlite3* db, orm_gsl::czstring sql) const { if (this->will_run_query) { this->will_run_query(sql); } + const int rc = sqlite3_exec(db, sql, nullptr, nullptr, nullptr); - if (rc != SQLITE_OK) { + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { throw_translated_sqlite_error(rc); } + if (this->did_run_query) { this->did_run_query(sql); } } - inline void perform_exec(sqlite3* db, - orm_gsl::czstring sql, - int (*callback)(void*, int, orm_gsl::zstring*, orm_gsl::zstring*), - void* user_data) const { + void perform_exec(sqlite3* db, + orm_gsl::czstring sql, + int (*callback)(void*, int, orm_gsl::zstring*, orm_gsl::zstring*), + void* user_data) const { if (this->will_run_query) { this->will_run_query(sql); } + const int rc = sqlite3_exec(db, sql, callback, user_data, nullptr); - if (rc != SQLITE_OK) { + if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { throw_translated_sqlite_error(rc); } + if (this->did_run_query) { this->did_run_query(sql); } } - inline void perform_exec(sqlite3* db, - const std::string& query, - int (*callback)(void*, int, orm_gsl::zstring*, orm_gsl::zstring*), - void* user_data) const { + void perform_exec(sqlite3* db, + const std::string& query, + int (*callback)(void*, int, orm_gsl::zstring*, orm_gsl::zstring*), + void* user_data) const { return perform_exec(db, query.c_str(), callback, user_data); } - template - void perform_steps(sqlite3_stmt* stmt, L&& lambda) const { + template + void perform_single_step(sqlite3_stmt* stmt) const { orm_gsl::czstring sql = nullptr; if (this->will_run_query || this->did_run_query) { sql = sqlite3_sql(stmt); @@ -13978,46 +14019,48 @@ namespace sqlite_orm { if (this->will_run_query) { this->will_run_query(sql); } - for (;;) { - switch (int rc = sqlite3_step(stmt)) { - case SQLITE_ROW: { - lambda(stmt); - } break; - case SQLITE_DONE: - if (this->did_run_query) { - this->did_run_query(sql); - } - return; - default: { - throw_translated_sqlite_error(stmt); - } - } + + internal::perform_single_step(stmt); + + if (this->did_run_query) { + this->did_run_query(sql); } } - }; - // Wrapper to reduce boiler-plate code - inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { - sqlite3_reset(stmt); - return stmt; - } + template + void perform_step(sqlite3_stmt* stmt, L&& lambda) const { + orm_gsl::czstring sql = nullptr; + if (this->will_run_query || this->did_run_query) { + sql = sqlite3_sql(stmt); + } + if (this->will_run_query) { + this->will_run_query(sql); + } - inline sqlite3_stmt* prepare_stmt(sqlite3* db, serialize_arg_type query) { - sqlite3_stmt* stmt; - const int rc = sqlite3_prepare_v2(db, query.data(), int(query.size()), &stmt, nullptr); - if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible but unexpected*/ { - throw_translated_sqlite_error(rc); + internal::perform_step(stmt, lambda); + + if (this->did_run_query) { + this->did_run_query(sql); + } } - return stmt; - } - template - void perform_step(sqlite3_stmt* stmt) { - const int rc = sqlite3_step(stmt); - if (rc != expected) { - throw_translated_sqlite_error(rc); + template + void perform_steps(sqlite3_stmt* stmt, L&& lambda) const { + orm_gsl::czstring sql = nullptr; + if (this->will_run_query || this->did_run_query) { + sql = sqlite3_sql(stmt); + } + if (this->will_run_query) { + this->will_run_query(sql); + } + + internal::perform_steps(stmt, lambda); + + if (this->did_run_query) { + this->did_run_query(sql); + } } - } + }; } } @@ -24659,17 +24702,7 @@ namespace sqlite_orm { void execute(const prepared_statement_t& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression, conditional_binder{stmt}); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + this->executor.perform_single_step(stmt); } /** @@ -24687,17 +24720,8 @@ namespace sqlite_orm { [&table = this->get_table(), &object = statement.expression.obj](auto& memberPointer) { return table.object_field_value(object, memberPointer); }); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + + this->executor.perform_single_step(stmt); return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -24735,17 +24759,8 @@ namespace sqlite_orm { const object_type& object = get_object(statement.expression); processObject(object); }; - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + + this->executor.perform_single_step(stmt); } /** @@ -24792,17 +24807,8 @@ namespace sqlite_orm { const object_type& object = get_object(statement.expression); processObject(object); } - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + + this->executor.perform_single_step(stmt); return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } @@ -24810,17 +24816,7 @@ namespace sqlite_orm { void execute(const prepared_statement_t>& statement) { sqlite3_stmt* stmt = reset_stmt(statement.stmt); iterate_ast(statement.expression.ids, conditional_binder{stmt}); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + this->executor.perform_single_step(stmt); } template @@ -24843,17 +24839,8 @@ namespace sqlite_orm { bindValue(polyfill::invoke(column.member_pointer, object)); } }); - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } + + this->executor.perform_single_step(stmt); } template @@ -24863,21 +24850,11 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::unique_ptr res; - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.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); }); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } return res; } @@ -24889,20 +24866,10 @@ namespace sqlite_orm { iterate_ast(statement.expression.ids, conditional_binder{stmt}); std::optional res; - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.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 (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -24915,26 +24882,17 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED std::optional res; - std::string sql; - if (this->executor.will_run_query || this->executor.did_run_query) { - sql = statement.sql(); - } - if (this->executor.will_run_query) { - this->executor.will_run_query(sql); - } - perform_step(stmt, [&table = this->get_table(), &res](sqlite3_stmt* stmt) { + this->executor.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 (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } if (!res.has_value()) { throw std::system_error{orm_error_code::not_found}; } return std::move(res).value(); #else auto& table = this->get_table(); + std::string sql; if (this->executor.will_run_query || this->executor.did_run_query) { sql = statement.sql(); @@ -24942,24 +24900,27 @@ namespace sqlite_orm { if (this->executor.will_run_query) { this->executor.will_run_query(sql); } - const auto stepRes = sqlite3_step(stmt); - if (this->executor.did_run_query) { - this->executor.did_run_query(sql); - } - switch (stepRes) { - case SQLITE_ROW: { - T res; - object_from_column_builder builder{res, stmt}; - table.for_each_column(builder); - return res; - } break; + + switch (int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: + break; case SQLITE_DONE: { throw std::system_error{orm_error_code::not_found}; - } break; + } default: { - throw_translated_sqlite_error(rc); + throw_translated_sqlite_error(stmt); } } + + T res; + object_from_column_builder builder{res, stmt}; + table.for_each_column(builder); + + if (this->executor.did_run_query) { + this->executor.did_run_query(sql); + } + + return res; #endif } From 17f861f33891ec266fa3ffaefbdd1328516a3c24 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Fri, 26 Sep 2025 22:06:13 +0300 Subject: [PATCH 7/7] Generalized serializer context * Removed `serializer_context_builder`, which is now entirely replaced by `obtain_db_objects()`. * Renamed serializer context flag variables. --- dev/ast/set.h | 5 +- dev/column_names_getter.h | 4 +- dev/conditions.h | 5 +- dev/cte_column_names_collector.h | 4 +- dev/mapped_view.h | 2 +- dev/order_by_serializer.h | 2 +- dev/result_set_view.h | 2 +- dev/serializer_context.h | 18 +--- dev/serializing_util.h | 2 +- dev/statement_serializer.h | 36 ++++---- dev/storage.h | 4 +- include/sqlite_orm/sqlite_orm.h | 84 ++++++++----------- .../column_names.cpp | 6 +- .../schema/column.cpp | 4 +- .../statements/select.cpp | 4 +- .../table_constraints/primary_key.cpp | 2 +- 16 files changed, 76 insertions(+), 108 deletions(-) diff --git a/dev/ast/set.h b/dev/ast/set.h index aa9c20846..ed5aa3751 100644 --- a/dev/ast/set.h +++ b/dev/ast/set.h @@ -51,7 +51,7 @@ namespace sqlite_orm { template void push_back(assign_t assign) { auto newContext = this->context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; // 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; @@ -108,7 +108,6 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { */ template internal::dynamic_set_t> dynamic_set(const S& storage) { - internal::serializer_context_builder builder(storage); - return builder(); + return {obtain_db_objects(storage)}; } } diff --git a/dev/column_names_getter.h b/dev/column_names_getter.h index 3b5dfc658..1200cc5dd 100644 --- a/dev/column_names_getter.h +++ b/dev/column_names_getter.h @@ -33,7 +33,7 @@ namespace sqlite_orm { 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, + table.for_each_column([qualified = !context.omit_table_name, &tableName = table.name, &collectedExpressions](const column_identifier& column) { if constexpr (is_alias::value) { @@ -50,7 +50,7 @@ namespace sqlite_orm { collectedExpressions.reserve(collectedExpressions.size() + 1); if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); - } else if (!context.skip_table_name) { + } else if (!context.omit_table_name) { const basic_table& table = pick_table>(context.db_objects); collectedExpressions.push_back(quote_identifier(table.name) + ".*"); } else { diff --git a/dev/conditions.h b/dev/conditions.h index f565912d0..24ae7446c 100644 --- a/dev/conditions.h +++ b/dev/conditions.h @@ -493,7 +493,7 @@ namespace sqlite_orm { template void push_back(order_by_t orderBy) { auto newContext = this->context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; auto columnName = serialize(orderBy._expression, newContext); this->entries.emplace_back(std::move(columnName), std::move(orderBy._collate_argument), orderBy._order); } @@ -1149,8 +1149,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::dynamic_order_by_t> dynamic_order_by(const S& storage) { - internal::serializer_context_builder builder(storage); - return builder(); + return {obtain_db_objects(storage)}; } /** diff --git a/dev/cte_column_names_collector.h b/dev/cte_column_names_collector.h index fc3c42d4a..b02fbf204 100644 --- a/dev/cte_column_names_collector.h +++ b/dev/cte_column_names_collector.h @@ -57,7 +57,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::vector operator()(const expression_type& t, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; std::string columnName = serialize(t, newContext); if (columnName.empty()) { throw std::system_error{orm_error_code::column_not_found}; @@ -137,7 +137,7 @@ namespace sqlite_orm { std::vector columnNames; columnNames.reserve(size_t(cols.count)); auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; iterate_tuple(cols.columns, [&columnNames, &newContext](auto& m) { using value_type = polyfill::remove_cvref_t; diff --git a/dev/mapped_view.h b/dev/mapped_view.h index aa72d1f66..57ffc8a92 100644 --- a/dev/mapped_view.h +++ b/dev/mapped_view.h @@ -42,7 +42,7 @@ namespace sqlite_orm { auto& dbObjects = obtain_db_objects(this->storage); context_t context{dbObjects}; - context.skip_table_name = false; + context.omit_table_name = false; context.replace_bindable_with_question = true; const std::string sql = serialize(this->expression, context); diff --git a/dev/order_by_serializer.h b/dev/order_by_serializer.h index 1782ca39a..f862c6939 100644 --- a/dev/order_by_serializer.h +++ b/dev/order_by_serializer.h @@ -45,7 +45,7 @@ namespace sqlite_orm { const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; auto newContext = context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; ss << serialize(orderBy._expression, newContext); seralize_collate(ss, orderBy); diff --git a/dev/result_set_view.h b/dev/result_set_view.h index 2aa6b2377..53a016855 100644 --- a/dev/result_set_view.h +++ b/dev/result_set_view.h @@ -53,7 +53,7 @@ namespace sqlite_orm::internal { using context_t = serializer_context; context_t context{exprDBOs}; - context.skip_table_name = false; + context.omit_table_name = false; context.replace_bindable_with_question = true; const std::string sql = serialize(this->expression, context); diff --git a/dev/serializer_context.h b/dev/serializer_context.h index dfa6d3ad0..2fe1afde5 100644 --- a/dev/serializer_context.h +++ b/dev/serializer_context.h @@ -6,9 +6,9 @@ namespace sqlite_orm { struct serializer_context_base { bool replace_bindable_with_question = false; - bool skip_table_name = true; + bool omit_table_name = true; bool use_parentheses = true; - bool fts5_columns = false; + bool omit_column_type = false; }; template @@ -19,20 +19,6 @@ namespace sqlite_orm { serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} }; - - 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; - }; } } diff --git a/dev/serializing_util.h b/dev/serializing_util.h index 219111d65..0c85932ae 100644 --- a/dev/serializing_util.h +++ b/dev/serializing_util.h @@ -433,7 +433,7 @@ namespace sqlite_orm { ss << ' ' << serialize(constraint, context); }); // add implicit null constraint - if (!context.fts5_columns) { + if (!context.omit_column_type) { constexpr bool hasExplicitNullableConstraint = mpl::invoke_t, check_if_has_type>, constraints_tuple>::value; diff --git a/dev/statement_serializer.h b/dev/statement_serializer.h index 1a0d32252..c443e3d2a 100644 --- a/dev/statement_serializer.h +++ b/dev/statement_serializer.h @@ -420,11 +420,11 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& c, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; - if (!context.skip_table_name) { + if (!context.omit_table_name) { ss << streaming_identifier(alias_extractor::extract()) << "."; } auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; ss << serialize(c.column, newContext); return ss.str(); } @@ -442,7 +442,7 @@ namespace sqlite_orm { std::stringstream ss; if (auto* columnName = find_column_name(context.db_objects, e)) { ss << streaming_identifier( - !context.skip_table_name ? lookup_table_name>(context.db_objects) : "", + !context.omit_table_name ? lookup_table_name>(context.db_objects) : "", *columnName, ""); } else { @@ -504,7 +504,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; - if (!context.skip_table_name) { + if (!context.omit_table_name) { ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; } ss << static_cast(statement); @@ -520,7 +520,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; - if (!context.skip_table_name) { + if (!context.omit_table_name) { ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; } ss << static_cast(statement); @@ -536,7 +536,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; - if (!context.skip_table_name) { + if (!context.omit_table_name) { ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; } ss << static_cast(statement); @@ -1305,7 +1305,7 @@ namespace sqlite_orm { const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; ss << streaming_identifier(column.name); - if (!context.fts5_columns) { + if (!context.omit_column_type) { ss << " " << type_printer>>().print(); } ss << streaming_column_constraints( @@ -1452,7 +1452,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "SET "; auto leftContext = context; - leftContext.skip_table_name = true; + leftContext.omit_table_name = true; iterate_tuple(statement.assigns, [&ss, &context, &leftContext, first = true](auto& value) mutable { static constexpr std::array sep = {", ", ""}; ss << sep[std::exchange(first, false)] << serialize(value.lhs, leftContext) << ' ' @@ -1612,7 +1612,7 @@ namespace sqlite_orm { ss << ' '; if constexpr (is_columns::value) { auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; newContext.use_parentheses = true; ss << serialize(value, newContext); } else if constexpr (is_values::value || is_select::value) { @@ -1877,7 +1877,7 @@ namespace sqlite_orm { template SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& sel, Ctx context) SQLITE_ORM_OR_CONST_CALLOP { - context.skip_table_name = false; + context.omit_table_name = false; // subqueries should always use parentheses in column names auto subCtx = context; subCtx.use_parentheses = true; @@ -1961,7 +1961,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "USING FTS5("; auto subContext = context; - subContext.fts5_columns = true; + subContext.omit_column_type = true; ss << streaming_expressions_tuple(statement.columns, subContext) << ")"; return ss.str(); } @@ -2051,7 +2051,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "OLD."; auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; ss << serialize(statement.expression, newContext); return ss.str(); } @@ -2067,7 +2067,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "NEW."; auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; ss << serialize(statement.expression, newContext); return ss.str(); } @@ -2310,7 +2310,7 @@ namespace sqlite_orm { const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; auto newContext = context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; ss << static_cast(on) << " " << serialize(on.arg, newContext) << " "; return ss.str(); } @@ -2325,7 +2325,7 @@ namespace sqlite_orm { const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; auto newContext = context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext) << " HAVING " << serialize(statement.expression, context); return ss.str(); @@ -2341,7 +2341,7 @@ namespace sqlite_orm { const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; auto newContext = context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext); return ss.str(); } @@ -2359,7 +2359,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { auto newContext = context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; std::stringstream ss; ss << "LIMIT "; if constexpr (HO) { @@ -2401,7 +2401,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; return static_cast(statement) + " (" + serialize(statement.column, newContext) + ")"; } }; diff --git a/dev/storage.h b/dev/storage.h index e06313083..aac24309e 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -1269,7 +1269,7 @@ namespace sqlite_orm { context_t context{exprDBOs}; context.replace_bindable_with_question = parametrized; // just like prepare_impl() - context.skip_table_name = false; + context.omit_table_name = false; return serialize(expression, context); } @@ -1287,7 +1287,7 @@ namespace sqlite_orm { using context_t = serializer_context>; context_t context{exprDBOs}; - context.skip_table_name = false; + context.omit_table_name = false; context.replace_bindable_with_question = true; auto conection = this->get_connection(); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 9bf748ac9..2ed7e0fb0 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -4994,9 +4994,9 @@ namespace sqlite_orm { struct serializer_context_base { bool replace_bindable_with_question = false; - bool skip_table_name = true; + bool omit_table_name = true; bool use_parentheses = true; - bool fts5_columns = false; + bool omit_column_type = false; }; template @@ -5007,20 +5007,6 @@ namespace sqlite_orm { serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} }; - - 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; - }; } } @@ -5632,7 +5618,7 @@ namespace sqlite_orm { template void push_back(order_by_t orderBy) { auto newContext = this->context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; auto columnName = serialize(orderBy._expression, newContext); this->entries.emplace_back(std::move(columnName), std::move(orderBy._collate_argument), orderBy._order); } @@ -6288,8 +6274,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { template internal::dynamic_order_by_t> dynamic_order_by(const S& storage) { - internal::serializer_context_builder builder(storage); - return builder(); + return {obtain_db_objects(storage)}; } /** @@ -14749,7 +14734,7 @@ namespace sqlite_orm { template void push_back(assign_t assign) { auto newContext = this->context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; // 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; @@ -14806,8 +14791,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm { */ template internal::dynamic_set_t> dynamic_set(const S& storage) { - internal::serializer_context_builder builder(storage); - return builder(); + return {obtain_db_objects(storage)}; } } @@ -16514,7 +16498,7 @@ namespace sqlite_orm { auto& dbObjects = obtain_db_objects(this->storage); context_t context{dbObjects}; - context.skip_table_name = false; + context.omit_table_name = false; context.replace_bindable_with_question = true; const std::string sql = serialize(this->expression, context); @@ -16692,7 +16676,7 @@ namespace sqlite_orm::internal { using context_t = serializer_context; context_t context{exprDBOs}; - context.skip_table_name = false; + context.omit_table_name = false; context.replace_bindable_with_question = true; const std::string sql = serialize(this->expression, context); @@ -17254,7 +17238,7 @@ namespace sqlite_orm { ss << ' ' << serialize(constraint, context); }); // add implicit null constraint - if (!context.fts5_columns) { + if (!context.omit_column_type) { constexpr bool hasExplicitNullableConstraint = mpl::invoke_t, check_if_has_type>, constraints_tuple>::value; @@ -19625,7 +19609,7 @@ namespace sqlite_orm { 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, + table.for_each_column([qualified = !context.omit_table_name, &tableName = table.name, &collectedExpressions](const column_identifier& column) { if constexpr (is_alias::value) { @@ -19642,7 +19626,7 @@ namespace sqlite_orm { collectedExpressions.reserve(collectedExpressions.size() + 1); if constexpr (is_alias::value) { collectedExpressions.push_back(quote_identifier(alias_extractor::extract()) + ".*"); - } else if (!context.skip_table_name) { + } else if (!context.omit_table_name) { const basic_table& table = pick_table>(context.db_objects); collectedExpressions.push_back(quote_identifier(table.name) + ".*"); } else { @@ -19787,7 +19771,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::vector operator()(const expression_type& t, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; std::string columnName = serialize(t, newContext); if (columnName.empty()) { throw std::system_error{orm_error_code::column_not_found}; @@ -19867,7 +19851,7 @@ namespace sqlite_orm { std::vector columnNames; columnNames.reserve(size_t(cols.count)); auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; iterate_tuple(cols.columns, [&columnNames, &newContext](auto& m) { using value_type = polyfill::remove_cvref_t; @@ -20002,7 +19986,7 @@ namespace sqlite_orm { const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; auto newContext = context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; ss << serialize(orderBy._expression, newContext); seralize_collate(ss, orderBy); @@ -20704,11 +20688,11 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& c, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; - if (!context.skip_table_name) { + if (!context.omit_table_name) { ss << streaming_identifier(alias_extractor::extract()) << "."; } auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; ss << serialize(c.column, newContext); return ss.str(); } @@ -20726,7 +20710,7 @@ namespace sqlite_orm { std::stringstream ss; if (auto* columnName = find_column_name(context.db_objects, e)) { ss << streaming_identifier( - !context.skip_table_name ? lookup_table_name>(context.db_objects) : "", + !context.omit_table_name ? lookup_table_name>(context.db_objects) : "", *columnName, ""); } else { @@ -20788,7 +20772,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; - if (!context.skip_table_name) { + if (!context.omit_table_name) { ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; } ss << static_cast(statement); @@ -20804,7 +20788,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; - if (!context.skip_table_name) { + if (!context.omit_table_name) { ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; } ss << static_cast(statement); @@ -20820,7 +20804,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; - if (!context.skip_table_name) { + if (!context.omit_table_name) { ss << streaming_identifier(lookup_table_name(context.db_objects)) << "."; } ss << static_cast(statement); @@ -21589,7 +21573,7 @@ namespace sqlite_orm { const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; ss << streaming_identifier(column.name); - if (!context.fts5_columns) { + if (!context.omit_column_type) { ss << " " << type_printer>>().print(); } ss << streaming_column_constraints( @@ -21736,7 +21720,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "SET "; auto leftContext = context; - leftContext.skip_table_name = true; + leftContext.omit_table_name = true; iterate_tuple(statement.assigns, [&ss, &context, &leftContext, first = true](auto& value) mutable { static constexpr std::array sep = {", ", ""}; ss << sep[std::exchange(first, false)] << serialize(value.lhs, leftContext) << ' ' @@ -21896,7 +21880,7 @@ namespace sqlite_orm { ss << ' '; if constexpr (is_columns::value) { auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; newContext.use_parentheses = true; ss << serialize(value, newContext); } else if constexpr (is_values::value || is_select::value) { @@ -22161,7 +22145,7 @@ namespace sqlite_orm { template SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& sel, Ctx context) SQLITE_ORM_OR_CONST_CALLOP { - context.skip_table_name = false; + context.omit_table_name = false; // subqueries should always use parentheses in column names auto subCtx = context; subCtx.use_parentheses = true; @@ -22245,7 +22229,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "USING FTS5("; auto subContext = context; - subContext.fts5_columns = true; + subContext.omit_column_type = true; ss << streaming_expressions_tuple(statement.columns, subContext) << ")"; return ss.str(); } @@ -22335,7 +22319,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "OLD."; auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; ss << serialize(statement.expression, newContext); return ss.str(); } @@ -22351,7 +22335,7 @@ namespace sqlite_orm { std::stringstream ss; ss << "NEW."; auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; ss << serialize(statement.expression, newContext); return ss.str(); } @@ -22594,7 +22578,7 @@ namespace sqlite_orm { const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; auto newContext = context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; ss << static_cast(on) << " " << serialize(on.arg, newContext) << " "; return ss.str(); } @@ -22609,7 +22593,7 @@ namespace sqlite_orm { const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; auto newContext = context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext) << " HAVING " << serialize(statement.expression, context); return ss.str(); @@ -22625,7 +22609,7 @@ namespace sqlite_orm { const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { std::stringstream ss; auto newContext = context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext); return ss.str(); } @@ -22643,7 +22627,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { auto newContext = context; - newContext.skip_table_name = false; + newContext.omit_table_name = false; std::stringstream ss; ss << "LIMIT "; if constexpr (HO) { @@ -22685,7 +22669,7 @@ namespace sqlite_orm { SQLITE_ORM_STATIC_CALLOP std::string operator()(const statement_type& statement, const Ctx& context) SQLITE_ORM_OR_CONST_CALLOP { auto newContext = context; - newContext.skip_table_name = true; + newContext.omit_table_name = true; return static_cast(statement) + " (" + serialize(statement.column, newContext) + ")"; } }; @@ -24510,7 +24494,7 @@ namespace sqlite_orm { context_t context{exprDBOs}; context.replace_bindable_with_question = parametrized; // just like prepare_impl() - context.skip_table_name = false; + context.omit_table_name = false; return serialize(expression, context); } @@ -24528,7 +24512,7 @@ namespace sqlite_orm { using context_t = serializer_context>; context_t context{exprDBOs}; - context.skip_table_name = false; + context.omit_table_name = false; context.replace_bindable_with_question = true; auto conection = this->get_connection(); diff --git a/tests/statement_serializer_tests/column_names.cpp b/tests/statement_serializer_tests/column_names.cpp index b1963cf82..31bbb9dc8 100644 --- a/tests/statement_serializer_tests/column_names.cpp +++ b/tests/statement_serializer_tests/column_names.cpp @@ -21,7 +21,7 @@ TEST_CASE("statement_serializer column names") { REQUIRE(value == R"("id")"); } SECTION("don't skip table name") { - context.skip_table_name = false; + context.omit_table_name = false; auto value = serialize(&User::id, context); REQUIRE(value == R"("users"."id")"); } @@ -159,7 +159,7 @@ TEST_CASE("statement_serializer column names") { SECTION("regular") { using context_t = internal::serializer_context; context_t context{dbObjects}; - context.skip_table_name = false; + context.omit_table_name = false; using als = alias_a; auto value = serialize(alias_column(&Object::id), context); REQUIRE(value == R"("a"."id")"); @@ -171,7 +171,7 @@ TEST_CASE("statement_serializer column names") { internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, 1_ctealias().as(select(1)))); using context_t = internal::serializer_context; context_t context{dbObjects2}; - context.skip_table_name = false; + context.omit_table_name = false; constexpr auto als = "a"_alias.for_<1_ctealias>(); auto value = serialize(als->*1_colalias, context); REQUIRE(value == R"("a"."1")"); diff --git a/tests/statement_serializer_tests/schema/column.cpp b/tests/statement_serializer_tests/schema/column.cpp index 0dec2393c..9fad196c6 100644 --- a/tests/statement_serializer_tests/schema/column.cpp +++ b/tests/statement_serializer_tests/schema/column.cpp @@ -14,7 +14,7 @@ TEST_CASE("statement_serializer column") { std::string value; std::string expected; SECTION("with types and constraints") { - context.fts5_columns = false; + context.omit_column_type = false; SECTION("id INTEGER (implicit) NOT NULL") { auto column = make_column("id", &User::id); value = serialize(column, context); @@ -82,7 +82,7 @@ TEST_CASE("statement_serializer column") { } } SECTION("without types and constraints") { - context.fts5_columns = true; + context.omit_column_type = true; SECTION("id INTEGER NOT NULL") { auto column = make_column("id", &User::id); value = serialize(column, context); diff --git a/tests/statement_serializer_tests/statements/select.cpp b/tests/statement_serializer_tests/statements/select.cpp index f21b125e8..1402562fb 100644 --- a/tests/statement_serializer_tests/statements/select.cpp +++ b/tests/statement_serializer_tests/statements/select.cpp @@ -192,7 +192,7 @@ TEST_CASE("statement_serializer select_t") { // issue #1106 SECTION("multi") { auto expression = columns(asterisk(), asterisk(true)); - context.skip_table_name = false; + context.omit_table_name = false; context.use_parentheses = false; stringValue = serialize(expression, context); expected = R"("users".*, "users"."id", "users"."name")"; @@ -228,7 +228,7 @@ TEST_CASE("statement_serializer select_t") { where(is_null(alias_column(&Employee::m_deptno)))); expression.highest_level = true; internal::serializer_context context{storage}; - context.skip_table_name = false; + context.omit_table_name = false; stringValue = serialize(expression, context); expected = R"(SELECT "d".* FROM "Dept" "d" LEFT JOIN "Emp" "e" ON "d"."deptno" = "e"."deptno" WHERE ("e"."deptno" IS NULL))"; diff --git a/tests/statement_serializer_tests/table_constraints/primary_key.cpp b/tests/statement_serializer_tests/table_constraints/primary_key.cpp index d843e3320..e55cb4d02 100644 --- a/tests/statement_serializer_tests/table_constraints/primary_key.cpp +++ b/tests/statement_serializer_tests/table_constraints/primary_key.cpp @@ -24,7 +24,7 @@ TEST_CASE("statement_serializer primary key table constraint") { auto dbObjects = db_objects_t{table1, table2}; using context_t = internal::serializer_context; context_t context{dbObjects}; - context.skip_table_name = false; + context.omit_table_name = false; SECTION("single column pk") { constexpr auto pk = primary_key(&User::id);