├── _config.yml ├── logo.png ├── .github ├── FUNDING.yml └── workflows │ └── clang-format-lint.yaml ├── dev ├── functional │ ├── sqlite3_config.h │ ├── finish_macros.h │ ├── start_macros.h │ ├── cxx_optional.h │ ├── cxx_string_view.h │ ├── cxx_universal.h │ ├── cstring_literal.h │ ├── mpl │ │ └── conditional.h │ ├── cxx_tuple_polyfill.h │ ├── index_sequence_util.h │ └── cxx_core_features.h ├── ast │ ├── rank.h │ ├── excluded.h │ ├── into.h │ ├── special_keywords.h │ ├── match.h │ ├── exists.h │ ├── group_by.h │ └── where.h ├── collate_argument.h ├── literal.h ├── typed_comparator.h ├── statement_finalizer.h ├── interface_definitions.h ├── tuple_helper │ ├── tuple_fy.h │ └── same_or_void.h ├── serialize_result_type.h ├── arithmetic_tag.h ├── default_value_extractor.h ├── optional_container.h ├── is_std_ptr.h ├── tags.h ├── mapped_type_proxy.h ├── values.h ├── serializer_context.h ├── column_result_proxy.h ├── implementations │ └── column_definitions.h ├── is_base_of_template.h ├── rowid.h ├── eponymous_vtabs │ └── dbstat.h ├── values_to_tuple.h ├── type_is_nullable.h ├── table_info.h ├── table_reference.h ├── sqlite_schema_table.h ├── storage_traits.h ├── locking_mode.h ├── indexed_column.h ├── sync_schema_result.h ├── connection_holder.h ├── cte_types.h ├── table_type_of.h ├── mapped_view.h └── backup.h ├── .gitignore ├── cmake └── SqliteOrmConfig.cmake.in ├── vcpkg └── triplets │ ├── x64-linux.cmake │ └── x64-osx.cmake ├── examples ├── find_package │ ├── src │ │ ├── CMakeLists.txt │ │ └── main.cpp │ └── CMakeLists.txt ├── fetch_content │ ├── src │ │ ├── CMakeLists.txt │ │ └── main.cpp │ ├── dependencies │ │ ├── sqlite_orm │ │ │ └── CMakeLists.txt │ │ └── CMakeLists.txt │ └── CMakeLists.txt ├── CMakeLists.txt ├── synchronous.cpp ├── in_memory.cpp ├── blob.cpp ├── unique.cpp ├── index.cpp ├── collate.cpp ├── composite_key.cpp ├── generated_column.cpp ├── having.cpp └── cross_join.cpp ├── tests ├── statement_serializer_tests │ ├── rowid.cpp │ ├── column_constraints │ │ ├── unique.cpp │ │ ├── null.cpp │ │ ├── not_null.cpp │ │ ├── unindexed.cpp │ │ └── default.cpp │ ├── schema │ │ ├── virtual_table.cpp │ │ ├── new_old.cpp │ │ ├── raise.cpp │ │ ├── table.cpp │ │ └── trigger.cpp │ ├── ast │ │ ├── rank.cpp │ │ ├── match.cpp │ │ ├── excluded.cpp │ │ ├── set.cpp │ │ └── dynamic_order_by.cpp │ ├── table_constraints │ │ ├── tokenize.cpp │ │ ├── prefix.cpp │ │ └── content.cpp │ ├── collate.cpp │ ├── alias_extractor.cpp │ ├── statements │ │ ├── remove.cpp │ │ └── remove_all.cpp │ ├── conditions.cpp │ ├── indexed_column.cpp │ ├── base_types.cpp │ ├── bitwise_operators.cpp │ └── arithmetic_operators.cpp ├── filename.cpp ├── catch_matchers.h ├── operators │ ├── between.cpp │ ├── is_null.cpp │ ├── cast.cpp │ ├── like.cpp │ ├── bitwise_operators.cpp │ └── not_operator.cpp ├── static_tests │ ├── static_tests_common.h │ ├── arithmetic_operators_result_type.cpp │ ├── compound_operators.cpp │ ├── functional │ │ ├── index_sequence_util.cpp │ │ ├── tuple_traits.cpp │ │ ├── same_or_void.cpp │ │ ├── tuple_conc.cpp │ │ └── static_if_tests.cpp │ ├── is_primary_key_insertable.cpp │ ├── statements.cpp │ └── is_column_with_insertable_primary_key.cpp ├── unique_cases │ ├── issue86.cpp │ ├── issue525.cpp │ ├── join_iterator_ctor_compilation_error.cpp │ ├── issue1354.cpp │ ├── prepare_get_all_with_case.cpp │ ├── delete_with_two_fields.cpp │ └── issue1357.cpp ├── prepared_statement_tests │ ├── column_names.cpp │ └── prepared_common.h ├── private_getters_tests.cpp ├── simple_query.cpp ├── builtin_tables.cpp ├── CMakeLists.txt ├── built_in_functions_tests │ └── datetime_function_tests.cpp ├── row_id.cpp ├── schema │ └── column_tests.cpp └── constraints │ └── check.cpp ├── third_party └── amalgamate │ ├── config.json │ ├── CHANGES.md │ ├── LICENSE.md │ └── README.md ├── not_single_header_include └── sqlite_orm │ └── sqlite_orm.h ├── .vscode ├── launch.json ├── tasks.json └── settings.json ├── dependencies └── CMakeLists.txt ├── COMM-LICENSE ├── packaging └── CMakeLists.txt └── TODO.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fnc12/sqlite_orm/HEAD/logo.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: fnc12 2 | custom: https://paypal.me/fnc12 3 | -------------------------------------------------------------------------------- /dev/functional/sqlite3_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_store 2 | examples/simple_neural_network.cpp 3 | 4 | cmake-build-debug/ 5 | 6 | .idea/ 7 | 8 | /compile 9 | build/ 10 | build-xcode/ 11 | -------------------------------------------------------------------------------- /cmake/SqliteOrmConfig.cmake.in: -------------------------------------------------------------------------------- 1 | include(CMakeFindDependencyMacro) 2 | find_dependency(SQLite3) 3 | 4 | include(${CMAKE_CURRENT_LIST_DIR}/SqliteOrmTargets.cmake) 5 | -------------------------------------------------------------------------------- /dev/functional/finish_macros.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(_MSC_VER) 4 | __pragma(pop_macro("max")) 5 | __pragma(pop_macro("min")) 6 | #endif // defined(_MSC_VER) 7 | -------------------------------------------------------------------------------- /vcpkg/triplets/x64-linux.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE dynamic) 4 | 5 | set(VCPKG_CMAKE_SYSTEM_NAME Linux) 6 | -------------------------------------------------------------------------------- /examples/find_package/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(Test) 2 | target_link_libraries(Test PRIVATE sqlite_orm::sqlite_orm) 3 | 4 | target_sources(Test 5 | PRIVATE 6 | main.cpp 7 | ) -------------------------------------------------------------------------------- /examples/fetch_content/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(Test) 2 | target_link_libraries(Test PRIVATE sqlite_orm::sqlite_orm) 3 | 4 | target_sources(Test 5 | PRIVATE 6 | main.cpp 7 | ) -------------------------------------------------------------------------------- /dev/functional/start_macros.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(_MSC_VER) 4 | __pragma(push_macro("min")) 5 | #undef min 6 | __pragma(push_macro("max")) 7 | #undef max 8 | #endif // defined(_MSC_VER) 9 | -------------------------------------------------------------------------------- /examples/fetch_content/dependencies/sqlite_orm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set here options for sliteOrm 2 | # for example set(BUILD_TESTING ON) if you want to have tests included of sqliteOrm 3 | FetchContent_MakeAvailable(sqliteOrm) -------------------------------------------------------------------------------- /vcpkg/triplets/x64-osx.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE dynamic) 4 | 5 | set(VCPKG_CMAKE_SYSTEM_NAME Darwin) 6 | set(VCPKG_OSX_ARCHITECTURES x86_64) 7 | -------------------------------------------------------------------------------- /dev/ast/rank.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace sqlite_orm { 4 | namespace internal { 5 | struct rank_t {}; 6 | } 7 | 8 | inline internal::rank_t rank() { 9 | return {}; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /dev/collate_argument.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace sqlite_orm { 4 | 5 | namespace internal { 6 | 7 | enum class collate_argument { 8 | binary, 9 | nocase, 10 | rtrim, 11 | }; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /dev/functional/cxx_optional.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cxx_core_features.h" 4 | 5 | #if SQLITE_ORM_HAS_INCLUDE() 6 | #include 7 | #endif 8 | 9 | #if __cpp_lib_optional >= 201606L 10 | #define SQLITE_ORM_OPTIONAL_SUPPORTED 11 | #endif 12 | -------------------------------------------------------------------------------- /dev/functional/cxx_string_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cxx_core_features.h" 4 | 5 | #if SQLITE_ORM_HAS_INCLUDE() 6 | #include 7 | #endif 8 | 9 | #if __cpp_lib_string_view >= 201606L 10 | #define SQLITE_ORM_STRING_VIEW_SUPPORTED 11 | #endif 12 | -------------------------------------------------------------------------------- /examples/fetch_content/dependencies/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | # You can configure this for your need, presumbably you want speificy a git tag here instead of a branch 4 | FetchContent_Declare(sqliteOrm 5 | GIT_REPOSITORY https://github.com/fnc12/sqlite_orm 6 | GIT_TAG origin/dev 7 | ) 8 | 9 | add_subdirectory(sqlite_orm) 10 | -------------------------------------------------------------------------------- /.github/workflows/clang-format-lint.yaml: -------------------------------------------------------------------------------- 1 | name: clang-format lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | - name: clang-format lint 12 | uses: d-griet/clang-format-lint-action@99a106be2f3f1a92d9783ea7c744fde62d8ce1fa 13 | with: 14 | clangFormatVersion: 19 15 | -------------------------------------------------------------------------------- /dev/literal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace sqlite_orm { 4 | namespace internal { 5 | 6 | /* 7 | * Protect an otherwise bindable element so that it is always serialized as a literal value. 8 | */ 9 | template 10 | struct literal_holder { 11 | using type = T; 12 | 13 | type value; 14 | }; 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/fetch_content/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ### Preamble ### 2 | cmake_minimum_required(VERSION 3.15) 3 | project(Test VERSION 0.1 LANGUAGES CXX) 4 | 5 | ##### Project wide setup #### 6 | set(CMAKE_CXX_STANDARD 17) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | set(CMAKE_CXX_EXTENSIONS OFF) 9 | 10 | ### Dependencies ### 11 | add_subdirectory(dependencies) 12 | 13 | ### Main Targets ### 14 | add_subdirectory(src) 15 | -------------------------------------------------------------------------------- /examples/find_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ### Preamble ### 2 | cmake_minimum_required(VERSION 3.15) 3 | project(Test VERSION 0.1 LANGUAGES CXX) 4 | 5 | ##### Project wide setup #### 6 | set(CMAKE_CXX_STANDARD 17) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | set(CMAKE_CXX_EXTENSIONS OFF) 9 | 10 | ### Dependencies ### 11 | find_package(SqliteOrm REQUIRED) 12 | 13 | ### Main Targets ### 14 | add_subdirectory(src) 15 | -------------------------------------------------------------------------------- /dev/typed_comparator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace sqlite_orm { 4 | 5 | namespace internal { 6 | 7 | template 8 | bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { 9 | return false; 10 | } 11 | template 12 | bool compare_any(const O& lhs, const O& rhs) { 13 | return lhs == rhs; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /dev/statement_finalizer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include // std::unique_ptr 5 | #include // std::integral_constant 6 | 7 | namespace sqlite_orm { 8 | 9 | /** 10 | * Guard class which finalizes `sqlite3_stmt` in dtor 11 | */ 12 | using statement_finalizer = 13 | std::unique_ptr>; 14 | } 15 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/rowid.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer rowid") { 7 | { 8 | internal::db_objects_tuple<> storage; 9 | internal::serializer_context> context{storage}; 10 | auto value = serialize(rowid(), context); 11 | REQUIRE(value == "rowid"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/column_constraints/unique.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer unique") { 7 | internal::db_objects_tuple<> storage; 8 | internal::serializer_context> context{storage}; 9 | auto un = unique(); 10 | auto value = serialize(un, context); 11 | REQUIRE(value == "UNIQUE"); 12 | } 13 | -------------------------------------------------------------------------------- /dev/ast/excluded.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::move 4 | 5 | namespace sqlite_orm { 6 | namespace internal { 7 | 8 | template 9 | struct excluded_t { 10 | using expression_type = T; 11 | 12 | expression_type expression; 13 | }; 14 | } 15 | 16 | template 17 | internal::excluded_t excluded(T expression) { 18 | return {std::move(expression)}; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/column_constraints/null.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer null") { 7 | internal::db_objects_tuple<> storage; 8 | internal::serializer_context> context{storage}; 9 | auto statement = null(); 10 | auto value = serialize(statement, context); 11 | REQUIRE(value == "NULL"); 12 | } 13 | -------------------------------------------------------------------------------- /third_party/amalgamate/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "project": "SQLite ORM", 3 | "target": "include/sqlite_orm/sqlite_orm.h", 4 | "sources": [ 5 | "dev/functional/start_macros.h", 6 | "dev/functional/sqlite3_config.h", 7 | "dev/functional/config.h", 8 | "dev/storage.h", 9 | "dev/interface_definitions.h", 10 | "dev/get_prepared_statement.h", 11 | "dev/carray.h", 12 | "dev/functional/finish_macros.h" 13 | ], 14 | "include_paths": [ "dev" ] 15 | } 16 | -------------------------------------------------------------------------------- /dev/ast/into.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../functional/cxx_type_traits_polyfill.h" 4 | 5 | namespace sqlite_orm { 6 | namespace internal { 7 | 8 | template 9 | struct into_t { 10 | using type = T; 11 | }; 12 | 13 | template 14 | using is_into = polyfill::is_specialization_of; 15 | } 16 | 17 | template 18 | internal::into_t into() { 19 | return {}; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/column_constraints/not_null.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer not null") { 7 | internal::db_objects_tuple<> storage; 8 | internal::serializer_context> context{storage}; 9 | auto statement = not_null(); 10 | auto value = serialize(statement, context); 11 | REQUIRE(value == "NOT NULL"); 12 | } 13 | -------------------------------------------------------------------------------- /dev/interface_definitions.h: -------------------------------------------------------------------------------- 1 | /** @file Mainly existing to disentangle implementation details from circular and cross dependencies 2 | * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) 3 | * this file is also used to provide definitions of interface methods 'hitting the database'. 4 | */ 5 | #pragma once 6 | 7 | #include "implementations/column_definitions.h" 8 | #include "implementations/table_definitions.h" 9 | #include "implementations/storage_definitions.h" 10 | -------------------------------------------------------------------------------- /dev/tuple_helper/tuple_fy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace sqlite_orm { 6 | 7 | namespace internal { 8 | 9 | template 10 | struct tuplify { 11 | using type = std::tuple; 12 | }; 13 | template 14 | struct tuplify> { 15 | using type = std::tuple; 16 | }; 17 | 18 | template 19 | using tuplify_t = typename tuplify::type; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /third_party/amalgamate/CHANGES.md: -------------------------------------------------------------------------------- 1 | The following changes have been made to the code with respect to : 2 | 3 | - Resolved inspection results from PyCharm: 4 | - replaced tabs with spaces 5 | - added encoding annotation 6 | - reindented file to remove trailing whitespaces 7 | - unused import `sys` 8 | - membership check 9 | - made function from `_is_within` 10 | - removed unused variable `actual_path` 11 | - normalized paths 12 | -------------------------------------------------------------------------------- /dev/ast/special_keywords.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace sqlite_orm { 4 | namespace internal { 5 | struct current_time_t {}; 6 | struct current_date_t {}; 7 | struct current_timestamp_t {}; 8 | } 9 | 10 | inline internal::current_time_t current_time() { 11 | return {}; 12 | } 13 | 14 | inline internal::current_date_t current_date() { 15 | return {}; 16 | } 17 | 18 | inline internal::current_timestamp_t current_timestamp() { 19 | return {}; 20 | } 21 | } -------------------------------------------------------------------------------- /tests/statement_serializer_tests/column_constraints/unindexed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if SQLITE_VERSION_NUMBER >= 3009000 5 | using namespace sqlite_orm; 6 | 7 | TEST_CASE("statement_serializer unindexed") { 8 | internal::db_objects_tuple<> storage; 9 | internal::serializer_context> context{storage}; 10 | auto node = unindexed(); 11 | auto value = serialize(node, context); 12 | REQUIRE(value == "UNINDEXED"); 13 | } 14 | #endif 15 | -------------------------------------------------------------------------------- /tests/filename.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("filename") { 7 | { 8 | auto storage = make_storage(""); 9 | REQUIRE(storage.filename() == ""); 10 | } 11 | { 12 | auto storage = make_storage(":memory:"); 13 | REQUIRE(storage.filename() == ":memory:"); 14 | } 15 | { 16 | auto storage = make_storage("myDatabase.sqlite"); 17 | REQUIRE(storage.filename() == "myDatabase.sqlite"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dev/serialize_result_type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "functional/cxx_string_view.h" 4 | #ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED 5 | #include // std::string 6 | #endif 7 | 8 | namespace sqlite_orm { 9 | namespace internal { 10 | #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED 11 | using serialize_result_type = std::string_view; 12 | using serialize_arg_type = std::string_view; 13 | #else 14 | using serialize_result_type = std::string; 15 | using serialize_arg_type = const std::string&; 16 | #endif 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /dev/ast/match.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::move 4 | 5 | namespace sqlite_orm { 6 | namespace internal { 7 | 8 | template 9 | struct match_t { 10 | using mapped_type = T; 11 | using argument_type = X; 12 | 13 | argument_type argument; 14 | 15 | match_t(argument_type argument) : argument(std::move(argument)) {} 16 | }; 17 | } 18 | 19 | template 20 | internal::match_t match(X argument) { 21 | return {std::move(argument)}; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/catch_matchers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class ErrorCodeExceptionMatcher : public Catch::Matchers::MatcherGenericBase { 7 | public: 8 | ErrorCodeExceptionMatcher(std::error_code errorCode) : errorCode(std::move(errorCode)) {} 9 | 10 | bool match(const std::system_error& systemError) const { 11 | return systemError.code() == this->errorCode; 12 | } 13 | 14 | protected: 15 | std::string describe() const override { 16 | std::stringstream ss; 17 | ss << this->errorCode; 18 | return ss.str(); 19 | } 20 | 21 | private: 22 | std::error_code errorCode; 23 | }; 24 | -------------------------------------------------------------------------------- /not_single_header_include/sqlite_orm/sqlite_orm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../../dev/functional/start_macros.h" 3 | // pull in the SQLite3 configuration early, such that version and feature macros are globally available in sqlite_orm 4 | #include "../../dev/functional/sqlite3_config.h" 5 | // though each header is required to include everything it needs 6 | // we include the configuration and all underlying c++ core features in order to make it universally available 7 | #include "../../dev/functional/config.h" 8 | #include "../../dev/storage.h" 9 | #include "../../dev/interface_definitions.h" 10 | #include "../../dev/get_prepared_statement.h" 11 | #include "../../dev/carray.h" 12 | #include "../../dev/functional/finish_macros.h" 13 | -------------------------------------------------------------------------------- /dev/arithmetic_tag.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::is_integral 4 | 5 | #include "functional/mpl/conditional.h" 6 | 7 | namespace sqlite_orm { 8 | 9 | /** 10 | * Helper classes used by statement_binder and row_extractor. 11 | */ 12 | struct int_or_smaller_tag {}; 13 | struct bigint_tag {}; 14 | struct real_tag {}; 15 | 16 | template 17 | using arithmetic_tag_t = 18 | mpl::conditional_t::value, 19 | // Integer class 20 | mpl::conditional_t, 21 | // Floating-point class 22 | real_tag>; 23 | } 24 | -------------------------------------------------------------------------------- /tests/operators/between.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("Between") { 7 | struct Object { 8 | int id = 0; 9 | }; 10 | 11 | auto storage = 12 | make_storage("", make_table("objects", make_column("id", &Object::id, primary_key().autoincrement()))); 13 | storage.sync_schema(); 14 | 15 | storage.insert(Object{}); 16 | storage.insert(Object{}); 17 | storage.insert(Object{}); 18 | storage.insert(Object{}); 19 | storage.insert(Object{}); 20 | 21 | auto allObjects = storage.get_all(); 22 | auto rows = storage.select(&Object::id, where(between(&Object::id, 1, 3))); 23 | REQUIRE(rows.size() == 3); 24 | } 25 | -------------------------------------------------------------------------------- /dev/default_value_extractor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::string 4 | 5 | #include "constraints.h" 6 | #include "serializer_context.h" 7 | #include "storage_lookup.h" 8 | 9 | namespace sqlite_orm { 10 | 11 | namespace internal { 12 | 13 | template 14 | auto serialize(const T& t, const Ctx& context); 15 | 16 | /** 17 | * Serialize a column's default value. 18 | */ 19 | template 20 | std::string serialize_default_value(const default_t& dft) { 21 | db_objects_tuple<> dbObjects; 22 | serializer_context> context{dbObjects}; 23 | return serialize(dft.value, context); 24 | } 25 | 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/schema/virtual_table.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if SQLITE_VERSION_NUMBER >= 3009000 5 | using namespace sqlite_orm; 6 | 7 | TEST_CASE("statement_serializer FTS5") { 8 | struct Post { 9 | std::string title; 10 | std::string body; 11 | }; 12 | internal::db_objects_tuple<> storage; 13 | internal::serializer_context> context{storage}; 14 | auto node = 15 | make_virtual_table("posts", using_fts5(make_column("title", &Post::title), make_column("body", &Post::body))); 16 | auto value = serialize(node, context); 17 | REQUIRE(value == R"(CREATE VIRTUAL TABLE IF NOT EXISTS "posts" USING FTS5("title", "body"))"); 18 | } 19 | #endif 20 | -------------------------------------------------------------------------------- /tests/static_tests/static_tests_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::unique_ptr 4 | #include // std::string 5 | 6 | struct User { 7 | int id; 8 | std::unique_ptr name; 9 | 10 | const int& getIdByRefConst() const { 11 | return this->id; 12 | } 13 | 14 | const int& getIdByRef() { 15 | return this->id; 16 | } 17 | 18 | int getIdByValConst() const { 19 | return this->id; 20 | } 21 | 22 | void setIdByVal(int id_) { 23 | this->id = id_; 24 | } 25 | 26 | void setIdByConstRef(const int& id_) { 27 | this->id = id_; 28 | } 29 | 30 | void setIdByRef(int& id_) { 31 | this->id = id_; 32 | } 33 | }; 34 | 35 | struct Object { 36 | int id; 37 | }; 38 | 39 | struct Token : Object {}; 40 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/ast/rank.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer rank") { 7 | using db_objects_t = internal::db_objects_tuple<>; 8 | auto dbObjects = db_objects_t{}; 9 | using context_t = internal::serializer_context; 10 | context_t context{dbObjects}; 11 | std::string value; 12 | std::string expected; 13 | SECTION("rank") { 14 | auto node = rank(); 15 | value = serialize(node, context); 16 | expected = "rank"; 17 | } 18 | SECTION("order by rank") { 19 | auto node = order_by(rank()); 20 | value = serialize(node, context); 21 | expected = "ORDER BY rank"; 22 | } 23 | REQUIRE(value == expected); 24 | } 25 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/ast/match.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if SQLITE_VERSION_NUMBER >= 3009000 5 | using namespace sqlite_orm; 6 | 7 | TEST_CASE("statement_serializer match") { 8 | struct User { 9 | int id = 0; 10 | std::string name; 11 | }; 12 | auto table = 13 | make_virtual_table("users", using_fts5(make_column("id", &User::id), make_column("name", &User::name))); 14 | using db_objects_t = internal::db_objects_tuple; 15 | auto dbObjects = db_objects_t{table}; 16 | using context_t = internal::serializer_context; 17 | context_t context{dbObjects}; 18 | auto node = match("Claude"); 19 | auto value = serialize(node, context); 20 | REQUIRE(value == R"("users" MATCH 'Claude')"); 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /dev/optional_container.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace sqlite_orm { 4 | 5 | namespace internal { 6 | 7 | /** 8 | * This is a cute class which allows storing something or nothing 9 | * depending on template argument. Useful for optional class members 10 | */ 11 | template 12 | struct optional_container { 13 | using type = T; 14 | 15 | type field; 16 | 17 | template 18 | void apply(const L& l) const { 19 | l(this->field); 20 | } 21 | }; 22 | 23 | template<> 24 | struct optional_container { 25 | using type = void; 26 | 27 | template 28 | void apply(const L&) const { 29 | //.. 30 | } 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/static_tests/arithmetic_operators_result_type.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // std::is_same 4 | 5 | using namespace sqlite_orm; 6 | 7 | TEST_CASE("Arithmetic operators result type") { 8 | STATIC_REQUIRE( 9 | std::is_same, decltype(add(1, 2))>::type, double>::value); 10 | STATIC_REQUIRE( 11 | std::is_same, decltype(sub(2, 1))>::type, double>::value); 12 | STATIC_REQUIRE( 13 | std::is_same, decltype(mul(2, 3))>::type, double>::value); 14 | STATIC_REQUIRE(std::is_same, decltype(sqlite_orm::div(2, 3))>::type, 15 | double>::value); 16 | } 17 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/table_constraints/tokenize.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer tokenize") { 7 | internal::db_objects_tuple<> storage; 8 | internal::serializer_context> context{storage}; 9 | std::string value; 10 | std::string expected; 11 | SECTION("porter ascii") { 12 | auto node = tokenize("porter ascii"); 13 | value = serialize(node, context); 14 | expected = "tokenize = 'porter ascii'"; 15 | } 16 | SECTION("unicode61 remove_diacritics 1") { 17 | auto node = tokenize("unicode61 remove_diacritics 1"); 18 | value = serialize(node, context); 19 | expected = "tokenize = 'unicode61 remove_diacritics 1'"; 20 | } 21 | REQUIRE(value == expected); 22 | } 23 | -------------------------------------------------------------------------------- /tests/static_tests/compound_operators.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // std::tuple_size 4 | 5 | #include "static_tests_common.h" 6 | 7 | using namespace sqlite_orm; 8 | using internal::is_compound_operator_v; 9 | using std::tuple_size; 10 | 11 | template 12 | void runTest(Compound) { 13 | STATIC_REQUIRE(is_compound_operator_v); 14 | STATIC_REQUIRE(tuple_size::value == 3); 15 | } 16 | 17 | TEST_CASE("Compound operators") { 18 | runTest(union_(select(&User::id), select(&User::id), select(&Token::id))); 19 | runTest(union_all(select(&User::id), select(&User::id), select(&Token::id))); 20 | runTest(except(select(&User::id), select(&User::id), select(&Token::id))); 21 | runTest(intersect(select(&User::id), select(&User::id), select(&Token::id))); 22 | } 23 | -------------------------------------------------------------------------------- /dev/functional/cxx_universal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * This header makes central C++ functionality on which sqlite_orm depends universally available: 5 | * - alternative operator representations 6 | * - ::size_t, ::ptrdiff_t, ::nullptr_t 7 | * - C++ core language feature macros 8 | * - macros for dealing with compiler quirks 9 | */ 10 | 11 | #include // alternative operator representations 12 | #include // sqlite_orm is using size_t, ptrdiff_t, nullptr_t everywhere, pull it in early 13 | 14 | // earlier clang versions didn't make nullptr_t available in the global namespace via stddef.h, 15 | // though it should have according to C++ documentation (see https://en.cppreference.com/w/cpp/types/nullptr_t#Notes). 16 | // actually it should be available when including stddef.h 17 | using std::nullptr_t; 18 | 19 | #include "cxx_core_features.h" 20 | #include "cxx_compiler_quirks.h" 21 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/collate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer collate") { 7 | internal::db_objects_tuple<> storage; 8 | internal::serializer_context> context{storage}; 9 | std::string value; 10 | std::string expected; 11 | SECTION("COLLATE NOCASE") { 12 | auto col = collate_nocase(); 13 | value = serialize(col, context); 14 | expected = "COLLATE NOCASE"; 15 | } 16 | SECTION("COLLATE BINARY") { 17 | auto col = collate_binary(); 18 | value = serialize(col, context); 19 | expected = "COLLATE BINARY"; 20 | } 21 | SECTION("COLLATE RTRIM") { 22 | auto col = collate_rtrim(); 23 | value = serialize(col, context); 24 | expected = "COLLATE RTRIM"; 25 | } 26 | REQUIRE(value == expected); 27 | } 28 | -------------------------------------------------------------------------------- /dev/is_std_ptr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace sqlite_orm { 7 | 8 | /** 9 | * Specialization for optional type (std::shared_ptr / std::unique_ptr). 10 | */ 11 | template 12 | struct is_std_ptr : std::false_type {}; 13 | 14 | template 15 | struct is_std_ptr> : std::true_type { 16 | using element_type = typename std::shared_ptr::element_type; 17 | 18 | static std::shared_ptr make(std::remove_cv_t&& v) { 19 | return std::make_shared(std::move(v)); 20 | } 21 | }; 22 | 23 | template 24 | struct is_std_ptr> : std::true_type { 25 | using element_type = typename std::unique_ptr::element_type; 26 | 27 | static auto make(std::remove_cv_t&& v) { 28 | return std::make_unique(std::move(v)); 29 | } 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/table_constraints/prefix.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if SQLITE_VERSION_NUMBER >= 3009000 5 | using namespace sqlite_orm; 6 | 7 | TEST_CASE("statement_serializer prefix") { 8 | internal::db_objects_tuple<> storage; 9 | internal::serializer_context> context{storage}; 10 | std::string value; 11 | std::string expected; 12 | SECTION("2") { 13 | auto node = prefix(2); 14 | value = serialize(node, context); 15 | expected = "prefix=2"; 16 | } 17 | SECTION("3") { 18 | auto node = prefix(3); 19 | value = serialize(node, context); 20 | expected = "prefix=3"; 21 | } 22 | SECTION("2 3") { 23 | auto node = prefix("2 3"); 24 | value = serialize(node, context); 25 | expected = "prefix='2 3'"; 26 | } 27 | REQUIRE(value == expected); 28 | } 29 | #endif 30 | -------------------------------------------------------------------------------- /dev/tags.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "functional/cxx_functional_polyfill.h" 4 | 5 | namespace sqlite_orm { 6 | namespace internal { 7 | struct negatable_t {}; 8 | 9 | /** 10 | * Inherit from this class to support arithmetic types overloading 11 | */ 12 | struct arithmetic_t {}; 13 | 14 | /** 15 | * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators 16 | */ 17 | struct condition_t {}; 18 | 19 | /** 20 | * Specialize if a type participates as an argument to overloaded operators (arithmetic, conditional, negation, chaining) 21 | */ 22 | template 23 | SQLITE_ORM_INLINE_VAR constexpr bool is_operator_argument_v = false; 24 | 25 | template 26 | using is_operator_argument = polyfill::bool_constant>; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /dev/functional/cstring_literal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef SQLITE_ORM_WITH_CPP20_ALIASES 4 | #include // std::index_sequence 5 | #include // std::copy_n 6 | #endif 7 | 8 | #ifdef SQLITE_ORM_WITH_CPP20_ALIASES 9 | namespace sqlite_orm::internal { 10 | /* 11 | * Wraps a C string of fixed size. 12 | * Its main purpose is to enable the user-defined string literal operator template. 13 | */ 14 | template 15 | struct cstring_literal { 16 | static constexpr size_t size() { 17 | return N - 1; 18 | } 19 | 20 | constexpr cstring_literal(const char (&cstr)[N]) { 21 | std::copy_n(cstr, N, this->cstr); 22 | } 23 | 24 | char cstr[N]; 25 | }; 26 | 27 | template class Template, cstring_literal literal, size_t... Idx> 28 | consteval auto explode_into(std::index_sequence) { 29 | return Template{}; 30 | } 31 | } 32 | #endif 33 | -------------------------------------------------------------------------------- /dev/mapped_type_proxy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::remove_const 4 | 5 | #include "type_traits.h" 6 | #include "table_reference.h" 7 | #include "alias_traits.h" 8 | 9 | namespace sqlite_orm { 10 | 11 | namespace internal { 12 | 13 | /** 14 | * If T is a table reference or recordset alias then the typename mapped_type_proxy::type is the unqualified aliased type, 15 | * otherwise unqualified T. 16 | */ 17 | template 18 | struct mapped_type_proxy : std::remove_const {}; 19 | 20 | #ifdef SQLITE_ORM_WITH_CPP20_ALIASES 21 | template 22 | struct mapped_type_proxy : R {}; 23 | #endif 24 | 25 | template 26 | struct mapped_type_proxy> : std::remove_const> {}; 27 | 28 | template 29 | using mapped_type_proxy_t = typename mapped_type_proxy::type; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run unit_tests", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/build/tests/unit_tests", 9 | "args": [], 10 | "cwd": "${workspaceFolder}", 11 | "stopAtEntry": false, 12 | "environment": [], 13 | "externalConsole": false, 14 | "MIMode": "lldb" 15 | }, 16 | { 17 | "name": "Run amalgamate script", 18 | "type": "python", 19 | "request": "launch", 20 | "program": "${workspaceFolder}/third_party/amalgamate/amalgamate.py", 21 | "args": [ 22 | "-c", "${workspaceFolder}/third_party/amalgamate/config.json", 23 | "-s", "${workspaceFolder}" 24 | ], 25 | "cwd": "${workspaceFolder}", 26 | "console": "integratedTerminal" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | # note: find_package(SQLite3 REQUIRED) already done in top-level CMakeLists 4 | 5 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 6 | 7 | file(GLOB files "*.cpp") 8 | 9 | set(run_example_targets) 10 | 11 | foreach(file ${files}) 12 | get_filename_component(file_basename ${file} NAME_WE) 13 | 14 | add_executable(${file_basename} ${file}) 15 | # note: sqlite3 already linked in top-level CMakeLists 16 | 17 | target_link_libraries(${file_basename} PRIVATE sqlite_orm) 18 | 19 | add_custom_target(run_${file_basename} 20 | COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${file_basename} 21 | DEPENDS ${file_basename} 22 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 23 | COMMENT "Running example: ${file_basename}" 24 | VERBATIM 25 | ) 26 | 27 | list(APPEND run_example_targets run_${file_basename}) 28 | endforeach() 29 | 30 | add_custom_target(run_all_examples 31 | DEPENDS ${run_example_targets} 32 | ) 33 | -------------------------------------------------------------------------------- /tests/unique_cases/issue86.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("Issue 86") { 7 | struct Data { 8 | uint8_t mUsed = 0; 9 | int mId = 0; 10 | int mCountryId = 0; 11 | int mLangId = 0; 12 | std::string mName; 13 | }; 14 | 15 | auto storage = make_storage("", 16 | make_table("cities", 17 | make_column("U", &Data::mUsed), 18 | make_column("Id", &Data::mId), 19 | make_column("CntId", &Data::mCountryId), 20 | make_column("LId", &Data::mLangId), 21 | make_column("N", &Data::mName))); 22 | storage.sync_schema(); 23 | std::string CurrCity = "ototo"; 24 | auto vms = storage.select(columns(&Data::mId, &Data::mLangId), where(like(&Data::mName, CurrCity))); 25 | } 26 | -------------------------------------------------------------------------------- /dev/ast/exists.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::move 4 | 5 | #include "../tags.h" 6 | 7 | namespace sqlite_orm { 8 | namespace internal { 9 | 10 | template 11 | struct exists_t : condition_t, negatable_t { 12 | using expression_type = T; 13 | using self = exists_t; 14 | 15 | expression_type expression; 16 | 17 | exists_t(expression_type expression_) : expression(std::move(expression_)) {} 18 | }; 19 | } 20 | 21 | /** 22 | * EXISTS(condition). 23 | * Example: storage.select(columns(&Agent::code, &Agent::name, &Agent::workingArea, &Agent::comission), 24 | where(exists(select(asterisk(), 25 | where(is_equal(&Customer::grade, 3) and 26 | is_equal(&Agent::code, &Customer::agentCode))))), 27 | order_by(&Agent::comission)); 28 | */ 29 | template 30 | internal::exists_t exists(T expression) { 31 | return {std::move(expression)}; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/alias_extractor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | using internal::alias_extractor; 6 | 7 | TEST_CASE("alias extractor") { 8 | struct User {}; 9 | 10 | SECTION("column alias") { 11 | REQUIRE(alias_extractor::extract() == "a"); 12 | REQUIRE(alias_extractor::as_alias() == "a"); 13 | } 14 | SECTION("table") { 15 | REQUIRE(alias_extractor::extract() == ""); 16 | REQUIRE(alias_extractor::as_alias() == ""); 17 | } 18 | SECTION("table alias") { 19 | REQUIRE(alias_extractor>::extract() == "a"); 20 | REQUIRE(alias_extractor>::as_alias() == "a"); 21 | } 22 | #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) 23 | SECTION("cte moniker") { 24 | using cte_1 = decltype(1_ctealias); 25 | REQUIRE(alias_extractor::extract() == "1"); 26 | REQUIRE(alias_extractor::as_alias() == ""); 27 | } 28 | #endif 29 | } 30 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/schema/new_old.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer new/old") { 7 | using internal::serialize; 8 | struct User { 9 | int id = 0; 10 | std::string name; 11 | }; 12 | auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); 13 | using db_objects_t = internal::db_objects_tuple; 14 | auto dbObjects = db_objects_t{table}; 15 | using context_t = internal::serializer_context; 16 | context_t context{dbObjects}; 17 | std::string value; 18 | decltype(value) expected; 19 | SECTION("new") { 20 | auto expression = new_(&User::id); 21 | value = serialize(expression, context); 22 | expected = R"(NEW."id")"; 23 | } 24 | SECTION("old") { 25 | auto expression = old(&User::id); 26 | value = serialize(expression, context); 27 | expected = R"(OLD."id")"; 28 | } 29 | REQUIRE(value == expected); 30 | } 31 | -------------------------------------------------------------------------------- /dependencies/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) 4 | # FIND_PACKAGE_ARGS is available since 3.24 5 | if(CMAKE_VERSION VERSION_LESS 3.24) 6 | FetchContent_Declare( 7 | Catch2 8 | GIT_REPOSITORY https://github.com/catchorg/Catch2.git 9 | GIT_TAG v3.2.1 10 | ) 11 | else() 12 | FetchContent_Declare( 13 | Catch2 14 | GIT_REPOSITORY https://github.com/catchorg/Catch2.git 15 | GIT_TAG v3.2.1 16 | # prefer find_package() over building from source 17 | FIND_PACKAGE_ARGS 3 CONFIG 18 | ) 19 | endif() 20 | endif() 21 | 22 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING) 23 | # CMake <3.24: exposes targets only locally, but caches them. So call FetchContent_MakeAvailable again in the directory of usage 24 | FetchContent_MakeAvailable(Catch2) 25 | endif() 26 | 27 | # CMake <3.24: exposes targets only locally, but caches them. So call find_package again in the directory of usage 28 | find_package(SQLite3 REQUIRED) 29 | -------------------------------------------------------------------------------- /tests/prepared_statement_tests/column_names.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED 7 | 8 | using namespace std::literals; 9 | 10 | TEST_CASE("column_name_method") { 11 | 12 | struct UserIdAlias : alias_tag { 13 | static const std::string& get() { 14 | static const std::string res = "USER_ID"; 15 | return res; 16 | } 17 | }; 18 | 19 | struct User { 20 | int id = 0; 21 | std::string name; 22 | }; 23 | auto storage = make_storage("column_name.sqlite", 24 | make_table("users", make_column("id", &User::id), make_column("name", &User::name))); 25 | 26 | storage.sync_schema(); 27 | 28 | auto statement = storage.prepare(select(columns(as(&User::id), &User::name))); 29 | std::string id_header{statement.column_name(0)}; 30 | std::string_view name_header = statement.column_name(1); 31 | 32 | REQUIRE(id_header == "USER_ID"s); 33 | REQUIRE(name_header == "name"s); 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /COMM-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2023 Eugene Zakharov and others 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /tests/operators/is_null.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("Is null") { 7 | struct User { 8 | int id = 0; 9 | std::unique_ptr name; 10 | 11 | #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED 12 | User() = default; 13 | User(int id, decltype(name) name = nullptr) : id{id}, name{std::move(name)} {} 14 | #endif 15 | }; 16 | auto storage = make_storage( 17 | "", 18 | make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name))); 19 | storage.sync_schema(); 20 | 21 | REQUIRE(storage.count() == 0); 22 | storage.replace(User{1, std::make_unique("Sheldon")}); 23 | REQUIRE(storage.count() == 1); 24 | storage.replace(User{2}); 25 | REQUIRE(storage.count() == 2); 26 | storage.replace(User{3, std::make_unique("Leonard")}); 27 | REQUIRE(storage.count() == 3); 28 | REQUIRE(storage.count(where(is_null(&User::name))) == 1); 29 | REQUIRE(storage.count(where(is_not_null(&User::name))) == 2); 30 | } 31 | -------------------------------------------------------------------------------- /tests/static_tests/functional/index_sequence_util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using std::index_sequence; 5 | using namespace sqlite_orm; 6 | using internal::flatten_idxseq_t; 7 | using internal::index_sequence_value_at; 8 | 9 | TEST_CASE("index sequence value at") { 10 | STATIC_REQUIRE(index_sequence_value_at<0>(index_sequence<1, 3>{}) == 1); 11 | #if defined(SQLITE_ORM_PACK_INDEXING_SUPPORTED) || defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) 12 | STATIC_REQUIRE(index_sequence_value_at<1>(index_sequence<1, 3>{}) == 3); 13 | #endif 14 | } 15 | 16 | TEST_CASE("flatten index sequence") { 17 | STATIC_REQUIRE(std::is_same, index_sequence<>>::value); 18 | STATIC_REQUIRE(std::is_same>, index_sequence<1, 3>>::value); 19 | STATIC_REQUIRE( 20 | std::is_same, index_sequence<1, 3>>, index_sequence<1, 3, 1, 3>>::value); 21 | STATIC_REQUIRE(std::is_same, index_sequence<1, 3>, index_sequence<1, 3>>, 22 | index_sequence<1, 3, 1, 3, 1, 3>>::value); 23 | } 24 | -------------------------------------------------------------------------------- /dev/values.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::vector 4 | #include // std::tuple 5 | #include // std::forward, std::move 6 | 7 | #include "functional/cxx_type_traits_polyfill.h" 8 | 9 | namespace sqlite_orm { 10 | 11 | namespace internal { 12 | 13 | template 14 | struct values_t { 15 | using args_tuple = std::tuple; 16 | 17 | args_tuple tuple; 18 | }; 19 | 20 | template 21 | SQLITE_ORM_INLINE_VAR constexpr bool is_values_v = polyfill::is_specialization_of::value; 22 | 23 | template 24 | using is_values = polyfill::bool_constant>; 25 | 26 | template 27 | struct dynamic_values_t { 28 | std::vector vector; 29 | }; 30 | 31 | } 32 | 33 | template 34 | internal::values_t values(Args... args) { 35 | return {{std::forward(args)...}}; 36 | } 37 | 38 | template 39 | internal::dynamic_values_t values(std::vector vector) { 40 | return {{std::move(vector)}}; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/private_getters_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("Issue 343") { 7 | class A { 8 | public: 9 | A() = default; 10 | 11 | A(int id_, std::string name_) : name(std::move(name_)), id(id_) {} 12 | 13 | int getId() const { 14 | return this->id; 15 | } 16 | 17 | void setId(int id_) { 18 | this->id = id_; 19 | } 20 | 21 | std::string name; 22 | 23 | private: 24 | int id = 0; 25 | }; 26 | 27 | auto storage = make_storage( 28 | {}, 29 | make_table("a", make_column("role", &A::getId, &A::setId, primary_key()), make_column("name", &A::name))); 30 | storage.sync_schema(); 31 | 32 | A object(1, "Ototo"); 33 | storage.insert(object); 34 | 35 | storage.replace(object); 36 | 37 | storage.update(object); 38 | 39 | storage.remove(object.getId()); 40 | 41 | std::vector vec{object}; 42 | storage.insert_range(vec.begin(), vec.end()); 43 | 44 | storage.replace_range(vec.begin(), vec.end()); 45 | 46 | auto all = storage.get_all(); 47 | std::ignore = all; 48 | } 49 | -------------------------------------------------------------------------------- /dev/serializer_context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace sqlite_orm { 4 | 5 | namespace internal { 6 | 7 | struct serializer_context_base { 8 | bool replace_bindable_with_question = false; 9 | bool skip_table_name = true; 10 | bool use_parentheses = true; 11 | bool fts5_columns = false; 12 | }; 13 | 14 | template 15 | struct serializer_context : serializer_context_base { 16 | using db_objects_type = DBOs; 17 | 18 | const db_objects_type& db_objects; 19 | 20 | serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} 21 | }; 22 | 23 | template 24 | struct serializer_context_builder { 25 | using storage_type = S; 26 | using db_objects_type = typename storage_type::db_objects_type; 27 | 28 | serializer_context_builder(const storage_type& storage_) : storage{storage_} {} 29 | 30 | serializer_context operator()() const { 31 | return {obtain_db_objects(this->storage)}; 32 | } 33 | 34 | const storage_type& storage; 35 | }; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/statements/remove.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer remove") { 7 | using internal::serialize; 8 | struct User { 9 | int id = 0; 10 | std::string name; 11 | }; 12 | auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)); 13 | using db_objects_t = internal::db_objects_tuple; 14 | db_objects_t dbObjects{table}; 15 | using context_t = internal::serializer_context; 16 | context_t context{dbObjects}; 17 | 18 | std::string value; 19 | decltype(value) expected; 20 | 21 | auto statement = remove(5); 22 | SECTION("with question marks") { 23 | context.replace_bindable_with_question = true; 24 | expected = R"(DELETE FROM "users" WHERE "id" = ?)"; 25 | } 26 | SECTION("without question marks") { 27 | context.replace_bindable_with_question = false; 28 | expected = R"(DELETE FROM "users" WHERE "id" = 5)"; 29 | } 30 | value = serialize(statement, context); 31 | 32 | REQUIRE(value == expected); 33 | } 34 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/schema/raise.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer raise") { 7 | using internal::serialize; 8 | 9 | std::string value; 10 | decltype(value) expected; 11 | 12 | internal::db_objects_tuple<> storage; 13 | internal::serializer_context> context{storage}; 14 | SECTION("ignore") { 15 | auto expression = raise_ignore(); 16 | value = serialize(expression, context); 17 | expected = "RAISE(IGNORE)"; 18 | } 19 | SECTION("rollback") { 20 | auto expression = raise_rollback("no rap"); 21 | value = serialize(expression, context); 22 | expected = "RAISE(ROLLBACK, 'no rap')"; 23 | } 24 | SECTION("abort") { 25 | auto expression = raise_abort("no rap"); 26 | value = serialize(expression, context); 27 | expected = "RAISE(ABORT, 'no rap')"; 28 | } 29 | SECTION("fail") { 30 | auto expression = raise_fail("no rap"); 31 | value = serialize(expression, context); 32 | expected = "RAISE(FAIL, 'no rap')"; 33 | } 34 | REQUIRE(value == expected); 35 | } 36 | -------------------------------------------------------------------------------- /dev/functional/mpl/conditional.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace sqlite_orm { 4 | namespace internal { 5 | namespace mpl { 6 | 7 | /* 8 | * Binary quoted metafunction equivalent to `std::conditional`, 9 | * using an improved implementation in respect to memoization. 10 | * 11 | * Because `conditional` is only typed on a single bool non-type template parameter, 12 | * the compiler only ever needs to memoize 2 instances of this class template. 13 | * The type selection is a nested cheap alias template. 14 | */ 15 | template 16 | struct conditional { 17 | template 18 | using fn = A; 19 | }; 20 | 21 | template<> 22 | struct conditional { 23 | template 24 | using fn = B; 25 | }; 26 | 27 | // directly invoke `conditional` 28 | template 29 | using conditional_t = typename conditional::template fn; 30 | } 31 | } 32 | 33 | namespace mpl = internal::mpl; 34 | } 35 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/column_constraints/default.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer default") { 7 | internal::db_objects_tuple<> storage; 8 | internal::serializer_context> context{storage}; 9 | std::string value; 10 | decltype(value) expected; 11 | SECTION("int literal") { 12 | auto def = default_value(1); 13 | value = serialize(def, context); 14 | expected = "DEFAULT (1)"; 15 | } 16 | SECTION("string literal") { 17 | auto def = default_value("hi"); 18 | value = serialize(def, context); 19 | expected = "DEFAULT ('hi')"; 20 | } 21 | SECTION("func") { 22 | auto def = default_value(datetime("now")); 23 | SECTION("use_parentheses") { 24 | context.use_parentheses = true; 25 | expected = "DEFAULT (DATETIME('now'))"; 26 | } 27 | SECTION("!use_parentheses") { 28 | context.use_parentheses = false; 29 | expected = "DEFAULT (DATETIME('now'))"; 30 | } 31 | value = serialize(def, context); 32 | } 33 | REQUIRE(value == expected); 34 | } 35 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/table_constraints/content.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer content") { 7 | internal::db_objects_tuple<> storage; 8 | internal::serializer_context> context{storage}; 9 | std::string value; 10 | std::string expected; 11 | SECTION("empty") { 12 | auto node = content(""); 13 | value = serialize(node, context); 14 | expected = "content=''"; 15 | } 16 | REQUIRE(value == expected); 17 | } 18 | 19 | TEST_CASE("statement_serializer table_content") { 20 | struct User { 21 | int id = 0; 22 | std::string name; 23 | }; 24 | auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)); 25 | using db_objects_t = internal::db_objects_tuple; 26 | auto dbObjects = db_objects_t{table}; 27 | using context_t = internal::serializer_context; 28 | context_t context{dbObjects}; 29 | 30 | auto node = content(); 31 | auto value = internal::serialize(node, context); 32 | REQUIRE(value == R"(content="users")"); 33 | } 34 | -------------------------------------------------------------------------------- /examples/synchronous.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct Query { 6 | std::string src_ip; 7 | std::uint16_t src_port; 8 | std::uint16_t txn_id; 9 | std::uint32_t tv_sec; 10 | std::uint32_t tv_usec; 11 | std::string name; 12 | std::uint16_t type; 13 | }; 14 | 15 | int main(int, char**) { 16 | 17 | using namespace sqlite_orm; 18 | 19 | auto storage = make_storage("synchronous.sqlite", 20 | make_table("queries", 21 | make_column("tv_sec", &Query::tv_sec), 22 | make_column("tv_usec", &Query::tv_usec), 23 | make_column("name", &Query::name), 24 | make_column("type", &Query::type), 25 | make_column("src_ip", &Query::src_ip), 26 | make_column("src_port", &Query::src_port), 27 | make_column("txn_id", &Query::txn_id))); 28 | storage.sync_schema(); 29 | storage.pragma.synchronous(0); 30 | storage.remove_all(); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/operators/cast.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("Cast") { 7 | struct Student { 8 | int id; 9 | float scoreFloat; 10 | std::string scoreString; 11 | }; 12 | 13 | auto storage = make_storage("", 14 | make_table("students", 15 | make_column("id", &Student::id, primary_key()), 16 | make_column("score_float", &Student::scoreFloat), 17 | make_column("score_str", &Student::scoreString))); 18 | storage.sync_schema(); 19 | 20 | storage.replace(Student{1, 10.1f, "14.5"}); 21 | 22 | { 23 | auto rows = storage.select(columns(cast(&Student::scoreFloat), cast(&Student::scoreString))); 24 | REQUIRE(rows.size() == 1); 25 | auto& row = rows.front(); 26 | REQUIRE(std::get<0>(row) == 10); 27 | REQUIRE(std::get<1>(row) == 14); 28 | } 29 | { 30 | auto rows = storage.select(cast(5)); 31 | REQUIRE(rows.size() == 1); 32 | auto& row = rows.front(); 33 | REQUIRE(row == "5"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/ast/excluded.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("excluded") { 7 | using internal::serialize; 8 | struct Vocabulary { 9 | std::string word; 10 | int count = 0; 11 | }; 12 | auto table = make_table("vocabulary", 13 | make_column("word", &Vocabulary::word, primary_key()), 14 | make_column("count", &Vocabulary::count, default_value(1))); 15 | using db_objects_t = internal::db_objects_tuple; 16 | auto dbObjects = db_objects_t{table}; 17 | using context_t = internal::serializer_context; 18 | context_t context{dbObjects}; 19 | 20 | std::string value; 21 | decltype(value) expected; 22 | SECTION("word") { 23 | auto statement = excluded(&Vocabulary::word); 24 | value = serialize(statement, context); 25 | expected = R"(excluded."word")"; 26 | } 27 | SECTION("count") { 28 | auto statement = excluded(&Vocabulary::count); 29 | value = serialize(statement, context); 30 | expected = R"(excluded."count")"; 31 | } 32 | 33 | REQUIRE(value == expected); 34 | } 35 | -------------------------------------------------------------------------------- /dev/column_result_proxy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "type_traits.h" 4 | #include "table_reference.h" 5 | 6 | namespace sqlite_orm { 7 | namespace internal { 8 | 9 | /* 10 | * Holder for the type of an unmapped aggregate/structure/object to be constructed ad-hoc from column results. 11 | * `T` must be constructible using direct-list-initialization. 12 | */ 13 | template 14 | struct structure { 15 | using type = T; 16 | }; 17 | } 18 | } 19 | 20 | namespace sqlite_orm { 21 | namespace internal { 22 | 23 | template 24 | struct column_result_proxy : std::remove_const {}; 25 | 26 | /* 27 | * Unwrap `table_reference` 28 | */ 29 | template 30 | struct column_result_proxy> : decay_table_ref

{}; 31 | 32 | /* 33 | * Pass through `structure` 34 | */ 35 | template 36 | struct column_result_proxy> : P {}; 37 | 38 | template 39 | using column_result_proxy_t = typename column_result_proxy::type; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/static_tests/is_primary_key_insertable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // std::decay 4 | 5 | using namespace sqlite_orm; 6 | 7 | TEST_CASE("is_primary_key_insertable") { 8 | struct User { 9 | int id; 10 | std::string username; 11 | std::string password; 12 | bool isActive; 13 | }; 14 | 15 | auto insertable = std::make_tuple( /// 16 | make_column("", &User::id, primary_key()), 17 | make_column("", &User::username, primary_key(), default_value("Clint Eastwood")), 18 | make_column("", &User::username, primary_key(), default_value(std::vector{})), 19 | make_column("", &User::username, primary_key().autoincrement())); 20 | 21 | auto noninsertable = std::make_tuple( /// 22 | make_column("", &User::username, primary_key()), 23 | make_column("", &User::password, primary_key())); 24 | 25 | iterate_tuple(insertable, [](auto& v) { 26 | STATIC_REQUIRE(internal::is_primary_key_insertable>::value); 27 | }); 28 | 29 | iterate_tuple(noninsertable, [](auto& v) { 30 | STATIC_REQUIRE_FALSE(internal::is_primary_key_insertable>::value); 31 | }); 32 | } -------------------------------------------------------------------------------- /dev/functional/cxx_tuple_polyfill.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::apply; std::tuple_size 4 | #if __cpp_lib_apply < 201603L 5 | #include // std::forward, std::index_sequence, std::make_index_sequence 6 | #endif 7 | 8 | #include "../functional/cxx_functional_polyfill.h" // std::invoke 9 | 10 | namespace sqlite_orm { 11 | namespace internal { 12 | namespace polyfill { 13 | #if __cpp_lib_apply >= 201603L 14 | using std::apply; 15 | #else 16 | template 17 | decltype(auto) apply(Callable&& callable, Tpl&& tpl, std::index_sequence) { 18 | return polyfill::invoke(std::forward(callable), std::get(std::forward(tpl))...); 19 | } 20 | 21 | template 22 | decltype(auto) apply(Callable&& callable, Tpl&& tpl) { 23 | constexpr size_t size = std::tuple_size>::value; 24 | return apply(std::forward(callable), 25 | std::forward(tpl), 26 | std::make_index_sequence{}); 27 | } 28 | #endif 29 | } 30 | } 31 | 32 | namespace polyfill = internal::polyfill; 33 | } 34 | -------------------------------------------------------------------------------- /examples/in_memory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace sqlite_orm; 8 | 9 | using std::cout; 10 | using std::endl; 11 | 12 | struct RapArtist { 13 | int id; 14 | std::string name; 15 | }; 16 | 17 | int main(int, char**) { 18 | 19 | auto storage = make_storage(":memory:", 20 | make_table("rap_artists", 21 | make_column("id", &RapArtist::id, primary_key()), 22 | make_column("name", &RapArtist::name))); 23 | cout << "in memory db opened" << endl; 24 | storage.sync_schema(); 25 | 26 | assert(!storage.count()); 27 | 28 | storage.insert(RapArtist{-1, "The Weeknd"}); 29 | 30 | storage.transaction([&] { 31 | storage.insert(RapArtist{-1, "Drake"}); 32 | return true; 33 | }); 34 | 35 | cout << "rap artists count = " << storage.count() << endl; 36 | 37 | // transaction also work in memory.. 38 | storage.transaction([&] { 39 | storage.insert(RapArtist{-1, "Kanye West"}); 40 | return false; 41 | }); 42 | 43 | cout << "rap artists count = " << storage.count() << " (no Kanye)" << endl; 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /examples/find_package/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace sqlite_orm; 8 | 9 | using std::cout; 10 | using std::endl; 11 | 12 | struct RapArtist { 13 | int id; 14 | std::string name; 15 | }; 16 | 17 | int main(int, char**) { 18 | 19 | auto storage = make_storage(":memory:", 20 | make_table("rap_artists", 21 | make_column("id", &RapArtist::id, primary_key()), 22 | make_column("name", &RapArtist::name))); 23 | cout << "in memory db opened" << endl; 24 | storage.sync_schema(); 25 | 26 | assert(!storage.count()); 27 | 28 | storage.insert(RapArtist{-1, "The Weeknd"}); 29 | 30 | storage.transaction([&] { 31 | storage.insert(RapArtist{-1, "Drake"}); 32 | return true; 33 | }); 34 | 35 | cout << "rap artists count = " << storage.count() << endl; 36 | 37 | // transaction also work in memory.. 38 | storage.transaction([&] { 39 | storage.insert(RapArtist{-1, "Kanye West"}); 40 | return false; 41 | }); 42 | 43 | cout << "rap artists count = " << storage.count() << " (no Kanye)" << endl; 44 | 45 | return 0; 46 | } -------------------------------------------------------------------------------- /examples/fetch_content/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace sqlite_orm; 8 | 9 | using std::cout; 10 | using std::endl; 11 | 12 | struct RapArtist { 13 | int id; 14 | std::string name; 15 | }; 16 | 17 | int main(int, char**) { 18 | 19 | auto storage = make_storage(":memory:", 20 | make_table("rap_artists", 21 | make_column("id", &RapArtist::id, primary_key()), 22 | make_column("name", &RapArtist::name))); 23 | cout << "in memory db opened" << endl; 24 | storage.sync_schema(); 25 | 26 | assert(!storage.count()); 27 | 28 | storage.insert(RapArtist{-1, "The Weeknd"}); 29 | 30 | storage.transaction([&] { 31 | storage.insert(RapArtist{-1, "Drake"}); 32 | return true; 33 | }); 34 | 35 | cout << "rap artists count = " << storage.count() << endl; 36 | 37 | // transaction also work in memory.. 38 | storage.transaction([&] { 39 | storage.insert(RapArtist{-1, "Kanye West"}); 40 | return false; 41 | }); 42 | 43 | cout << "rap artists count = " << storage.count() << " (no Kanye)" << endl; 44 | 45 | return 0; 46 | } -------------------------------------------------------------------------------- /examples/blob.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using std::cout; 9 | using std::endl; 10 | 11 | struct User { 12 | int id; 13 | std::string name; 14 | std::vector hash; // binary format 15 | }; 16 | 17 | int main(int, char**) { 18 | using namespace sqlite_orm; 19 | auto storage = make_storage("blob.sqlite", 20 | make_table("users", 21 | make_column("id", &User::id, primary_key()), 22 | make_column("name", &User::name), 23 | make_column("hash", &User::hash))); 24 | storage.sync_schema(); 25 | storage.remove_all(); 26 | 27 | User alex{ 28 | 0, 29 | "Alex", 30 | {0x10, 0x20, 0x30, 0x40}, 31 | }; 32 | alex.id = storage.insert(alex); 33 | 34 | cout << "users count = " << storage.count() << endl; 35 | 36 | cout << "alex = " << storage.dump(storage.get(alex.id)) << endl; 37 | 38 | auto hash = storage.get(alex.id).hash; 39 | assert(hash.size() == 4); 40 | assert(hash[0] == 0x10); 41 | assert(hash[1] == 0x20); 42 | assert(hash[2] == 0x30); 43 | assert(hash[3] == 0x40); 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /dev/implementations/column_definitions.h: -------------------------------------------------------------------------------- 1 | /** @file Mainly existing to disentangle implementation details from circular and cross dependencies 2 | * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) 3 | * this file is also used to provide definitions of interface methods 'hitting the database'. 4 | */ 5 | #pragma once 6 | 7 | #include // std::make_unique 8 | 9 | #include "../functional/static_magic.h" 10 | #include "../tuple_helper/tuple_traits.h" 11 | #include "../default_value_extractor.h" 12 | #include "../schema/column.h" 13 | 14 | namespace sqlite_orm { 15 | namespace internal { 16 | 17 | template 18 | std::unique_ptr column_constraints::default_value() const { 19 | static constexpr size_t default_op_index = find_tuple_template::value; 20 | 21 | std::unique_ptr value; 22 | call_if_constexpr::value>( 23 | [&value](auto& constraints) { 24 | value = 25 | std::make_unique(serialize_default_value(std::get(constraints))); 26 | }, 27 | this->constraints); 28 | return value; 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packaging/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(GNUInstallDirs) 2 | include(CMakePackageConfigHelpers) 3 | 4 | set(SQLITE_ORM_INSTALL_CMAKEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/SqliteOrm CACHE STRING "Path to SqliteOrm cmake files") 5 | 6 | configure_file(${PROJECT_SOURCE_DIR}/cmake/SqliteOrmConfig.cmake.in SqliteOrmConfig.cmake @ONLY) 7 | 8 | write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/SqliteOrmConfigVersion.cmake 9 | VERSION ${sqlite_orm_VERSION} 10 | COMPATIBILITY SameMajorVersion 11 | ) 12 | 13 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/SqliteOrmConfig.cmake 14 | ${CMAKE_CURRENT_BINARY_DIR}/SqliteOrmConfigVersion.cmake 15 | DESTINATION ${SQLITE_ORM_INSTALL_CMAKEDIR}) 16 | 17 | install(TARGETS sqlite_orm 18 | EXPORT SqliteOrmTargets 19 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 20 | COMPONENT sqlite_orm_runtime 21 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 22 | COMPONENT sqlite_orm_runtime 23 | NAMELINK_COMPONENT sqlite_orm_development 24 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 25 | COMPONENT sqlite_orm_development 26 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 27 | ) 28 | 29 | INSTALL(DIRECTORY ${PROJECT_SOURCE_DIR}/include/sqlite_orm DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 30 | 31 | install(EXPORT SqliteOrmTargets 32 | DESTINATION ${SQLITE_ORM_INSTALL_CMAKEDIR} 33 | NAMESPACE sqlite_orm:: 34 | COMPONENT sqlite_orm_development 35 | ) -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # To do list 2 | 3 | `sqlite_orm` is a wonderful library but there are still features that are not implemented. Here you can find a list of them: 4 | 5 | * `FOREIGN KEY` - sync_schema fk comparison and ability of two tables to have fk to each other (`PRAGMA foreign_key_list(%table_name%);` may be useful) 6 | * rest of core functions(https://sqlite.org/lang_corefunc.html) 7 | * `ATTACH` 8 | * blob incremental I/O https://sqlite.org/c3ref/blob_open.html 9 | * CREATE VIEW and other view operations https://sqlite.org/lang_createview.html 10 | * query static check for correct order (e.g. `GROUP BY` after `WHERE`) 11 | * `WINDOW` 12 | * `SAVEPOINT` https://www.sqlite.org/lang_savepoint.html 13 | * add `static_assert` in crud `get*` functions in case user passes `where_t` instead of id to make compilation error more clear (example https://github.com/fnc12/sqlite_orm/issues/485) 14 | * named constraints: constraint can have name `CREATE TABLE heroes(id INTEGER CONSTRAINT pk PRIMARY KEY)` 15 | * scalar math functions https://sqlite.org/lang_mathfunc.html 16 | * `UPDATE FROM` support https://sqlite.org/lang_update.html#upfrom 17 | * `iif()` function https://sqlite.org/lang_corefunc.html#iif 18 | * strict tables https://sqlite.org/stricttables.html 19 | * static assert when UPDATE is called with no PKs 20 | * `json_each` and `json_tree` functions for JSON1 extension 21 | * update hook 22 | * `RAISE` 23 | 24 | Please feel free to add any feature that isn't listed here and not implemented yet. 25 | -------------------------------------------------------------------------------- /tests/unique_cases/issue525.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("issue525") { 7 | struct User { 8 | int id; 9 | std::string firstName; 10 | std::string lastName; 11 | int birthDate; 12 | std::unique_ptr imageUrl; 13 | int typeId; 14 | }; 15 | 16 | struct UserType { 17 | int id; 18 | std::string name; 19 | }; 20 | 21 | auto filename = "db1.sqlite"; 22 | auto storage = make_storage(filename, 23 | make_table("users", 24 | make_column("id", &User::id, primary_key().autoincrement()), 25 | make_column("first_name", &User::firstName), 26 | make_column("last_name", &User::lastName), 27 | make_column("birth_date", &User::birthDate), 28 | make_column("image_url", &User::imageUrl), 29 | make_column("type_id", &User::typeId)), 30 | make_table("user_types", 31 | make_column("id", &UserType::id, primary_key().autoincrement()), 32 | make_column("name", &UserType::name, default_value("name_placeholder")))); 33 | 34 | storage.sync_schema(); 35 | } 36 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/schema/table.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer table_t") { 7 | struct User { 8 | int id = 0; 9 | std::string name; 10 | }; 11 | std::string value; 12 | std::string expected; 13 | SECTION("simple") { 14 | auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); 15 | using db_objects_t = internal::db_objects_tuple; 16 | auto dbObjects = db_objects_t{table}; 17 | using context_t = internal::serializer_context; 18 | context_t context{dbObjects}; 19 | value = internal::serialize(table, context); 20 | expected = R"(CREATE TABLE "users" ("id" INTEGER NOT NULL, "name" TEXT NOT NULL))"; 21 | } 22 | SECTION("without_rowid") { 23 | auto table = 24 | make_table("users", make_column("id", &User::id), make_column("name", &User::name)).without_rowid(); 25 | using db_objects_t = internal::db_objects_tuple; 26 | auto dbObjects = db_objects_t{table}; 27 | using context_t = internal::serializer_context; 28 | context_t context{dbObjects}; 29 | value = internal::serialize(table, context); 30 | expected = R"(CREATE TABLE "users" ("id" INTEGER NOT NULL, "name" TEXT NOT NULL) WITHOUT ROWID)"; 31 | } 32 | REQUIRE(value == expected); 33 | } 34 | -------------------------------------------------------------------------------- /dev/tuple_helper/same_or_void.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::common_type 4 | 5 | namespace sqlite_orm { 6 | namespace internal { 7 | 8 | /** 9 | * Accepts any number of arguments and evaluates a nested `type` typename as `T` if all arguments are the same, otherwise `void`. 10 | */ 11 | template 12 | struct same_or_void { 13 | using type = void; 14 | }; 15 | 16 | template 17 | struct same_or_void { 18 | using type = A; 19 | }; 20 | 21 | template 22 | struct same_or_void { 23 | using type = A; 24 | }; 25 | 26 | template 27 | using same_or_void_t = typename same_or_void::type; 28 | 29 | template 30 | struct same_or_void : same_or_void {}; 31 | 32 | template 33 | struct common_type_of; 34 | 35 | template class Pack, class... Types> 36 | struct common_type_of> : std::common_type {}; 37 | 38 | /** 39 | * Accepts a pack of types and defines a nested `type` typename to a common type if possible, otherwise nonexistent. 40 | * 41 | * @note: SFINAE friendly like `std::common_type`. 42 | */ 43 | template 44 | using common_type_of_t = typename common_type_of::type; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/unique.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using std::cerr; 8 | using std::cout; 9 | using std::endl; 10 | 11 | struct Entry { 12 | int id; 13 | std::string uniqueColumn; 14 | std::unique_ptr nullableColumn; 15 | }; 16 | 17 | int main(int, char**) { 18 | using namespace sqlite_orm; 19 | auto storage = make_storage("unique.sqlite", 20 | make_table("unique_test", 21 | make_column("id", &Entry::id, primary_key().autoincrement()), 22 | make_column("unique_text", &Entry::uniqueColumn, unique()), 23 | make_column("nullable_text", &Entry::nullableColumn))); 24 | storage.sync_schema(); 25 | storage.remove_all(); 26 | 27 | try { 28 | auto sameString = "Bebe Rexha"; 29 | 30 | auto id1 = storage.insert(Entry{0, sameString, std::make_unique("The way I are")}); 31 | cout << "inserted " << storage.dump(storage.get(id1)) << endl; 32 | 33 | // it's ok but the next line will throw std::system_error 34 | 35 | auto id2 = storage.insert(Entry{0, sameString, std::make_unique("I got you")}); 36 | cout << "inserted " << storage.dump(storage.get(id2)) << endl; 37 | } catch (const std::system_error& e) { 38 | cerr << e.what() << endl; 39 | } 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/conditions.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer conditions") { 7 | std::string value, expected; 8 | 9 | SECTION("using") { 10 | struct User { 11 | int64 id; 12 | }; 13 | 14 | auto t1 = make_table("user", make_column("id", &User::id)); 15 | auto storage = internal::db_objects_tuple{t1}; 16 | using db_objects_tuple = decltype(storage); 17 | 18 | internal::serializer_context ctx{storage}; 19 | 20 | SECTION("using column") { 21 | auto expression = using_(&User::id); 22 | value = serialize(expression, ctx); 23 | expected = R"(USING ("id"))"; 24 | } 25 | SECTION("using explicit column") { 26 | auto expression = using_(column(&User::id)); 27 | value = serialize(expression, ctx); 28 | expected = R"(USING ("id"))"; 29 | } 30 | } 31 | SECTION("order by") { 32 | auto storage = internal::db_objects_tuple<>{}; 33 | using db_objects_tuple = decltype(storage); 34 | 35 | internal::serializer_context ctx{storage}; 36 | 37 | SECTION("positional ordinal") { 38 | auto expression = order_by(1); 39 | value = serialize(expression, ctx); 40 | expected = "ORDER BY 1"; 41 | } 42 | } 43 | 44 | REQUIRE(value == expected); 45 | } 46 | -------------------------------------------------------------------------------- /tests/static_tests/statements.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | using internal::expression_object_type_t; 6 | using internal::statement_object_type_t; 7 | 8 | template 9 | static void runExpressionTest(Expression /*expression*/) { 10 | using Statement = internal::prepared_statement_t; 11 | STATIC_REQUIRE(std::is_same, Expected>::value); 12 | STATIC_REQUIRE(std::is_same, Expected>::value); 13 | } 14 | 15 | TEST_CASE("statements") { 16 | struct Object { 17 | int64 id; 18 | }; 19 | constexpr Object obj{}; 20 | constexpr std::array objs{}; 21 | 22 | SECTION("expression object") { 23 | runExpressionTest(insert(obj)); 24 | runExpressionTest(insert(std::cref(obj))); 25 | runExpressionTest(insert(obj, columns(&Object::id))); 26 | runExpressionTest(insert(std::cref(obj), columns(&Object::id))); 27 | runExpressionTest(insert_range(objs.cbegin(), objs.cend())); 28 | runExpressionTest(replace(obj)); 29 | runExpressionTest(replace(std::cref(obj))); 30 | runExpressionTest(replace_range(objs.cbegin(), objs.cend())); 31 | runExpressionTest(update(obj)); 32 | runExpressionTest(update(std::cref(obj))); 33 | runExpressionTest(remove(0)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /dev/is_base_of_template.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::true_type, std::false_type, std::declval 4 | 5 | namespace sqlite_orm { 6 | 7 | namespace internal { 8 | 9 | /* 10 | * This is because of bug in MSVC, for more information, please visit 11 | * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 12 | */ 13 | #ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION 14 | template class Base> 15 | struct is_base_of_template_impl { 16 | template 17 | static constexpr std::true_type test(const Base&); 18 | 19 | static constexpr std::false_type test(...); 20 | }; 21 | 22 | template class C> 23 | using is_base_of_template = decltype(is_base_of_template_impl::test(std::declval())); 24 | #else 25 | template class C, typename... Ts> 26 | std::true_type is_base_of_template_impl(const C&); 27 | 28 | template class C> 29 | std::false_type is_base_of_template_impl(...); 30 | 31 | template class C> 32 | using is_base_of_template = decltype(is_base_of_template_impl(std::declval())); 33 | #endif 34 | 35 | template class C> 36 | SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template::value; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /dev/rowid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::string 4 | 5 | namespace sqlite_orm { 6 | 7 | namespace internal { 8 | 9 | struct rowid_t { 10 | operator std::string() const { 11 | return "rowid"; 12 | } 13 | }; 14 | 15 | struct oid_t { 16 | operator std::string() const { 17 | return "oid"; 18 | } 19 | }; 20 | 21 | struct _rowid_t { 22 | operator std::string() const { 23 | return "_rowid_"; 24 | } 25 | }; 26 | 27 | template 28 | struct table_rowid_t : public rowid_t { 29 | using type = T; 30 | }; 31 | 32 | template 33 | struct table_oid_t : public oid_t { 34 | using type = T; 35 | }; 36 | template 37 | struct table__rowid_t : public _rowid_t { 38 | using type = T; 39 | }; 40 | 41 | } 42 | 43 | inline internal::rowid_t rowid() { 44 | return {}; 45 | } 46 | 47 | inline internal::oid_t oid() { 48 | return {}; 49 | } 50 | 51 | inline internal::_rowid_t _rowid_() { 52 | return {}; 53 | } 54 | 55 | template 56 | internal::table_rowid_t rowid() { 57 | return {}; 58 | } 59 | 60 | template 61 | internal::table_oid_t oid() { 62 | return {}; 63 | } 64 | 65 | template 66 | internal::table__rowid_t _rowid_() { 67 | return {}; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /dev/ast/group_by.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::tuple, std::make_tuple 4 | #include // std::true_type, std::false_type 5 | #include // std::forward, std::move 6 | 7 | #include "../functional/cxx_type_traits_polyfill.h" 8 | 9 | namespace sqlite_orm { 10 | namespace internal { 11 | 12 | template 13 | struct group_by_with_having { 14 | using args_type = std::tuple; 15 | using expression_type = T; 16 | 17 | args_type args; 18 | expression_type expression; 19 | }; 20 | 21 | /** 22 | * GROUP BY pack holder. 23 | */ 24 | template 25 | struct group_by_t { 26 | using args_type = std::tuple; 27 | 28 | args_type args; 29 | 30 | template 31 | group_by_with_having having(T expression) { 32 | return {std::move(this->args), std::move(expression)}; 33 | } 34 | }; 35 | 36 | template 37 | using is_group_by = polyfill::disjunction, 38 | polyfill::is_specialization_of>; 39 | } 40 | 41 | /** 42 | * GROUP BY column. 43 | * Example: storage.get_all(group_by(&Employee::name)) 44 | */ 45 | template 46 | internal::group_by_t group_by(Args... args) { 47 | return {{std::forward(args)...}}; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /dev/eponymous_vtabs/dbstat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef SQLITE_ENABLE_DBSTAT_VTAB 4 | #include // std::string 5 | #endif 6 | 7 | #include "../schema/column.h" 8 | #include "../schema/table.h" 9 | #include "../column_pointer.h" 10 | 11 | namespace sqlite_orm { 12 | #ifdef SQLITE_ENABLE_DBSTAT_VTAB 13 | struct dbstat { 14 | std::string name; 15 | std::string path; 16 | int pageno = 0; 17 | std::string pagetype; 18 | int ncell = 0; 19 | int payload = 0; 20 | int unused = 0; 21 | int mx_payload = 0; 22 | int pgoffset = 0; 23 | int pgsize = 0; 24 | }; 25 | 26 | inline auto make_dbstat_table() { 27 | return make_table("dbstat", 28 | make_column("name", &dbstat::name), 29 | make_column("path", &dbstat::path), 30 | make_column("pageno", &dbstat::pageno), 31 | make_column("pagetype", &dbstat::pagetype), 32 | make_column("ncell", &dbstat::ncell), 33 | make_column("payload", &dbstat::payload), 34 | make_column("unused", &dbstat::unused), 35 | make_column("mx_payload", &dbstat::mx_payload), 36 | make_column("pgoffset", &dbstat::pgoffset), 37 | make_column("pgsize", &dbstat::pgsize)); 38 | } 39 | 40 | #ifdef SQLITE_ORM_WITH_CPP20_ALIASES 41 | inline constexpr orm_table_reference auto dbstat_table = c(); 42 | #endif 43 | #endif // SQLITE_ENABLE_DBSTAT_VTAB 44 | } 45 | -------------------------------------------------------------------------------- /dev/values_to_tuple.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include // std::enable_if, std::is_same, std::index_sequence, std::make_index_sequence 5 | #include // std::tuple, std::tuple_size, std::tuple_element 6 | 7 | #include "functional/cxx_functional_polyfill.h" 8 | #include "type_traits.h" 9 | #include "row_extractor.h" 10 | #include "arg_values.h" 11 | 12 | namespace sqlite_orm { 13 | 14 | namespace internal { 15 | 16 | template 17 | struct tuple_from_values { 18 | template> = true> 19 | R operator()(sqlite3_value** values, int /*argsCount*/) const { 20 | return this->create_from(values, std::make_index_sequence::value>{}); 21 | } 22 | 23 | template> = true> 24 | R operator()(sqlite3_value** values, int argsCount) const { 25 | return {arg_values(argsCount, values)}; 26 | } 27 | 28 | private: 29 | template 30 | Tpl create_from(sqlite3_value** values, std::index_sequence) const { 31 | return {this->extract>(values[Idx])...}; 32 | } 33 | 34 | template 35 | T extract(sqlite3_value* value) const { 36 | const auto rowExtractor = boxed_value_extractor(); 37 | return rowExtractor.extract(value); 38 | } 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /third_party/amalgamate/LICENSE.md: -------------------------------------------------------------------------------- 1 | amalgamate.py - Amalgamate C source and header files 2 | Copyright (c) 2012, Erik Edlund 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Erik Edlund, nor the names of its contributors may 15 | be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /tests/unique_cases/join_iterator_ctor_compilation_error.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("Join iterator ctor compilation error") { 7 | struct Tag { 8 | int objectId; 9 | std::string text; 10 | }; 11 | 12 | auto storage = 13 | make_storage("join_error.sqlite", 14 | make_table("tags", make_column("object_id", &Tag::objectId), make_column("text", &Tag::text))); 15 | storage.sync_schema(); 16 | 17 | auto offs = 0; 18 | auto lim = 5; 19 | storage.select(columns(&Tag::text, count(&Tag::text)), 20 | group_by(&Tag::text), 21 | order_by(count(&Tag::text)).desc(), 22 | limit(offs, lim)); 23 | { 24 | auto statement = storage.prepare(select(columns(&Tag::text, count(&Tag::text)), 25 | group_by(&Tag::text), 26 | order_by(count(&Tag::text)).desc(), 27 | limit(offs, lim))); 28 | REQUIRE(get<0>(statement) == offs); 29 | REQUIRE(get<1>(statement) == lim); 30 | } 31 | { 32 | auto statement = storage.prepare(select(columns(&Tag::text, count(&Tag::text)), 33 | group_by(&Tag::text), 34 | order_by(count(&Tag::text)).desc(), 35 | limit(lim, offset(offs)))); 36 | REQUIRE(get<0>(statement) == lim); 37 | REQUIRE(get<1>(statement) == offs); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/static_tests/functional/tuple_traits.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | using internal::asterisk_t; 6 | using internal::count_tuple; 7 | using internal::default_t; 8 | using internal::find_tuple_type; 9 | using internal::is_primary_key; 10 | using internal::primary_key_t; 11 | using internal::tuple_has; 12 | using internal::tuple_has_template; 13 | using internal::tuple_has_type; 14 | 15 | TEST_CASE("tuple traits") { 16 | using empty_tuple_type = std::tuple<>; 17 | using tuple_type = std::tuple, primary_key_t<>, std::string>; 18 | 19 | STATIC_REQUIRE(internal::tuple_has::value); 20 | STATIC_REQUIRE_FALSE(internal::tuple_has::value); 21 | STATIC_REQUIRE(internal::tuple_has_type::value); 22 | STATIC_REQUIRE(internal::tuple_has_type::value); 23 | STATIC_REQUIRE_FALSE(internal::tuple_has_type::value); 24 | STATIC_REQUIRE(internal::tuple_has_template::value); 25 | STATIC_REQUIRE_FALSE(internal::tuple_has_template::value); 26 | STATIC_REQUIRE(internal::find_tuple_type::value == 1); 27 | STATIC_REQUIRE(internal::find_tuple_type::value == 1); 28 | STATIC_REQUIRE(internal::find_tuple_type::value == std::tuple_size::value); 29 | STATIC_REQUIRE(internal::find_tuple_template::value == 2); 30 | STATIC_REQUIRE(internal::count_tuple::value == 1); 31 | } 32 | -------------------------------------------------------------------------------- /tests/simple_query.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("Simple query") { 7 | auto storage = make_storage(""); 8 | { 9 | // SELECT 1 10 | auto one = storage.select(1); 11 | REQUIRE(one.size() == 1); 12 | REQUIRE(one.front() == 1); 13 | } 14 | { 15 | // SELECT 'ototo' 16 | auto ototo = storage.select("ototo"); 17 | REQUIRE(ototo.size() == 1); 18 | REQUIRE(ototo.front() == "ototo"); 19 | } 20 | { 21 | // SELECT 1 + 1 22 | auto two = storage.select(c(1) + 1); 23 | REQUIRE(two.size() == 1); 24 | REQUIRE(two.front() == 2); 25 | 26 | auto twoAgain = storage.select(add(1, 1)); 27 | REQUIRE(two == twoAgain); 28 | } 29 | { 30 | // SELECT 10 / 5, 2 * 4 31 | auto math = storage.select(columns(sqlite_orm::div(10, 5), mul(2, 4))); 32 | REQUIRE(math.size() == 1); 33 | REQUIRE(math.front() == std::make_tuple(2, 8)); 34 | } 35 | { 36 | // SELECT 1, 2 37 | auto twoRows = storage.select(columns(1, 2)); 38 | REQUIRE(twoRows.size() == 1); 39 | REQUIRE(std::get<0>(twoRows.front()) == 1); 40 | REQUIRE(std::get<1>(twoRows.front()) == 2); 41 | } 42 | { 43 | // SELECT 1, 2 44 | // UNION ALL 45 | // SELECT 3, 4; 46 | auto twoRowsUnion = storage.select(union_all(select(columns(1, 2)), select(columns(3, 4)))); 47 | REQUIRE(twoRowsUnion.size() == 2); 48 | REQUIRE(twoRowsUnion[0] == std::make_tuple(1, 2)); 49 | REQUIRE(twoRowsUnion[1] == std::make_tuple(3, 4)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /dev/type_is_nullable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::false_type, std::true_type, std::enable_if 4 | #include // std::shared_ptr, std::unique_ptr 5 | #include "functional/cxx_optional.h" 6 | 7 | #include "functional/cxx_type_traits_polyfill.h" 8 | 9 | namespace sqlite_orm { 10 | 11 | /** 12 | * This is class that tells `sqlite_orm` that type is nullable. Nullable types 13 | * are mapped to sqlite database as `NULL` and not-nullable are mapped as `NOT NULL`. 14 | * Default nullability status for all types is `NOT NULL`. So if you want to map 15 | * custom type as `NULL` (for example: boost::optional) you have to create a specialization 16 | * of `type_is_nullable` for your type and derive from `std::true_type`. 17 | */ 18 | template 19 | struct type_is_nullable : std::false_type { 20 | bool operator()(const T&) const { 21 | return true; 22 | } 23 | }; 24 | 25 | /** 26 | * This is a specialization for std::shared_ptr, std::unique_ptr, std::optional, which are nullable in sqlite_orm. 27 | */ 28 | template 29 | struct type_is_nullable, 33 | #endif 34 | polyfill::is_specialization_of, 35 | polyfill::is_specialization_of>::value>> : std::true_type { 36 | bool operator()(const T& t) const { 37 | return static_cast(t); 38 | } 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /dev/ast/where.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::false_type, std::true_type 4 | #include // std::move 5 | 6 | #include "../functional/cxx_type_traits_polyfill.h" 7 | #include "../serialize_result_type.h" 8 | 9 | namespace sqlite_orm { 10 | namespace internal { 11 | 12 | struct where_string { 13 | serialize_result_type serialize() const { 14 | return "WHERE"; 15 | } 16 | }; 17 | 18 | /** 19 | * WHERE argument holder. 20 | * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc 21 | * Don't construct it manually. Call `where(...)` function instead. 22 | */ 23 | template 24 | struct where_t : where_string { 25 | using expression_type = C; 26 | 27 | expression_type expression; 28 | 29 | constexpr where_t(expression_type expression_) : expression(std::move(expression_)) {} 30 | }; 31 | 32 | template 33 | SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of::value; 34 | 35 | template 36 | struct is_where : polyfill::bool_constant> {}; 37 | } 38 | 39 | /** 40 | * WHERE clause. Use it to add WHERE conditions wherever you like. 41 | * C is expression type. Can be any expression like: is_equal_t, is_null_t, exists_t etc 42 | * @example 43 | * // SELECT name 44 | * // FROM letters 45 | * // WHERE id > 3 46 | * auto rows = storage.select(&Letter::name, where(greater_than(&Letter::id, 3))); 47 | */ 48 | template 49 | constexpr internal::where_t where(C expression) { 50 | return {std::move(expression)}; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /dev/table_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::string 4 | 5 | namespace sqlite_orm { 6 | 7 | struct table_info { 8 | int cid = 0; 9 | std::string name; 10 | std::string type; 11 | bool notnull = false; 12 | std::string dflt_value; 13 | int pk = 0; 14 | 15 | #if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) 16 | table_info(decltype(cid) cid_, 17 | decltype(name) name_, 18 | decltype(type) type_, 19 | decltype(notnull) notnull_, 20 | decltype(dflt_value) dflt_value_, 21 | decltype(pk) pk_) : 22 | cid(cid_), name(std::move(name_)), type(std::move(type_)), notnull(notnull_), 23 | dflt_value(std::move(dflt_value_)), pk(pk_) {} 24 | #endif 25 | }; 26 | 27 | struct table_xinfo { 28 | int cid = 0; 29 | std::string name; 30 | std::string type; 31 | bool notnull = false; 32 | std::string dflt_value; 33 | int pk = 0; 34 | int hidden = 0; // different than 0 => generated_always_as() - TODO verify 35 | 36 | #if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) 37 | table_xinfo(decltype(cid) cid_, 38 | decltype(name) name_, 39 | decltype(type) type_, 40 | decltype(notnull) notnull_, 41 | decltype(dflt_value) dflt_value_, 42 | decltype(pk) pk_, 43 | decltype(hidden) hidden_) : 44 | cid(cid_), name(std::move(name_)), type(std::move(type_)), notnull(notnull_), 45 | dflt_value(std::move(dflt_value_)), pk(pk_), hidden{hidden_} {} 46 | #endif 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/indexed_column.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer indexed_column") { 7 | struct User { 8 | int id = 0; 9 | std::string name; 10 | }; 11 | auto table = make_table("users", make_column("id", &User::id), make_column("name", &User::name)); 12 | using db_objects_t = internal::db_objects_tuple; 13 | auto dbObjects = db_objects_t{table}; 14 | using context_t = internal::serializer_context; 15 | context_t context{dbObjects}; 16 | { 17 | auto column = indexed_column(&User::id); 18 | auto value = internal::serialize(column, context); 19 | REQUIRE(value == R"("id")"); 20 | } 21 | { 22 | auto column = indexed_column(&User::id).asc(); 23 | auto value = internal::serialize(column, context); 24 | REQUIRE(value == R"("id" ASC)"); 25 | } 26 | { 27 | auto column = indexed_column(&User::id).desc(); 28 | auto value = internal::serialize(column, context); 29 | REQUIRE(value == R"("id" DESC)"); 30 | } 31 | { 32 | auto column = indexed_column(&User::id).collate("BINARY"); 33 | auto value = internal::serialize(column, context); 34 | REQUIRE(value == R"("id" COLLATE BINARY)"); 35 | } 36 | { 37 | auto column = indexed_column(&User::name).collate("BINARY").asc(); 38 | auto value = internal::serialize(column, context); 39 | REQUIRE(value == R"("name" COLLATE BINARY ASC)"); 40 | } 41 | { 42 | auto column = indexed_column(&User::name).collate("OTHER").desc(); 43 | auto value = internal::serialize(column, context); 44 | REQUIRE(value == R"("name" COLLATE OTHER DESC)"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/unique_cases/issue1354.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("issue1354") { 7 | struct User { 8 | uint32_t id; 9 | std::string name; 10 | std::string lastName; 11 | }; 12 | 13 | struct OtherUser { 14 | uint32_t id; 15 | std::string name; 16 | std::string lastName; 17 | }; 18 | 19 | struct UserStruct { 20 | uint32_t Id; 21 | std::string Name; 22 | std::string LastName; 23 | }; 24 | 25 | constexpr auto userStruct = struct_(&User::id, &User::name, &OtherUser::lastName); 26 | 27 | auto storage = make_storage("", 28 | make_table("users", 29 | make_column("id", &User::id), 30 | make_column("name", &User::name), 31 | make_column("last_name", &User::lastName)), 32 | make_table("other_users", 33 | make_column("id", &OtherUser::id), 34 | make_column("name", &OtherUser::name), 35 | make_column("last_name", &OtherUser::lastName))); 36 | storage.sync_schema(); 37 | auto orderBySql = dynamic_order_by(storage); 38 | 39 | SECTION("name") { 40 | orderBySql.push_back(order_by(&User::name).asc()); 41 | } 42 | SECTION("lastName") { 43 | orderBySql.push_back(order_by(&User::lastName).asc()); 44 | } 45 | SECTION("both") { 46 | orderBySql.push_back(order_by(&User::name).asc()); 47 | orderBySql.push_back(order_by(&User::lastName).asc()); 48 | } 49 | 50 | auto users = 51 | storage.select(userStruct, left_join(on(c(&User::id) == &OtherUser::id)), orderBySql, limit(1)); 52 | } 53 | -------------------------------------------------------------------------------- /dev/table_reference.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::remove_const, std::type_identity 4 | 5 | #include "functional/cxx_type_traits_polyfill.h" 6 | 7 | namespace sqlite_orm { 8 | namespace internal { 9 | /* 10 | * Identity wrapper around a mapped object, facilitating uniform column pointer expressions. 11 | */ 12 | template 13 | struct table_reference : polyfill::type_identity {}; 14 | 15 | template 16 | struct decay_table_ref : std::remove_const {}; 17 | template 18 | struct decay_table_ref> : polyfill::type_identity {}; 19 | template 20 | struct decay_table_ref> : polyfill::type_identity {}; 21 | 22 | template 23 | using decay_table_ref_t = typename decay_table_ref::type; 24 | #ifdef SQLITE_ORM_WITH_CPP20_ALIASES 25 | template 26 | using auto_decay_table_ref_t = typename decay_table_ref::type; 27 | #endif 28 | 29 | template 30 | SQLITE_ORM_INLINE_VAR constexpr bool is_table_reference_v = 31 | polyfill::is_specialization_of_v, table_reference>; 32 | 33 | template 34 | struct is_table_reference : polyfill::bool_constant> {}; 35 | } 36 | 37 | #ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED 38 | /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. 39 | * 40 | * A concrete table reference has the following traits: 41 | * - specialization of `table_reference`, whose `type` typename references a mapped object. 42 | */ 43 | template 44 | concept orm_table_reference = polyfill::is_specialization_of_v, internal::table_reference>; 45 | #endif 46 | } 47 | -------------------------------------------------------------------------------- /dev/sqlite_schema_table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::string 4 | 5 | #include "schema/column.h" 6 | #include "schema/table.h" 7 | #include "column_pointer.h" 8 | #include "alias.h" 9 | 10 | namespace sqlite_orm { 11 | /** 12 | * SQLite's "schema table" that stores the schema for a database. 13 | * 14 | * @note Despite the fact that the schema table was renamed from "sqlite_master" to "sqlite_schema" in SQLite 3.33.0 15 | * the renaming process was more like keeping the previous name "sqlite_master" and attaching an internal alias "sqlite_schema". 16 | * One can infer this fact from the following SQL statement: 17 | * It qualifies the set of columns, but bails out with error "no such table: sqlite_schema": `SELECT sqlite_schema.* from sqlite_schema`. 18 | * Hence we keep its previous table name `sqlite_master`, and provide `sqlite_schema` as a table alias in sqlite_orm. 19 | */ 20 | struct sqlite_master { 21 | std::string type; 22 | std::string name; 23 | std::string tbl_name; 24 | int rootpage = 0; 25 | std::string sql; 26 | 27 | #ifdef SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED 28 | friend bool operator==(const sqlite_master&, const sqlite_master&) = default; 29 | #endif 30 | }; 31 | 32 | inline auto make_sqlite_schema_table() { 33 | return make_table("sqlite_master", 34 | make_column("type", &sqlite_master::type), 35 | make_column("name", &sqlite_master::name), 36 | make_column("tbl_name", &sqlite_master::tbl_name), 37 | make_column("rootpage", &sqlite_master::rootpage), 38 | make_column("sql", &sqlite_master::sql)); 39 | } 40 | 41 | #ifdef SQLITE_ORM_WITH_CPP20_ALIASES 42 | inline constexpr orm_table_reference auto sqlite_master_table = c(); 43 | inline constexpr orm_table_alias auto sqlite_schema = "sqlite_schema"_alias.for_(); 44 | #endif 45 | } 46 | -------------------------------------------------------------------------------- /tests/unique_cases/prepare_get_all_with_case.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("Prepare with case") { 7 | struct UserProfile { 8 | int id = 0; 9 | std::string firstName; 10 | 11 | #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED 12 | UserProfile() = default; 13 | UserProfile(int id, std::string firstName) : id{id}, firstName{std::move(firstName)} {} 14 | #endif 15 | }; 16 | 17 | auto storage = make_storage({}, 18 | make_table("user_profiles", 19 | make_column("id", &UserProfile::id, primary_key()), 20 | make_column("first_name", &UserProfile::firstName))); 21 | storage.sync_schema(); 22 | 23 | const std::string name = "Iggy"; 24 | 25 | SECTION("case string name") { 26 | auto statement = storage.prepare(get_all(where(is_equal( 27 | &UserProfile::firstName, 28 | case_(name).when((length(name) > 0), then(name)).else_(&UserProfile::firstName).end())))); 29 | std::ignore = statement; 30 | } 31 | SECTION("case string") { 32 | auto statement = storage.prepare(get_all(where( 33 | case_().when(length(name) > 0, then(like(&UserProfile::firstName, name))).else_(true).end()))); 34 | std::ignore = statement; 35 | } 36 | SECTION("case int") { 37 | storage.insert(UserProfile{1, "Iggy"}); 38 | storage.insert(UserProfile{2, "I%"}); 39 | auto rows = storage.get_all(); 40 | auto statement3 = storage.prepare(get_all( 41 | where(is_equal(&UserProfile::id, 42 | case_(c(&UserProfile::id) * 2).when(2, then(1)).when(4, then(2)).else_(3).end())))); 43 | auto r2 = storage.execute(statement3); 44 | REQUIRE(r2.size() == rows.size()); // does not filter at all 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /dev/storage_traits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::tuple 4 | 5 | #include "functional/cxx_type_traits_polyfill.h" 6 | #include "tuple_helper/tuple_filter.h" 7 | #include "tuple_helper/tuple_transformer.h" 8 | #include "type_traits.h" 9 | #include "storage_lookup.h" 10 | #include "schema/column.h" 11 | 12 | namespace sqlite_orm { 13 | namespace internal { 14 | 15 | namespace storage_traits { 16 | 17 | /** 18 | * DBO - db object (table) 19 | */ 20 | template 21 | struct storage_mapped_columns_impl 22 | : tuple_transformer, is_column>, field_type_t> {}; 23 | 24 | template<> 25 | struct storage_mapped_columns_impl { 26 | using type = std::tuple<>; 27 | }; 28 | 29 | /** 30 | * DBOs - db_objects_tuple type 31 | * Lookup - mapped or unmapped data type 32 | */ 33 | template 34 | struct storage_mapped_columns : storage_mapped_columns_impl> {}; 35 | 36 | /** 37 | * DBO - db object (table) 38 | */ 39 | template 40 | struct storage_mapped_column_expressions_impl 41 | : tuple_transformer, is_column>, column_field_expression_t> {}; 42 | 43 | template<> 44 | struct storage_mapped_column_expressions_impl { 45 | using type = std::tuple<>; 46 | }; 47 | 48 | /** 49 | * DBOs - db_objects_tuple type 50 | * Lookup - mapped or unmapped data type 51 | */ 52 | template 53 | struct storage_mapped_column_expressions 54 | : storage_mapped_column_expressions_impl> {}; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/base_types.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer base types") { 7 | internal::db_objects_tuple<> storage; 8 | internal::serializer_context> context{storage}; 9 | std::string stringValue; 10 | decltype(stringValue) expected; 11 | SECTION("std::string") { 12 | std::string str("calma"); 13 | SECTION("no question") { 14 | stringValue = serialize(str, context); 15 | expected = "'calma'"; 16 | } 17 | SECTION("question") { 18 | context.replace_bindable_with_question = true; 19 | stringValue = serialize(str, context); 20 | expected = "?"; 21 | } 22 | } 23 | SECTION("const char *") { 24 | const char* str = "baby"; 25 | SECTION("no question") { 26 | stringValue = serialize(str, context); 27 | expected = "'baby'"; 28 | } 29 | SECTION("question") { 30 | context.replace_bindable_with_question = true; 31 | stringValue = serialize(str, context); 32 | expected = "?"; 33 | } 34 | } 35 | #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED 36 | SECTION("std::string_view") { 37 | std::string_view str = "agora"; 38 | SECTION("no question") { 39 | stringValue = serialize(str, context); 40 | expected = "'agora'"; 41 | } 42 | SECTION("question") { 43 | context.replace_bindable_with_question = true; 44 | stringValue = serialize(str, context); 45 | expected = "?"; 46 | } 47 | } 48 | #endif 49 | SECTION("blob") { 50 | std::vector blob{}; 51 | stringValue = serialize(blob, context); 52 | expected = "x''"; 53 | } 54 | SECTION("escaped string") { 55 | std::string str{"'"}; 56 | stringValue = serialize(str, context); 57 | expected = "''''"; 58 | } 59 | REQUIRE(stringValue == expected); 60 | } 61 | -------------------------------------------------------------------------------- /examples/index.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | using std::cout; 7 | using std::endl; 8 | 9 | struct Contract { 10 | std::string firstName; 11 | std::string lastName; 12 | std::string email; 13 | }; 14 | 15 | using namespace sqlite_orm; 16 | 17 | // beware - put `make_index` before `make_table` cause `sync_schema` is called in reverse order 18 | // otherwise you'll receive an exception 19 | auto storage = make_storage( 20 | "index.sqlite", 21 | make_index("idx_contacts_name", &Contract::firstName, &Contract::lastName, where(length(&Contract::firstName) > 2)), 22 | make_unique_index("idx_contacts_email", indexed_column(&Contract::email).collate("BINARY").desc()), 23 | make_table("contacts", 24 | make_column("first_name", &Contract::firstName), 25 | make_column("last_name", &Contract::lastName), 26 | make_column("email", &Contract::email))); 27 | 28 | int main(int, char**) { 29 | 30 | storage.sync_schema(); 31 | storage.remove_all(); 32 | 33 | storage.insert(Contract{ 34 | "John", 35 | "Doe", 36 | "john.doe@sqlitetutorial.net", 37 | }); 38 | try { 39 | storage.insert(Contract{ 40 | "Johny", 41 | "Doe", 42 | "john.doe@sqlitetutorial.net", 43 | }); 44 | } catch (const std::system_error& e) { 45 | cout << e.what() << endl; 46 | } 47 | std::vector moreContracts = { 48 | Contract{ 49 | "David", 50 | "Brown", 51 | "david.brown@sqlitetutorial.net", 52 | }, 53 | Contract{ 54 | "Lisa", 55 | "Smith", 56 | "lisa.smith@sqlitetutorial.net", 57 | }, 58 | }; 59 | storage.insert_range(moreContracts.begin(), moreContracts.end()); 60 | 61 | auto lisas = storage.get_all(where(c(&Contract::email) == "lisa.smith@sqlitetutorial.net")); 62 | 63 | storage.drop_index("idx_contacts_name"); 64 | storage.drop_index("idx_contacts_email"); 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /dev/locking_mode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::array 4 | #include // std::string 5 | #include // std::pair 6 | #include // std::ranges::transform 7 | #include // std::toupper 8 | 9 | #include "serialize_result_type.h" 10 | 11 | namespace sqlite_orm { 12 | enum class locking_mode : signed char { 13 | NORMAL = 0, 14 | EXCLUSIVE = 1, 15 | }; 16 | 17 | namespace internal { 18 | inline const serialize_result_type& locking_mode_to_string(locking_mode value) { 19 | #ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED 20 | static constexpr std::array idx2str = { 21 | #else 22 | static const std::array idx2str = { 23 | #endif 24 | "NORMAL", 25 | "EXCLUSIVE", 26 | }; 27 | return idx2str.at(static_cast(value)); 28 | } 29 | 30 | inline std::pair locking_mode_from_string(std::string string) { 31 | static constexpr std::array lockingModes = {{ 32 | locking_mode::NORMAL, 33 | locking_mode::EXCLUSIVE, 34 | }}; 35 | 36 | #if __cpp_lib_ranges >= 201911L 37 | std::ranges::transform(string, string.begin(), [](unsigned char c) noexcept { 38 | return std::toupper(c); 39 | }); 40 | if (auto found = std::ranges::find(lockingModes, string, locking_mode_to_string); 41 | found != lockingModes.end()) SQLITE_ORM_CPP_LIKELY { 42 | return {true, *found}; 43 | } 44 | #else 45 | std::transform(string.begin(), string.end(), string.begin(), [](unsigned char c) noexcept { 46 | return std::toupper(c); 47 | }); 48 | for (auto lockingMode: lockingModes) { 49 | if (locking_mode_to_string(lockingMode) == string) { 50 | return {true, lockingMode}; 51 | } 52 | } 53 | #endif 54 | return {false, locking_mode::NORMAL}; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /tests/builtin_tables.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | using Catch::Matchers::Equals; 6 | 7 | TEST_CASE("builtin tables") { 8 | SECTION("sqlite_schema") { 9 | auto storage = make_storage("", make_sqlite_schema_table()); 10 | storage.sync_schema(); 11 | 12 | auto masterRows = storage.get_all(); 13 | std::ignore = masterRows; 14 | #ifdef SQLITE_ORM_WITH_CPP20_ALIASES 15 | auto schemaRows = storage.get_all(); 16 | 17 | STATIC_REQUIRE(std::is_same_v); 18 | REQUIRE_THAT(schemaRows, Equals(masterRows)); 19 | 20 | auto schemaRows2 = storage.get_all(); 21 | 22 | STATIC_REQUIRE(std::is_same_v); 23 | REQUIRE_THAT(schemaRows2, Equals(masterRows)); 24 | 25 | #if __cpp_lib_containers_ranges >= 202202L 26 | std::vector schemaRows3{std::from_range, storage.iterate()}; 27 | #else 28 | auto view = storage.iterate(); 29 | std::vector schemaRows3{view.begin(), view.end()}; 30 | #endif 31 | REQUIRE_THAT(schemaRows2, Equals(masterRows)); 32 | #endif 33 | } 34 | 35 | #ifdef SQLITE_ENABLE_DBSTAT_VTAB 36 | SECTION("dbstat") { 37 | struct User { 38 | int id = 0; 39 | std::string name; 40 | }; 41 | auto storage = make_storage( 42 | "", 43 | make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)), 44 | make_dbstat_table()); 45 | storage.sync_schema(); 46 | 47 | storage.remove_all(); 48 | 49 | storage.replace(User{1, "Dua Lipa"}); 50 | 51 | auto dbstatRows = storage.get_all(); 52 | std::ignore = dbstatRows; 53 | 54 | #ifdef SQLITE_ORM_WITH_CPP20_ALIASES 55 | dbstatRows = storage.get_all(); 56 | #endif 57 | } 58 | #endif // SQLITE_ENABLE_DBSTAT_VTAB 59 | } 60 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.16) 2 | 3 | # Note: find_package(SQLite3 REQUIRED) already done in top-level CMakeLists 4 | FetchContent_MakeAvailable(Catch2) 5 | 6 | option(SQLITE_ORM_OMITS_CODECVT "Omits codec testing" OFF) 7 | 8 | # Glob all .cpp files recursively in the current directory 9 | file(GLOB_RECURSE UNIT_TEST_SOURCES "*.cpp") 10 | 11 | add_executable(unit_tests ${UNIT_TEST_SOURCES}) 12 | 13 | if(SQLITE_ORM_OMITS_CODECVT) 14 | message(STATUS "SQLITE_ORM_OMITS_CODECVT is enabled") 15 | target_compile_definitions(unit_tests PRIVATE SQLITE_ORM_OMITS_CODECVT=1) 16 | endif() 17 | 18 | if (MSVC) 19 | target_compile_options(unit_tests PUBLIC 20 | # multi-processor compilation 21 | /MP) 22 | if (MSVC_VERSION LESS_EQUAL 1900) 23 | target_compile_options(unit_tests PUBLIC 24 | # C4503: decorated name length exceeded 25 | /wd4503 26 | # C4800: forcing value to bool (performance warning) 27 | /wd4800) 28 | else() 29 | target_compile_options(unit_tests PUBLIC 30 | # warning-level 4 31 | /W4 32 | # C4127: conditional expression is constant 33 | /wd4127 34 | # C4456: declaration of 'symbol' hides previous local declaration 35 | /wd4456 36 | # C4458: declaration of 'symbol' hides class member 37 | /wd4458) 38 | endif() 39 | if (CMAKE_CXX_FLAGS MATCHES "/D_UNICODE") 40 | # explicitly set the entry point of the executable file, 41 | # otherwise for some reason the linker will not pick up `wmain`, which is provided by the static Catch2 library 42 | target_link_options(unit_tests PRIVATE "/ENTRY:wmainCRTStartup") 43 | endif() 44 | endif() 45 | 46 | target_precompile_headers(unit_tests PRIVATE 47 | 48 | 49 | ) 50 | 51 | # note: sqlite3 already linked in top-level CMakeLists 52 | target_link_libraries(unit_tests PRIVATE sqlite_orm Catch2::Catch2WithMain) 53 | 54 | add_test(NAME "All_in_one_unit_test" 55 | COMMAND unit_tests 56 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 57 | -------------------------------------------------------------------------------- /tests/static_tests/functional/same_or_void.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include // std::is_same 5 | #include // std::string 6 | #include // std::tuple 7 | 8 | using namespace sqlite_orm; 9 | 10 | TEST_CASE("same_or_void") { 11 | using internal::common_type_of_t; 12 | using internal::same_or_void; 13 | 14 | // one argument 15 | STATIC_REQUIRE(std::is_same::type, int>::value); 16 | STATIC_REQUIRE(std::is_same::type, std::string>::value); 17 | STATIC_REQUIRE(std::is_same::type, long>::value); 18 | 19 | // two arguments 20 | STATIC_REQUIRE(std::is_same::type, int>::value); 21 | STATIC_REQUIRE(std::is_same::type, void>::value); 22 | STATIC_REQUIRE(std::is_same::type, std::string>::value); 23 | STATIC_REQUIRE(std::is_same::type, void>::value); 24 | 25 | // three arguments 26 | STATIC_REQUIRE(std::is_same::type, int>::value); 27 | STATIC_REQUIRE(std::is_same::type, long>::value); 28 | STATIC_REQUIRE(std::is_same::type, void>::value); 29 | STATIC_REQUIRE(std::is_same::type, void>::value); 30 | STATIC_REQUIRE(std::is_same::type, void>::value); 31 | 32 | // four arguments 33 | STATIC_REQUIRE(std::is_same::type, int>::value); 34 | STATIC_REQUIRE(std::is_same::type, long>::value); 35 | STATIC_REQUIRE(std::is_same::type, void>::value); 36 | 37 | // type pack, e.g. tuple 38 | STATIC_REQUIRE(std::is_same>, int>::value); 39 | STATIC_REQUIRE(std::is_same>, long>::value); 40 | STATIC_REQUIRE( 41 | std::is_same>, polyfill::nonesuch>::value); 42 | } 43 | -------------------------------------------------------------------------------- /tests/static_tests/is_column_with_insertable_primary_key.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | using namespace internal; 6 | 7 | template 8 | using insertable_index_sequence = filter_tuple_sequence_t>; 12 | template 13 | using noninsertable_index_sequence = filter_tuple_sequence_t::template fn, 15 | polyfill::type_identity_t, 16 | col_index_sequence_with>; 17 | 18 | TEST_CASE("is_column_with_insertable_primary_key") { 19 | struct User { 20 | int id; 21 | std::string username; 22 | std::string password; 23 | bool isActive; 24 | }; 25 | 26 | auto insertable = std::make_tuple( /// 27 | make_column("", &User::id, primary_key()), 28 | make_column("", &User::username, primary_key(), default_value("Clint Eastwood")), 29 | make_column("", &User::username, primary_key(), default_value(std::vector{})), 30 | make_column("", &User::username, primary_key().autoincrement())); 31 | 32 | auto noninsertable = std::make_tuple( /// 33 | make_column("", &User::username, primary_key()), 34 | make_column("", &User::password, primary_key())); 35 | 36 | auto outside = std::make_tuple( /// 37 | make_column("", &User::id), ///< not a primary key 38 | std::make_shared() ///< not a column 39 | ); 40 | 41 | STATIC_REQUIRE(insertable_index_sequence::size() == 4); 42 | STATIC_REQUIRE(noninsertable_index_sequence::size() == 2); 43 | STATIC_REQUIRE(noninsertable_index_sequence::size() == 0); 44 | } -------------------------------------------------------------------------------- /tests/statement_serializer_tests/schema/trigger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer trigger") { 7 | using internal::serialize; 8 | struct Lead { 9 | int id = 0; 10 | std::string firstName; 11 | std::string lastName; 12 | std::string email; 13 | std::string phone; 14 | }; 15 | auto table = make_table("leads", 16 | make_column("id", &Lead::id, primary_key()), 17 | make_column("first_name", &Lead::firstName), 18 | make_column("last_name", &Lead::lastName), 19 | make_column("email", &Lead::email), 20 | make_column("phone", &Lead::phone)); 21 | using db_objects_t = internal::db_objects_tuple; 22 | auto dbObjects = db_objects_t{table}; 23 | using context_t = internal::serializer_context; 24 | context_t context{dbObjects}; 25 | std::string value; 26 | decltype(value) expected; 27 | SECTION("without for each row") { 28 | auto expression = make_trigger("validate_email_before_insert_leads", 29 | before() 30 | .insert() 31 | .on() 32 | .begin(select(case_() 33 | .when(not like(new_(&Lead::email), "%_@__%.__%"), 34 | then(raise_abort("Invalid email address"))) 35 | .end())) 36 | .end()); 37 | value = serialize(expression, context); 38 | expected = 39 | R"(CREATE TRIGGER IF NOT EXISTS "validate_email_before_insert_leads" BEFORE INSERT ON "leads" BEGIN SELECT )" 40 | R"(CASE WHEN NOT NEW."email" LIKE '%_@__%.__%' THEN RAISE(ABORT, 'Invalid email address') END; END)"; 41 | } 42 | REQUIRE(value == expected); 43 | } 44 | -------------------------------------------------------------------------------- /dev/indexed_column.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::string 4 | #include // std::move 5 | 6 | #include "ast/where.h" 7 | 8 | namespace sqlite_orm { 9 | 10 | namespace internal { 11 | 12 | template 13 | struct indexed_column_t { 14 | using column_type = C; 15 | 16 | #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED 17 | indexed_column_t(column_type _column_or_expression) : 18 | column_or_expression(std::move(_column_or_expression)) {} 19 | #endif 20 | 21 | column_type column_or_expression; 22 | std::string _collation_name; 23 | int _order = 0; // -1 = desc, 1 = asc, 0 = not specified 24 | 25 | indexed_column_t collate(std::string name) { 26 | auto res = std::move(*this); 27 | res._collation_name = std::move(name); 28 | return res; 29 | } 30 | 31 | indexed_column_t asc() { 32 | auto res = std::move(*this); 33 | res._order = 1; 34 | return res; 35 | } 36 | 37 | indexed_column_t desc() { 38 | auto res = std::move(*this); 39 | res._order = -1; 40 | return res; 41 | } 42 | }; 43 | 44 | template 45 | indexed_column_t make_indexed_column(C col) { 46 | return {std::move(col)}; 47 | } 48 | 49 | template 50 | where_t make_indexed_column(where_t wher) { 51 | return std::move(wher); 52 | } 53 | 54 | template 55 | indexed_column_t make_indexed_column(indexed_column_t col) { 56 | return std::move(col); 57 | } 58 | } 59 | 60 | /** 61 | * Use this function to specify indexed column inside `make_index` function call. 62 | * Example: make_index("index_name", indexed_column(&User::id).asc()) 63 | */ 64 | template 65 | internal::indexed_column_t indexed_column(C column_or_expression) { 66 | return {std::move(column_or_expression)}; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Run unit_tests", 6 | "type": "shell", 7 | "command": "${workspaceFolder}/build/tests/unit_tests", 8 | "options": { 9 | "cwd": "${workspaceFolder}/build/tests" 10 | }, 11 | "group": { 12 | "kind": "test", 13 | "isDefault": true 14 | }, 15 | "problemMatcher": [], 16 | "presentation": { 17 | "reveal": "always", 18 | "panel": "shared" 19 | } 20 | }, 21 | { 22 | "label": "Run amalgamate script", 23 | "type": "shell", 24 | "command": "python3", 25 | "args": [ 26 | "third_party/amalgamate/amalgamate.py", 27 | "-c", 28 | "third_party/amalgamate/config.json", 29 | "-s", 30 | "." 31 | ], 32 | "group": { 33 | "kind": "build", 34 | "isDefault": false 35 | }, 36 | "problemMatcher": [], 37 | "presentation": { 38 | "reveal": "always", 39 | "panel": "shared" 40 | } 41 | }, 42 | { 43 | "label": "Run clang-format", 44 | "type": "shell", 45 | "command": "clang-format", 46 | "args": [ 47 | "-i", 48 | "-style=file", 49 | "include/sqlite_orm/*.h", 50 | "tests/*.cpp", 51 | "tests/*/*.cpp", 52 | "tests/*/*/*.cpp", 53 | "dev/*.h", 54 | "dev/*/*.h", 55 | "tests/*/*.h", 56 | "examples/*.cpp", 57 | "examples/*/src/*.cpp" 58 | ], 59 | "group": { 60 | "kind": "build", 61 | "isDefault": false 62 | }, 63 | "problemMatcher": [], 64 | "presentation": { 65 | "reveal": "always", 66 | "panel": "shared" 67 | } 68 | } 69 | ] 70 | } -------------------------------------------------------------------------------- /examples/collate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using std::cout; 9 | using std::endl; 10 | 11 | struct User { 12 | int id; 13 | std::string name; 14 | std::time_t createdAt; 15 | }; 16 | 17 | struct Foo { 18 | std::string text; 19 | int baz; 20 | }; 21 | 22 | int main(int, char**) { 23 | 24 | using namespace sqlite_orm; 25 | auto storage = make_storage( 26 | "collate.sqlite", 27 | make_table("users", 28 | make_column("id", &User::id, primary_key()), 29 | make_column("name", &User::name), 30 | make_column("created_at", &User::createdAt)), 31 | make_table("foo", make_column("text", &Foo::text, collate_nocase()), make_column("baz", &Foo::baz))); 32 | storage.sync_schema(); 33 | storage.remove_all(); 34 | storage.remove_all(); 35 | 36 | storage.insert(User{0, "Lil Kim", std::time(nullptr)}); 37 | storage.insert(User{0, "lil kim", std::time(nullptr)}); 38 | storage.insert(User{0, "Nicki Minaj", std::time(nullptr)}); 39 | 40 | // SELECT COUNT(*) 41 | // FROM users 42 | // WHERE name = 'lil kim' 43 | auto preciseLilKimsCount = storage.count(where(is_equal(&User::name, "lil kim"))); 44 | cout << "preciseLilKimsCount = " << preciseLilKimsCount << endl; 45 | 46 | // SELECT COUNT(*) FROM users WHERE name = 'lil kim' COLLATE NOCASE 47 | auto nocaseCount = storage.count(where(is_equal(&User::name, "lil kim").collate_nocase())); 48 | cout << "nocaseCount = " << nocaseCount << endl; 49 | 50 | // SELECT COUNT(*) FROM users 51 | cout << "total users count = " << storage.count() << endl; 52 | 53 | storage.insert(Foo{"Touch", 10}); 54 | storage.insert(Foo{"touch", 20}); 55 | 56 | cout << "foo count = " << storage.count(where(c(&Foo::text) == "touch")) << endl; 57 | 58 | // SELECT id 59 | // FROM users 60 | // ORDER BY name COLLATE RTRIM ASC 61 | auto rows = storage.select(&User::id, order_by(&User::name).collate_rtrim().asc()); 62 | cout << "rows count = " << rows.size() << endl; 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /tests/built_in_functions_tests/datetime_function_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("time") { 7 | auto storage = make_storage({}); 8 | { 9 | auto rows = storage.select(time("12:00", "localtime")); 10 | REQUIRE(rows.size() == 1); 11 | REQUIRE_FALSE(rows.front().empty()); 12 | } 13 | { 14 | auto rows = storage.select(time("12:00", "utc")); 15 | REQUIRE(rows.size() == 1); 16 | REQUIRE_FALSE(rows.front().empty()); 17 | } 18 | } 19 | 20 | TEST_CASE("julianday") { 21 | struct Test { 22 | std::string text; 23 | }; 24 | 25 | auto storage = make_storage({}, make_table("test", make_column("text", &Test::text))); 26 | storage.sync_schema(); 27 | auto singleTestCase = [&storage](const std::string& arg, double expected) { 28 | { 29 | auto rows = storage.select(julianday(arg)); 30 | REQUIRE(rows.size() == 1); 31 | REQUIRE((rows.front() - expected) < 0.001); // too much precision 32 | } 33 | { 34 | storage.insert(Test{arg}); 35 | auto rows = storage.select(julianday(&Test::text)); 36 | REQUIRE(rows.size() == 1); 37 | REQUIRE((rows.front() - expected) < 0.001); 38 | storage.remove_all(); 39 | } 40 | }; 41 | singleTestCase("2016-10-18", 2457679.5); 42 | singleTestCase("2016-10-18 16:45", 2457680.19791667); 43 | singleTestCase("2016-10-18 16:45:30", 2457680.19826389); 44 | } 45 | 46 | TEST_CASE("datetime") { 47 | auto storage = make_storage({}); 48 | auto rows = storage.select(datetime("now")); 49 | REQUIRE(rows.size() == 1); 50 | REQUIRE_FALSE(rows.front().empty()); 51 | } 52 | 53 | TEST_CASE("date") { 54 | auto storage = make_storage({}); 55 | auto rows = storage.select(date("now", "start of month", "+1 month", "-1 day")); 56 | REQUIRE(rows.size() == 1); 57 | REQUIRE_FALSE(rows.front().empty()); 58 | } 59 | 60 | TEST_CASE("strftime") { 61 | auto storage = make_storage({}); 62 | auto rows = storage.select(strftime("%Y %m %d", "now")); 63 | REQUIRE(rows.size() == 1); 64 | REQUIRE_FALSE(rows.front().empty()); 65 | } 66 | -------------------------------------------------------------------------------- /tests/operators/like.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("Like operator") { 7 | struct User { 8 | int id = 0; 9 | std::string name; 10 | 11 | #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED 12 | User() = default; 13 | User(int id, std::string name) : id{id}, name{std::move(name)} {} 14 | #endif 15 | }; 16 | struct Pattern { 17 | std::string value; 18 | }; 19 | 20 | auto storage = make_storage("", 21 | make_table("users", 22 | make_column("id", &User::id, primary_key().autoincrement()), 23 | make_column("name", &User::name)), 24 | make_table("patterns", make_column("value", &Pattern::value))); 25 | storage.sync_schema(); 26 | 27 | storage.insert(User{0, "Sia"}); 28 | storage.insert(User{0, "Stark"}); 29 | storage.insert(User{0, "Index"}); 30 | 31 | auto whereCondition = where(like(&User::name, "S%")); 32 | { 33 | auto users = storage.get_all(whereCondition); 34 | REQUIRE(users.size() == 2); 35 | } 36 | { 37 | auto rows = storage.select(&User::id, whereCondition); 38 | REQUIRE(rows.size() == 2); 39 | } 40 | { 41 | auto rows = storage.select(like("ototo", "ot_to")); 42 | REQUIRE(rows.size() == 1); 43 | REQUIRE(rows.front() == true); 44 | } 45 | { 46 | auto rows = storage.select(not like("ototo", "ot_to")); 47 | REQUIRE(rows.size() == 1); 48 | REQUIRE(rows.front() == false); 49 | } 50 | { 51 | auto rows = storage.select(like(&User::name, "S%a")); 52 | REQUIRE(rows.size() == 3); 53 | REQUIRE(count(rows.begin(), rows.end(), true) == 1); 54 | } 55 | 56 | storage.insert(Pattern{"o%o"}); 57 | { 58 | auto rows = storage.select(like("ototo", &Pattern::value)); 59 | REQUIRE(rows.size() == 1); 60 | REQUIRE(rows.front() == true); 61 | } 62 | { 63 | auto rows = storage.select(like("aaa", &Pattern::value)); 64 | REQUIRE(rows.size() == 1); 65 | REQUIRE(rows.front() == false); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/row_id.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("Row id") { 7 | 8 | struct SimpleTable { 9 | std::string letter; 10 | std::string desc; 11 | }; 12 | 13 | auto storage = make_storage( 14 | "rowid.sqlite", 15 | make_table("tbl1", make_column("letter", &SimpleTable::letter), make_column("desc", &SimpleTable::desc))); 16 | storage.sync_schema(); 17 | storage.remove_all(); 18 | 19 | storage.insert(SimpleTable{"A", "first letter"}); 20 | storage.insert(SimpleTable{"B", "second letter"}); 21 | storage.insert(SimpleTable{"C", "third letter"}); 22 | SECTION("select everything") { 23 | auto rows = storage.select(columns(rowid(), 24 | oid(), 25 | _rowid_(), 26 | rowid(), 27 | oid(), 28 | _rowid_(), 29 | &SimpleTable::letter, 30 | &SimpleTable::desc)); 31 | for (size_t i = 0; i < rows.size(); ++i) { 32 | auto& row = rows[i]; 33 | REQUIRE(std::get<0>(row) == std::get<1>(row)); 34 | REQUIRE(std::get<1>(row) == std::get<2>(row)); 35 | REQUIRE(std::get<2>(row) == static_cast(i + 1)); 36 | REQUIRE(std::get<2>(row) == std::get<3>(row)); 37 | REQUIRE(std::get<3>(row) == std::get<4>(row)); 38 | REQUIRE(std::get<4>(row) == std::get<5>(row)); 39 | } 40 | } 41 | SECTION("select single") { 42 | std::vector> rows; 43 | SECTION("rowid") { 44 | rows = storage.select(max(rowid())); 45 | } 46 | SECTION("oid") { 47 | rows = storage.select(max(oid())); 48 | } 49 | SECTION("_rowid_") { 50 | rows = storage.select(max(_rowid_())); 51 | } 52 | REQUIRE(rows.size() == 1); 53 | REQUIRE(rows[0]); 54 | REQUIRE(*rows[0] == 3); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /dev/sync_schema_result.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace sqlite_orm { 6 | 7 | enum class sync_schema_result { 8 | 9 | /** 10 | * created new table, table with the same tablename did not exist 11 | */ 12 | new_table_created, 13 | 14 | /** 15 | * table schema is the same as storage, nothing to be done 16 | */ 17 | already_in_sync, 18 | 19 | /** 20 | * removed excess columns in table (than storage) without dropping a table 21 | */ 22 | old_columns_removed, 23 | 24 | /** 25 | * lacking columns in table (than storage) added without dropping a table 26 | */ 27 | new_columns_added, 28 | 29 | /** 30 | * both old_columns_removed and new_columns_added 31 | */ 32 | new_columns_added_and_old_columns_removed, 33 | 34 | /** 35 | * old table is dropped and new is recreated. Reasons : 36 | * 1. delete excess columns in the table than storage if preseve = false 37 | * 2. Lacking columns in the table cannot be added due to NULL and DEFAULT constraint 38 | * 3. Reasons 1 and 2 both together 39 | * 4. data_type mismatch between table and storage. 40 | */ 41 | dropped_and_recreated, 42 | }; 43 | 44 | inline std::ostream& operator<<(std::ostream& os, sync_schema_result value) { 45 | switch (value) { 46 | case sync_schema_result::new_table_created: 47 | return os << "new table created"; 48 | case sync_schema_result::already_in_sync: 49 | return os << "table and storage is already in sync."; 50 | case sync_schema_result::old_columns_removed: 51 | return os << "old excess columns removed"; 52 | case sync_schema_result::new_columns_added: 53 | return os << "new columns added"; 54 | case sync_schema_result::new_columns_added_and_old_columns_removed: 55 | return os << "old excess columns removed and new columns added"; 56 | case sync_schema_result::dropped_and_recreated: 57 | return os << "old table dropped and recreated"; 58 | } 59 | return os; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/prepared_statement_tests/prepared_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::string 4 | #include // std::ignore 5 | #include 6 | #include // std::ostream 7 | 8 | namespace PreparedStatementTests { 9 | struct User { 10 | int id = 0; 11 | std::string name; 12 | 13 | #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED 14 | User() = default; 15 | User(int id, std::string name) : id{id}, name{std::move(name)} {} 16 | #endif 17 | }; 18 | 19 | struct Visit { 20 | int id = 0; 21 | decltype(User::id) userId; 22 | long time = 0; 23 | 24 | #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED 25 | Visit() = default; 26 | Visit(int id, decltype(Visit::userId) userId, long time) : id{id}, userId{userId}, time{time} {} 27 | #endif 28 | }; 29 | 30 | struct UserAndVisit { 31 | decltype(User::id) userId; 32 | decltype(Visit::id) visitId; 33 | std::string description; 34 | 35 | #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED 36 | UserAndVisit() = default; 37 | UserAndVisit(decltype(UserAndVisit::userId) userId, 38 | decltype(UserAndVisit::visitId) visitId, 39 | std::string description) : userId{userId}, visitId{visitId}, description{std::move(description)} {} 40 | #endif 41 | }; 42 | 43 | inline bool operator==(const User& lhs, const User& rhs) { 44 | return lhs.id == rhs.id && lhs.name == rhs.name; 45 | } 46 | 47 | inline bool operator!=(const User& lhs, const User& rhs) { 48 | return !(lhs == rhs); 49 | } 50 | 51 | inline void testSerializing(const sqlite_orm::internal::prepared_statement_base& statement) { 52 | auto sql = statement.sql(); 53 | std::ignore = sql; 54 | #if SQLITE_VERSION_NUMBER >= 3014000 55 | auto expanded = statement.expanded_sql(); 56 | std::ignore = expanded; 57 | #endif 58 | #if SQLITE_VERSION_NUMBER >= 3026000 and defined(SQLITE_ENABLE_NORMALIZE) 59 | auto normalized = statement.normalized_sql(); 60 | std::ignore = normalized; 61 | #endif 62 | } 63 | 64 | inline std::ostream& operator<<(std::ostream& os, const User& user) { 65 | return os << "{" << user.id << ", " << user.name << "}"; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/statements/remove_all.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer remove_all") { 7 | using internal::serialize; 8 | struct User { 9 | int id = 0; 10 | std::string name; 11 | }; 12 | auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)); 13 | using db_objects_t = internal::db_objects_tuple; 14 | db_objects_t dbObjects{table}; 15 | using context_t = internal::serializer_context; 16 | context_t context{dbObjects}; 17 | 18 | std::string value; 19 | std::string expected; 20 | 21 | SECTION("all") { 22 | auto expression = remove_all(); 23 | value = serialize(expression, context); 24 | expected = R"(DELETE FROM "users")"; 25 | } 26 | SECTION("where") { 27 | auto expression = remove_all(where(&User::id == c(1))); 28 | value = serialize(expression, context); 29 | expected = R"(DELETE FROM "users" WHERE ("id" = 1))"; 30 | } 31 | SECTION("conditions") { 32 | auto expression = remove_all(where(&User::id == c(1)), limit(1)); 33 | value = serialize(expression, context); 34 | expected = R"(DELETE FROM "users" WHERE ("id" = 1) LIMIT 1)"; 35 | } 36 | #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) 37 | #ifdef SQLITE_ORM_WITH_CPP20_ALIASES 38 | SECTION("With clause") { 39 | constexpr orm_cte_moniker auto data = "data"_cte; 40 | constexpr auto cteExpression = cte().as(select(1)); 41 | auto dbObjects2 = internal::db_objects_cat(dbObjects, internal::make_cte_table(dbObjects, cteExpression)); 42 | using context_t = internal::serializer_context; 43 | context_t context2{dbObjects2}; 44 | 45 | auto expression = with(cteExpression, remove_all(where(c(&User::id).in(select(data->*1_colalias))))); 46 | value = serialize(expression, context2); 47 | expected = 48 | R"(WITH "data"("1") AS (SELECT 1) DELETE FROM "users" WHERE ("id" IN (SELECT "data"."1" FROM "data")))"; 49 | } 50 | #endif 51 | #endif 52 | REQUIRE(value == expected); 53 | } 54 | -------------------------------------------------------------------------------- /dev/functional/index_sequence_util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::index_sequence 4 | 5 | namespace sqlite_orm { 6 | namespace internal { 7 | #if defined(SQLITE_ORM_PACK_INDEXING_SUPPORTED) 8 | /** 9 | * Get the index value of an `index_sequence` at a specific position. 10 | */ 11 | template 12 | SQLITE_ORM_CONSTEVAL auto index_sequence_value_at(std::index_sequence) { 13 | return Idx...[Pos]; 14 | } 15 | #elif defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) 16 | /** 17 | * Get the index value of an `index_sequence` at a specific position. 18 | */ 19 | template 20 | SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { 21 | static_assert(Pos < sizeof...(Idx)); 22 | #ifdef SQLITE_ORM_CONSTEVAL_SUPPORTED 23 | size_t result; 24 | #else 25 | size_t result = 0; 26 | #endif 27 | size_t i = 0; 28 | // note: `(void)` cast silences warning 'expression result unused' 29 | (void)((result = Idx, i++ == Pos) || ...); 30 | return result; 31 | } 32 | #else 33 | /** 34 | * Get the index value of an `index_sequence` at a specific position. 35 | * `Pos` must always be `0`. 36 | */ 37 | template 38 | SQLITE_ORM_CONSTEVAL size_t index_sequence_value_at(std::index_sequence) { 39 | static_assert(Pos == 0, ""); 40 | return I; 41 | } 42 | #endif 43 | 44 | template 45 | struct flatten_idxseq { 46 | using type = std::index_sequence<>; 47 | }; 48 | 49 | template 50 | struct flatten_idxseq> { 51 | using type = std::index_sequence; 52 | }; 53 | 54 | template 55 | struct flatten_idxseq, std::index_sequence, Seq...> 56 | : flatten_idxseq, Seq...> {}; 57 | 58 | template 59 | using flatten_idxseq_t = typename flatten_idxseq::type; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.c": "c", 4 | "string": "cpp", 5 | "__bit_reference": "cpp", 6 | "__hash_table": "cpp", 7 | "__split_buffer": "cpp", 8 | "__tree": "cpp", 9 | "array": "cpp", 10 | "deque": "cpp", 11 | "forward_list": "cpp", 12 | "initializer_list": "cpp", 13 | "list": "cpp", 14 | "map": "cpp", 15 | "set": "cpp", 16 | "span": "cpp", 17 | "string_view": "cpp", 18 | "unordered_map": "cpp", 19 | "vector": "cpp", 20 | "chrono": "cpp", 21 | "format": "cpp", 22 | "ranges": "cpp", 23 | "text_encoding": "cpp", 24 | "__locale": "cpp", 25 | "__node_handle": "cpp", 26 | "__verbose_abort": "cpp", 27 | "bitset": "cpp", 28 | "cctype": "cpp", 29 | "charconv": "cpp", 30 | "cmath": "cpp", 31 | "codecvt": "cpp", 32 | "cstdarg": "cpp", 33 | "cstddef": "cpp", 34 | "cstdint": "cpp", 35 | "cstdio": "cpp", 36 | "cstdlib": "cpp", 37 | "cstring": "cpp", 38 | "ctime": "cpp", 39 | "cwchar": "cpp", 40 | "cwctype": "cpp", 41 | "memory": "cpp", 42 | "iomanip": "cpp", 43 | "ios": "cpp", 44 | "iosfwd": "cpp", 45 | "istream": "cpp", 46 | "limits": "cpp", 47 | "locale": "cpp", 48 | "mutex": "cpp", 49 | "new": "cpp", 50 | "optional": "cpp", 51 | "ostream": "cpp", 52 | "print": "cpp", 53 | "ratio": "cpp", 54 | "sstream": "cpp", 55 | "stdexcept": "cpp", 56 | "streambuf": "cpp", 57 | "tuple": "cpp", 58 | "typeindex": "cpp", 59 | "typeinfo": "cpp", 60 | "variant": "cpp", 61 | "algorithm": "cpp", 62 | "clocale": "cpp", 63 | "atomic": "cpp", 64 | "bit": "cpp", 65 | "*.tcc": "cpp", 66 | "compare": "cpp", 67 | "concepts": "cpp", 68 | "exception": "cpp", 69 | "functional": "cpp", 70 | "iterator": "cpp", 71 | "memory_resource": "cpp", 72 | "random": "cpp", 73 | "system_error": "cpp", 74 | "type_traits": "cpp", 75 | "utility": "cpp", 76 | "__config": "cpp", 77 | "any": "cpp" 78 | } 79 | } -------------------------------------------------------------------------------- /examples/composite_key.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This example shows you how to create a storage with a tablw with a composite primary key 3 | * and another table woth foreign key to first table's primary compisite key. 4 | */ 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #if SQLITE_VERSION_NUMBER >= 3006019 14 | #define ENABLE_THIS_EXAMPLE 15 | #endif 16 | 17 | #ifdef ENABLE_THIS_EXAMPLE 18 | using std::cout; 19 | using std::endl; 20 | 21 | struct User { 22 | int id; 23 | std::string firstName; 24 | std::string lastName; 25 | }; 26 | 27 | struct UserVisit { 28 | int userId; 29 | std::string userFirstName; 30 | std::time_t time; 31 | }; 32 | #endif 33 | 34 | int main() { 35 | #ifdef ENABLE_THIS_EXAMPLE 36 | using namespace sqlite_orm; 37 | 38 | auto storage = make_storage( 39 | {}, 40 | make_table("users", 41 | make_column("id", &User::id), 42 | make_column("first_name", &User::firstName), 43 | make_column("last_name", &User::lastName), 44 | primary_key(&User::id, &User::firstName)), 45 | make_table("visits", 46 | make_column("user_id", &UserVisit::userId), 47 | make_column("user_first_name", &UserVisit::userFirstName), 48 | make_column("time", &UserVisit::time), 49 | foreign_key(&UserVisit::userId, &UserVisit::userFirstName).references(&User::id, &User::firstName))); 50 | storage.sync_schema(); 51 | 52 | storage.replace(User{ 53 | 1, 54 | "Bebe", 55 | "Rexha", 56 | }); 57 | auto bebeRexha = storage.get(1, "Bebe"); 58 | cout << "bebeRexha = " << storage.dump(bebeRexha) << endl; 59 | auto bebeRexhaMaybe = storage.get_pointer(1, "Bebe"); 60 | try { 61 | // 2 and 'Drake' values will be ignored cause they are primary keys 62 | storage.insert(User{2, "Drake", "Singer"}); 63 | } catch (const std::system_error& e) { 64 | cout << "exception = " << e.what() << endl; 65 | } 66 | storage.replace(User{2, "The Weeknd", "Singer"}); 67 | auto weeknd = storage.get(2, "The Weeknd"); 68 | cout << "weeknd = " << storage.dump(weeknd) << endl; 69 | #endif 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /tests/unique_cases/delete_with_two_fields.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("delete with two fields") { 7 | struct Device { 8 | std::string serialNumber; 9 | std::string deviceId; 10 | }; 11 | auto storage = make_storage({}, 12 | make_table("devices", 13 | make_column("serial_number", &Device::serialNumber), 14 | make_column("device_id", &Device::deviceId))); 15 | storage.sync_schema(); 16 | 17 | SECTION("none") { 18 | storage.remove_all(where(in(std::make_tuple(&Device::serialNumber, &Device::deviceId), 19 | values(std::make_tuple("abc", "123"), std::make_tuple("def", "456"))))); 20 | 21 | storage.remove_all( 22 | where(in(std::make_tuple(&Device::serialNumber, &Device::deviceId), 23 | values(std::vector>{std::make_tuple("abc", "123"), 24 | std::make_tuple("def", "456")})))); 25 | REQUIRE(storage.count() == 0); 26 | } 27 | SECTION("two devices") { 28 | Device device_abc{"abc", "123"}; 29 | Device device_def{"def", "456"}; 30 | 31 | storage.replace(device_abc); 32 | REQUIRE(storage.count() == 1); 33 | storage.replace(device_def); 34 | REQUIRE(storage.count() == 2); 35 | 36 | SECTION("static condition") { 37 | storage.remove_all(where(in(std::make_tuple(&Device::serialNumber, &Device::deviceId), 38 | values(std::make_tuple("abc", "123"), std::make_tuple("def", "456"))))); 39 | } 40 | SECTION("dynamic condition") { 41 | storage.remove_all( 42 | where(in(std::make_tuple(&Device::serialNumber, &Device::deviceId), 43 | values(std::vector>{std::make_tuple("abc", "123"), 44 | std::make_tuple("def", "456")})))); 45 | } 46 | REQUIRE(storage.count() == 0); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/unique_cases/issue1357.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if SQLITE_VERSION_NUMBER >= 3006019 5 | using namespace sqlite_orm; 6 | 7 | #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED 8 | 9 | TEST_CASE("issue1357") { 10 | struct Employee { 11 | int m_empno; 12 | std::string m_ename; 13 | std::string m_job; 14 | std::optional m_mgr; 15 | std::string m_hiredate; 16 | double m_salary; 17 | std::optional m_commission; 18 | int m_depno; 19 | }; 20 | 21 | struct Department { 22 | int m_deptno; 23 | std::string m_deptname; 24 | std::string m_loc; 25 | }; 26 | 27 | using namespace sqlite_orm; 28 | 29 | auto storage = make_storage("", 30 | make_table("Emp", 31 | make_column("empno", &Employee::m_empno, primary_key().autoincrement()), 32 | make_column("ename", &Employee::m_ename), 33 | make_column("job", &Employee::m_job), 34 | make_column("mgr", &Employee::m_mgr), 35 | make_column("hiredate", &Employee::m_hiredate), 36 | make_column("salary", &Employee::m_salary), 37 | make_column("comm", &Employee::m_commission), 38 | make_column("depno", &Employee::m_depno), 39 | foreign_key(&Employee::m_depno).references(&Department::m_deptno)), 40 | make_table("Dept", 41 | make_column("deptno", &Department::m_deptno, primary_key().autoincrement()), 42 | make_column("deptname", &Department::m_deptname), 43 | make_column("loc", &Department::m_loc, null()))); 44 | storage.sync_schema(true); 45 | storage.insert(Department{10, "Accounts", "New York"}); 46 | storage.insert(Employee{1, "Paul", "Salesman", 2, "2002-02-12", 20000.0, 0.0, 1}); 47 | storage.insert(Employee{2, "Allen", "Salesman", 2, "2002-02-12", 20000.0, 0.0, 1}); 48 | REQUIRE_NOTHROW(storage.sync_schema(true)); 49 | } 50 | #endif 51 | #endif 52 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/bitwise_operators.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer bitwise operators") { 7 | internal::db_objects_tuple<> storage; 8 | internal::serializer_context> context{storage}; 9 | std::string value; 10 | decltype(value) expected; 11 | SECTION("bitwise_or") { 12 | SECTION("func") { 13 | value = serialize(bitwise_or(3, 5), context); 14 | } 15 | SECTION("operator") { 16 | value = serialize(c(3) | 5, context); 17 | } 18 | expected = "3 | 5"; 19 | } 20 | SECTION("bitwise_and") { 21 | SECTION("func") { 22 | value = serialize(bitwise_and(5, -9), context); 23 | } 24 | SECTION("operator") { 25 | value = serialize(c(5) & -9, context); 26 | } 27 | expected = "5 & -9"; 28 | } 29 | SECTION("bitwise_shift_left") { 30 | SECTION("func") { 31 | value = serialize(bitwise_shift_left(10, 1), context); 32 | } 33 | SECTION("operator") { 34 | value = serialize(c(10) << 1, context); 35 | } 36 | expected = "10 << 1"; 37 | } 38 | SECTION("bitwise_shift_right") { 39 | SECTION("func") { 40 | value = serialize(bitwise_shift_right(10, 2), context); 41 | } 42 | SECTION("operator") { 43 | value = serialize(c(10) >> 2, context); 44 | } 45 | expected = "10 >> 2"; 46 | } 47 | SECTION("bitwise_not") { 48 | SECTION("func") { 49 | value = serialize(bitwise_not(20), context); 50 | } 51 | SECTION("operator") { 52 | value = serialize(~c(20), context); 53 | } 54 | expected = "~20"; 55 | } 56 | SECTION("parentheses keeping order of precedence") { 57 | SECTION("1") { 58 | value = serialize(c(4) << 5 << 3, context); 59 | expected = "(4 << 5) << 3"; 60 | } 61 | SECTION("2") { 62 | value = serialize(4 << (c(5) << 3), context); 63 | expected = "4 << (5 << 3)"; 64 | } 65 | SECTION("3") { 66 | value = serialize(4 | ~c(5) & 3 | 1, context); 67 | expected = "(4 | (~5 & 3)) | 1"; 68 | } 69 | } 70 | REQUIRE(value == expected); 71 | } 72 | -------------------------------------------------------------------------------- /tests/schema/column_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | using internal::is_generated_always; 6 | 7 | TEST_CASE("column tests is") { 8 | struct User { 9 | int id = 0; 10 | int age = 0; 11 | }; 12 | SECTION("no constraints") { 13 | auto column = make_column("id", &User::id); 14 | REQUIRE_FALSE(column.is()); 15 | } 16 | #if SQLITE_VERSION_NUMBER >= 3031000 17 | SECTION("1 constraint: generated") { 18 | SECTION("full") { 19 | auto column = make_column("age", &User::age, generated_always_as(add(&User::id, 5))); 20 | REQUIRE(column.is()); 21 | } 22 | 23 | SECTION("not full") { 24 | auto column = make_column("age", &User::age, as(add(&User::id, 5))); 25 | REQUIRE(column.is()); 26 | } 27 | } 28 | #endif 29 | SECTION("1 constraint: primary key") { 30 | auto column = make_column("id", &User::id, primary_key()); 31 | REQUIRE_FALSE(column.is()); 32 | } 33 | } 34 | 35 | TEST_CASE("column is_not_null") { 36 | struct User { 37 | int id = 0; 38 | std::unique_ptr name; 39 | }; 40 | SECTION("non-nullable") { 41 | SECTION("implicit") { 42 | auto column = make_column("id", &User::id); 43 | REQUIRE(column.is_not_null()); 44 | } 45 | SECTION("explicit not null") { 46 | auto column = make_column("id", &User::id, not_null()); 47 | REQUIRE(column.is_not_null()); 48 | } 49 | SECTION("explicit null") { 50 | auto column = make_column("id", &User::id, null()); 51 | REQUIRE(column.is_not_null()); 52 | } 53 | } 54 | SECTION("nullable") { 55 | SECTION("implicit") { 56 | auto column = make_column("name", &User::name); 57 | REQUIRE(!column.is_not_null()); 58 | } 59 | SECTION("explicit not null") { 60 | auto column = make_column("name", &User::name, not_null()); 61 | REQUIRE(!column.is_not_null()); 62 | } 63 | SECTION("explicit null") { 64 | auto column = make_column("name", &User::name, null()); 65 | REQUIRE(!column.is_not_null()); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/static_tests/functional/tuple_conc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // std::string 4 | #include // std::is_same 5 | 6 | using namespace sqlite_orm; 7 | 8 | TEST_CASE("Tuple conc") { 9 | using namespace internal; 10 | { 11 | using TupleL = std::tuple; 12 | using TupleR = std::tuple; 13 | using IntStringTuple = tuple_cat_t; 14 | STATIC_REQUIRE(std::is_same>::value); 15 | } 16 | { 17 | using TupleL = std::tuple; 18 | using TupleR = std::tuple; 19 | using IntFloatTuple = tuple_cat_t; 20 | STATIC_REQUIRE(std::is_same>::value); 21 | } 22 | { 23 | using TupleL = std::tuple<>; 24 | using TupleR = std::tuple; 25 | using NoneFloatTuple = tuple_cat_t; 26 | STATIC_REQUIRE(std::is_same>::value); 27 | } 28 | { 29 | using TupleL = std::tuple<>; 30 | using TupleR = std::tuple<>; 31 | using NoneNoneTuple = tuple_cat_t; 32 | STATIC_REQUIRE(std::is_same>::value); 33 | } 34 | { 35 | using TupleL = std::tuple; 36 | using TupleR = std::tuple; 37 | using IntFloatDoubleTuple = tuple_cat_t; 38 | STATIC_REQUIRE(std::is_same>::value); 39 | } 40 | { 41 | using Arg = std::tuple; 42 | using SingleArgTuple = tuple_cat_t; 43 | STATIC_REQUIRE(std::is_same::value); 44 | } 45 | { 46 | using Arg1 = std::tuple; 47 | using Arg2 = std::tuple; 48 | using Arg3 = std::tuple; 49 | using IntFloatStringTuple = tuple_cat_t; 50 | STATIC_REQUIRE(std::is_same>::value); 51 | } 52 | { 53 | using Arg1 = std::tuple; 54 | using Arg2 = std::tuple; 55 | using Arg3 = std::tuple<>; 56 | using IntFloatEmptyTuple = tuple_cat_t; 57 | STATIC_REQUIRE(std::is_same>::value); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/ast/set.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("set") { 7 | struct User { 8 | int id = 0; 9 | std::string name; 10 | }; 11 | auto table = make_table("users", make_column("id", &User::id, primary_key()), make_column("name", &User::name)); 12 | using db_objects_t = internal::db_objects_tuple; 13 | auto dbObjects = db_objects_t{table}; 14 | using context_t = internal::serializer_context; 15 | context_t context{dbObjects}; 16 | 17 | std::string value; 18 | std::string expected; 19 | SECTION("one item") { 20 | SECTION("static") { 21 | auto expression = set(assign(&User::id, 5)); 22 | value = internal::serialize(expression, context); 23 | } 24 | SECTION("dynamic") { 25 | auto storage = make_storage("", table); 26 | auto expression = dynamic_set(storage); 27 | expression.push_back(assign(&User::id, 5)); 28 | SECTION("empty") { 29 | //.. 30 | } 31 | SECTION("clear and push_back") { 32 | expression.clear(); 33 | expression.push_back(assign(&User::id, 5)); 34 | } 35 | value = internal::serialize(expression, context); 36 | } 37 | expected = "SET \"id\" = 5"; 38 | } 39 | SECTION("two items") { 40 | SECTION("static") { 41 | auto expression = set(assign(&User::id, 5), assign(&User::name, "ototo")); 42 | value = internal::serialize(expression, context); 43 | } 44 | SECTION("dynamic") { 45 | auto storage = make_storage("", table); 46 | auto expression = dynamic_set(storage); 47 | expression.push_back(assign(&User::id, 5)); 48 | expression.push_back(assign(&User::name, "ototo")); 49 | SECTION("empty") { 50 | //.. 51 | } 52 | SECTION("clear and push_back") { 53 | expression.clear(); 54 | expression.push_back(assign(&User::id, 5)); 55 | expression.push_back(assign(&User::name, "ototo")); 56 | } 57 | value = internal::serialize(expression, context); 58 | } 59 | expected = "SET \"id\" = 5, \"name\" = 'ototo'"; 60 | } 61 | REQUIRE(value == expected); 62 | } 63 | -------------------------------------------------------------------------------- /dev/connection_holder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include // std::string 6 | 7 | #include "error_code.h" 8 | 9 | namespace sqlite_orm { 10 | 11 | namespace internal { 12 | 13 | struct connection_holder { 14 | 15 | connection_holder(std::string filename_) : filename(std::move(filename_)) {} 16 | 17 | void retain() { 18 | if (1 == ++this->_retain_count) { 19 | auto rc = sqlite3_open(this->filename.c_str(), &this->db); 20 | if (rc != SQLITE_OK) { 21 | throw_translated_sqlite_error(db); 22 | } 23 | } 24 | } 25 | 26 | void release() { 27 | if (0 == --this->_retain_count) { 28 | auto rc = sqlite3_close(this->db); 29 | if (rc != SQLITE_OK) { 30 | throw_translated_sqlite_error(db); 31 | } 32 | } 33 | } 34 | 35 | sqlite3* get() const { 36 | return this->db; 37 | } 38 | 39 | int retain_count() const { 40 | return this->_retain_count; 41 | } 42 | 43 | const std::string filename; 44 | 45 | protected: 46 | sqlite3* db = nullptr; 47 | std::atomic_int _retain_count{}; 48 | }; 49 | 50 | struct connection_ref { 51 | connection_ref(connection_holder& holder) : holder(&holder) { 52 | this->holder->retain(); 53 | } 54 | 55 | connection_ref(const connection_ref& other) : holder(other.holder) { 56 | this->holder->retain(); 57 | } 58 | 59 | // rebind connection reference 60 | connection_ref& operator=(const connection_ref& other) { 61 | if (other.holder != this->holder) { 62 | this->holder->release(); 63 | this->holder = other.holder; 64 | this->holder->retain(); 65 | } 66 | 67 | return *this; 68 | } 69 | 70 | ~connection_ref() { 71 | this->holder->release(); 72 | } 73 | 74 | sqlite3* get() const { 75 | return this->holder->get(); 76 | } 77 | 78 | private: 79 | connection_holder* holder = nullptr; 80 | }; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /dev/cte_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) 4 | #include 5 | #include 6 | #endif 7 | 8 | #include "functional/cxx_core_features.h" 9 | #include "functional/cxx_type_traits_polyfill.h" 10 | #include "tuple_helper/tuple_fy.h" 11 | 12 | #if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE) 13 | namespace sqlite_orm { 14 | 15 | namespace internal { 16 | 17 | /** 18 | * Aliased column expression mapped into a CTE, stored as a field in a table column. 19 | */ 20 | template 21 | struct aliased_field { 22 | ~aliased_field() = delete; 23 | aliased_field(const aliased_field&) = delete; 24 | void operator=(const aliased_field&) = delete; 25 | 26 | F field; 27 | }; 28 | 29 | /** 30 | * This class captures various properties and aspects of a subselect's column expression, 31 | * and is used as a proxy in table_t<>. 32 | */ 33 | template 39 | class subselect_mapper { 40 | public: 41 | subselect_mapper() = delete; 42 | 43 | // this type name is used to detect the mapping from moniker to object 44 | using cte_moniker_type = Moniker; 45 | using fields_type = std::tuple; 46 | // this type captures the expressions forming the columns in a subselect; 47 | // it is currently unused, however proves to be useful in compilation errors, 48 | // as it simplifies recognizing errors in column expressions 49 | using expressions_tuple = tuplify_t; 50 | // this type captures column reference expressions specified at CTE construction; 51 | // those are: member pointers, alias holders 52 | using explicit_colrefs_tuple = ExplicitColRefs; 53 | // this type captures column reference expressions from the subselect; 54 | // those are: member pointers, alias holders 55 | using subselect_colrefs_tuple = SubselectColRefs; 56 | // this type captures column reference expressions merged from SubselectColRefs and ExplicitColRefs 57 | using final_colrefs_tuple = FinalColRefs; 58 | }; 59 | } 60 | } 61 | #endif 62 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/arithmetic_operators.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("statement_serializer arithmetic operators") { 7 | internal::db_objects_tuple<> storage; 8 | internal::serializer_context> context{storage}; 9 | std::string value; 10 | decltype(value) expected; 11 | SECTION("unary minus") { 12 | SECTION("func") { 13 | value = serialize(minus(20), context); 14 | } 15 | SECTION("operator") { 16 | value = serialize(-c(20), context); 17 | } 18 | expected = "-20"; 19 | } 20 | SECTION("add") { 21 | SECTION("func") { 22 | value = serialize(add(3, 5), context); 23 | } 24 | SECTION("operator") { 25 | value = serialize(c(3) + 5, context); 26 | } 27 | expected = "3 + 5"; 28 | } 29 | SECTION("sub") { 30 | SECTION("func") { 31 | value = serialize(sub(5, -9), context); 32 | } 33 | SECTION("operator") { 34 | value = serialize(c(5) - -9, context); 35 | } 36 | expected = "5 - -9"; 37 | } 38 | SECTION("mul") { 39 | SECTION("func") { 40 | value = serialize(mul(10, 0.5), context); 41 | } 42 | SECTION("operator") { 43 | value = serialize(c(10) * 0.5, context); 44 | } 45 | expected = "10 * 0.5"; 46 | } 47 | SECTION("div") { 48 | SECTION("func") { 49 | value = serialize(sqlite_orm::div(10, 2), context); 50 | } 51 | SECTION("operator") { 52 | value = serialize(c(10) / 2, context); 53 | } 54 | expected = "10 / 2"; 55 | } 56 | SECTION("mod") { 57 | SECTION("func") { 58 | value = serialize(mod(20, 3), context); 59 | } 60 | SECTION("operator") { 61 | value = serialize(c(20) % 3, context); 62 | } 63 | expected = "20 % 3"; 64 | } 65 | SECTION("parentheses keeping order of precedence") { 66 | SECTION("1") { 67 | value = serialize(c(4) + 5 + -c(3), context); 68 | expected = "(4 + 5) + -3"; 69 | } 70 | SECTION("2") { 71 | value = serialize(4 + -(c(5) + 3), context); 72 | expected = "4 + -(5 + 3)"; 73 | } 74 | SECTION("3") { 75 | value = serialize(4 + c(5) * 3 + 1, context); 76 | expected = "(4 + (5 * 3)) + 1"; 77 | } 78 | } 79 | REQUIRE(value == expected); 80 | } 81 | -------------------------------------------------------------------------------- /tests/operators/bitwise_operators.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("bitwise operators") { 7 | struct Entry { 8 | int lhs = 0; 9 | int rhs = 0; 10 | 11 | #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED 12 | Entry() = default; 13 | Entry(int lhs, int rhs) : lhs{lhs}, rhs{rhs} {} 14 | #endif 15 | }; 16 | auto storage = 17 | make_storage({}, make_table("entries", make_column("lhs", &Entry::lhs), make_column("rhs", &Entry::rhs))); 18 | storage.sync_schema(); 19 | 20 | { 21 | auto rows = storage.select(union_(select(bitwise_or(60, 13)), select(c(60) | 13))); 22 | REQUIRE(rows == std::vector{61}); 23 | } 24 | { 25 | auto rows = storage.select(union_(select(bitwise_and(60, 13)), select(c(60) & 13))); 26 | REQUIRE(rows == std::vector{12}); 27 | } 28 | { 29 | auto rows = storage.select(union_(select(bitwise_shift_left(60, 2)), select(c(60) << 2))); 30 | REQUIRE(rows == std::vector{240}); 31 | } 32 | { 33 | auto rows = storage.select(union_(select(bitwise_shift_right(60, 2)), select(c(60) >> 2))); 34 | REQUIRE(rows == std::vector{15}); 35 | } 36 | { 37 | auto rows = storage.select(union_(select(bitwise_not(60)), select(~c(60)))); 38 | REQUIRE(rows == std::vector{-61}); 39 | } 40 | storage.insert(Entry{60, 13}); 41 | { 42 | auto rows = 43 | storage.select(union_(select(bitwise_or(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) | &Entry::rhs))); 44 | REQUIRE(rows == std::vector{61}); 45 | } 46 | { 47 | auto rows = 48 | storage.select(union_(select(bitwise_and(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) & &Entry::rhs))); 49 | REQUIRE(rows == std::vector{12}); 50 | } 51 | storage.remove_all(); 52 | storage.insert(Entry{60, 2}); 53 | { 54 | auto rows = storage.select( 55 | union_(select(bitwise_shift_left(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) << &Entry::rhs))); 56 | REQUIRE(rows == std::vector{240}); 57 | } 58 | { 59 | auto rows = storage.select( 60 | union_(select(bitwise_shift_right(&Entry::lhs, &Entry::rhs)), select(c(&Entry::lhs) >> &Entry::rhs))); 61 | REQUIRE(rows == std::vector{15}); 62 | } 63 | { 64 | auto rows = storage.select(union_(select(bitwise_not(&Entry::lhs)), select(~c(&Entry::lhs)))); 65 | REQUIRE(rows == std::vector{-61}); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/generated_column.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Thanks to that nice website https://database.guide/how-to-create-a-computed-column-in-sqlite/ for this example. 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #if SQLITE_VERSION_NUMBER >= 3031000 9 | #define ENABLE_THIS_EXAMPLE 10 | #endif 11 | 12 | #ifdef ENABLE_THIS_EXAMPLE 13 | using namespace sqlite_orm; 14 | using std::cout; 15 | using std::endl; 16 | #endif // ENABLE_THIS_EXAMPLE 17 | 18 | int main() { 19 | #ifdef ENABLE_THIS_EXAMPLE 20 | struct Product { 21 | int id = 0; 22 | std::string name; 23 | int quantity = 0; 24 | float price = 0; 25 | float totalValue = 0; 26 | 27 | #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED 28 | Product() {} 29 | Product(int id, std::string name, int quantity, float price, float totalValue = 0.f) : 30 | id{id}, name{std::move(name)}, quantity{quantity}, price{price}, totalValue{totalValue} {} 31 | #endif 32 | }; 33 | auto storage = make_storage({}, 34 | make_table("products", 35 | make_column("id", &Product::id, primary_key()), 36 | make_column("name", &Product::name), 37 | make_column("quantity", &Product::quantity), 38 | make_column("price", &Product::price), 39 | make_column("total_value", 40 | &Product::totalValue, 41 | generated_always_as(&Product::price * c(&Product::quantity))))); 42 | storage.sync_schema(); 43 | 44 | storage.replace(Product{1, "Hammer", 10, 9.99f}); 45 | storage.replace(Product{2, "Saw", 5, 11.34f}); 46 | storage.replace(Product{3, "Wrench", 7, 37.00f}); 47 | storage.replace(Product{4, "Chisel", 9, 23.00f}); 48 | storage.replace(Product{5, "Bandage", 70, 120.00f}); 49 | 50 | cout << "Products:" << endl; 51 | for (auto& product: storage.iterate()) { 52 | cout << storage.dump(product) << endl; 53 | } 54 | cout << endl; 55 | 56 | // UPDATE products 57 | // SET quantity = 5 WHERE id = 1; 58 | storage.update_all(set(c(&Product::quantity) = 5), where(c(&Product::id) == 1)); 59 | 60 | cout << "Products after update:" << endl; 61 | for (auto& product: storage.iterate()) { 62 | cout << storage.dump(product) << endl; 63 | } 64 | cout << endl; 65 | #endif // ENABLE_THIS_EXAMPLE 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /third_party/amalgamate/README.md: -------------------------------------------------------------------------------- 1 | 2 | # amalgamate.py - Amalgamate C source and header files 3 | 4 | Origin: https://bitbucket.org/erikedlund/amalgamate 5 | 6 | Mirror: https://github.com/edlund/amalgamate 7 | 8 | `amalgamate.py` aims to make it easy to use SQLite-style C source and header 9 | amalgamation in projects. 10 | 11 | For more information, please refer to: http://sqlite.org/amalgamation.html 12 | 13 | ## Here be dragons 14 | 15 | `amalgamate.py` is quite dumb, it only knows the bare minimum about C code 16 | required in order to be able to handle trivial include directives. It can 17 | produce weird results for unexpected code. 18 | 19 | Things to be aware of: 20 | 21 | `amalgamate.py` will not handle complex include directives correctly: 22 | 23 | #define HEADER_PATH "path/to/header.h" 24 | #include HEADER_PATH 25 | 26 | In the above example, `path/to/header.h` will not be included in the 27 | amalgamation (HEADER_PATH is never expanded). 28 | 29 | `amalgamate.py` makes the assumption that each source and header file which 30 | is not empty will end in a new-line character, which is not immediately 31 | preceded by a backslash character (see 5.1.1.2p1.2 of ISO C99). 32 | 33 | `amalgamate.py` should be usable with C++ code, but raw string literals from 34 | C++11 will definitely cause problems: 35 | 36 | R"delimiter(Terrible raw \ data " #include )delimiter" 37 | R"delimiter(Terrible raw \ data " escaping)delimiter" 38 | 39 | In the examples above, `amalgamate.py` will stop parsing the raw string literal 40 | when it encounters the first quotation mark, which will produce unexpected 41 | results. 42 | 43 | ## Installing amalgamate.py 44 | 45 | Python v.2.7.0 or higher is required. 46 | 47 | `amalgamate.py` can be tested and installed using the following commands: 48 | 49 | ./test.sh && sudo -k cp ./amalgamate.py /usr/local/bin/ 50 | 51 | ## Using amalgamate.py 52 | 53 | amalgamate.py [-v] -c path/to/config.json -s path/to/source/dir \ 54 | [-p path/to/prologue.(c|h)] 55 | 56 | * The `-c, --config` option should specify the path to a JSON config file which 57 | lists the source files, include paths and where to write the resulting 58 | amalgamation. Have a look at `test/source.c.json` and `test/include.h.json` 59 | to see two examples. 60 | 61 | * The `-s, --source` option should specify the path to the source directory. 62 | This is useful for supporting separate source and build directories. 63 | 64 | * The `-p, --prologue` option should specify the path to a file which will be 65 | added to the beginning of the amalgamation. It is optional. 66 | 67 | -------------------------------------------------------------------------------- /tests/constraints/check.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("check") { 7 | SECTION("table level") { 8 | struct Contact { 9 | int id = 0; 10 | std::string firstName; 11 | std::string lastName; 12 | std::unique_ptr email; 13 | std::string phone; 14 | }; 15 | auto storage = make_storage({}, 16 | make_table("contacts", 17 | make_column("contact_id", &Contact::id, primary_key()), 18 | make_column("first_name", &Contact::firstName), 19 | make_column("last_name", &Contact::lastName), 20 | make_column("email", &Contact::email), 21 | make_column("phone", &Contact::phone), 22 | check(length(&Contact::phone) >= 10))); 23 | storage.sync_schema(); 24 | } 25 | SECTION("column level") { 26 | struct Book { 27 | int id = 0; 28 | std::string name; 29 | std::string pubName; 30 | int price = 0; 31 | }; 32 | SECTION(">") { 33 | auto storage = make_storage({}, 34 | make_table("BOOK", 35 | make_column("Book_id", &Book::id, primary_key()), 36 | make_column("Book_name", &Book::name), 37 | make_column("Pub_name", &Book::pubName), 38 | make_column("PRICE", &Book::price, check(c(&Book::price) > 0)))); 39 | storage.sync_schema(); 40 | } 41 | SECTION("like") { 42 | auto storage = make_storage({}, 43 | make_table("BOOK", 44 | make_column("Book_id", &Book::id, primary_key()), 45 | make_column("Book_name", &Book::name), 46 | make_column("Pub_name", &Book::pubName), 47 | make_column("PRICE", &Book::price, check(c(&Book::price) > 0)), 48 | check(like(&Book::pubName, "M%")))); 49 | storage.sync_schema(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/having.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Ths example is implemented from here https://www.tutorialspoint.com/sqlite/sqlite_having_clause.htm 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using std::cout; 10 | using std::endl; 11 | 12 | struct Employee { 13 | int id; 14 | std::string name; 15 | int age; 16 | std::string address; 17 | float salary; 18 | }; 19 | 20 | int main() { 21 | 22 | using namespace sqlite_orm; 23 | 24 | auto storage = make_storage("having.sqlite", 25 | make_table("COMPANY", 26 | make_column("ID", &Employee::id, primary_key()), 27 | make_column("NAME", &Employee::name), 28 | make_column("AGE", &Employee::age), 29 | make_column("ADDRESS", &Employee::address), 30 | make_column("SALARY", &Employee::salary))); 31 | storage.sync_schema(); 32 | storage.remove_all(); 33 | 34 | storage.replace(Employee{1, "Paul", 32, "California", 20000.0}); 35 | storage.replace(Employee{2, "Allen", 25, "Texas", 15000.0}); 36 | storage.replace(Employee{3, "Teddy", 23, "Norway", 20000.0}); 37 | storage.replace(Employee{4, "Mark", 25, "Rich-Mond", 65000.0}); 38 | storage.replace(Employee{5, "David", 27, "Texas", 85000.0}); 39 | storage.replace(Employee{6, "Kim", 22, "South-Hall", 45000.0}); 40 | storage.replace(Employee{7, "James", 24, "Houston", 10000.0}); 41 | storage.replace(Employee{8, "Paul", 24, "Houston", 20000.0}); 42 | storage.replace(Employee{9, "James", 44, "Norway", 5000.0}); 43 | storage.replace(Employee{10, "James", 45, "Texas", 5000.0}); 44 | 45 | { 46 | // SELECT * 47 | // FROM COMPANY 48 | // GROUP BY name 49 | // HAVING count(name) < 2; 50 | auto rows = storage.get_all(group_by(&Employee::name).having(less_than(count(&Employee::name), 2))); 51 | for (auto& employee: rows) { 52 | cout << storage.dump(employee) << endl; 53 | } 54 | cout << endl; 55 | } 56 | { 57 | // SELECT * 58 | // FROM COMPANY 59 | // GROUP BY name 60 | // HAVING count(name) > 2; 61 | auto rows = 62 | storage.get_all(group_by(&Employee::name).having(greater_than(count(&Employee::name), 2))); 63 | for (auto& employee: rows) { 64 | cout << storage.dump(employee) << endl; 65 | } 66 | cout << endl; 67 | } 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /dev/table_type_of.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include // std::enable_if, std::is_convertible 4 | 5 | namespace sqlite_orm { 6 | 7 | namespace internal { 8 | 9 | template 10 | struct column_pointer; 11 | 12 | template 13 | struct indexed_column_t; 14 | 15 | /** 16 | * Trait class used to define table mapped type by setter/getter/member 17 | * T - member pointer 18 | * `type` is a type which is mapped. 19 | * E.g. 20 | * - `table_type_of::type` is `User` 21 | * - `table_type_of::type` is `User` 22 | * - `table_type_of::type` is `User` 23 | * - `table_type_of(&User::id))>::type` is `User` 24 | * - `table_type_of*&User::id)>::type` is `User` 25 | */ 26 | template 27 | struct table_type_of; 28 | 29 | template 30 | struct table_type_of { 31 | using type = O; 32 | }; 33 | 34 | template 35 | struct table_type_of> { 36 | using type = T; 37 | }; 38 | 39 | template 40 | struct table_type_of> : table_type_of {}; 41 | 42 | template 43 | using table_type_of_t = typename table_type_of::type; 44 | 45 | /* 46 | * This trait can be used to check whether the object type of a member pointer or column pointer matches the target type. 47 | * 48 | * 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. 49 | * E.g. 50 | * regular: `alias_column>(column(&Base::field))` 51 | * short: `alias_column>(&Base::field)` 52 | */ 53 | template 54 | SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v = false; 55 | 56 | /* 57 | * `true` if a pointer-to-member of Base is convertible to a pointer-to-member of Derived. 58 | */ 59 | template 60 | SQLITE_ORM_INLINE_VAR constexpr bool 61 | is_field_of_v::value>> = true; 62 | 63 | template 64 | SQLITE_ORM_INLINE_VAR constexpr bool is_field_of_v, T, void> = true; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/static_tests/functional/static_if_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | using internal::call_if_constexpr; 6 | using internal::static_if; 7 | 8 | TEST_CASE("static_if") { 9 | using called_pair = std::pair; 10 | called_pair called; 11 | { // simple true 12 | called = {}; 13 | static_if( 14 | [&called] { 15 | ++called.first; 16 | }, 17 | [&called] { 18 | ++called.second; 19 | })(); 20 | REQUIRE(called == called_pair{1, 0}); 21 | } 22 | { // simple false 23 | called = {}; 24 | static_if( 25 | [&called] { 26 | ++called.first; 27 | }, 28 | [&called] { 29 | ++called.second; 30 | })(); 31 | REQUIRE(called == called_pair{0, 1}); 32 | } 33 | { // tuple is empty 34 | called = {}; 35 | static_if>::value>( 36 | [&called] { 37 | ++called.first; 38 | }, 39 | [&called] { 40 | ++called.second; 41 | })(); 42 | REQUIRE(called == called_pair{1, 0}); 43 | } 44 | { // tuple is not empty 45 | called = {}; 46 | static_if>>>( 47 | [&called] { 48 | ++called.first; 49 | }, 50 | [&called] { 51 | ++called.second; 52 | })(); 53 | REQUIRE(called == called_pair{0, 1}); 54 | } 55 | { 56 | struct User { 57 | std::string name; 58 | }; 59 | auto ch = check(length(&User::name) > 5); 60 | STATIC_REQUIRE_FALSE(internal::is_column_v); 61 | called = {}; 62 | static_if>( 63 | [&called] { 64 | ++called.first; 65 | }, 66 | [&called] { 67 | ++called.second; 68 | })(); 69 | REQUIRE(called == called_pair{0, 1}); 70 | } 71 | { // simple true 72 | called = {}; 73 | call_if_constexpr([&called] { 74 | ++called.first; 75 | }); 76 | REQUIRE(called == called_pair{1, 0}); 77 | } 78 | { // simple false 79 | called = {}; 80 | call_if_constexpr([&called] { 81 | ++called.first; 82 | }); 83 | REQUIRE(called == called_pair{0, 0}); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/operators/not_operator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("Not operator") { 7 | struct Object { 8 | int id = 0; 9 | 10 | #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED 11 | Object() = default; 12 | Object(int id) : id{id} {} 13 | #endif 14 | }; 15 | 16 | auto storage = make_storage("", make_table("objects", make_column("id", &Object::id, primary_key()))); 17 | storage.sync_schema(); 18 | 19 | storage.replace(Object{2}); 20 | storage.replace(Object{3}); 21 | 22 | std::vector rows; 23 | std::vector expected; 24 | SECTION("is_equal") { 25 | rows = storage.select(&Object::id, where(not is_equal(&Object::id, 1))); 26 | expected.push_back(2); 27 | expected.push_back(3); 28 | } 29 | SECTION("is_not_equal") { 30 | rows = storage.select(&Object::id, where(not is_not_equal(&Object::id, 3))); 31 | expected.push_back(3); 32 | } 33 | SECTION("greater_than") { 34 | rows = storage.select(&Object::id, where(not greater_than(&Object::id, 2))); 35 | expected.push_back(2); 36 | } 37 | SECTION("greater_or_equal") { 38 | rows = storage.select(&Object::id, where(not greater_or_equal(&Object::id, 3))); 39 | expected.push_back(2); 40 | } 41 | SECTION("less_than") { 42 | rows = storage.select(&Object::id, where(not less_than(&Object::id, 3))); 43 | expected.push_back(3); 44 | } 45 | SECTION("less_or_equal") { 46 | rows = storage.select(&Object::id, where(not less_or_equal(&Object::id, 2))); 47 | expected.push_back(3); 48 | } 49 | SECTION("in") { 50 | rows = storage.select(&Object::id, where(not in(&Object::id, {1, 2}))); 51 | expected.push_back(3); 52 | } 53 | SECTION("is_null") { 54 | rows = storage.select(&Object::id, where(not is_null(&Object::id))); 55 | expected.push_back(2); 56 | expected.push_back(3); 57 | } 58 | SECTION("is_not_null") { 59 | rows = storage.select(&Object::id, where(not is_not_null(&Object::id))); 60 | } 61 | SECTION("like") { 62 | rows = storage.select(&Object::id, where(not like(cast(&Object::id), "2"))); 63 | expected.push_back(3); 64 | } 65 | SECTION("glob") { 66 | rows = storage.select(&Object::id, where(not like(cast(&Object::id), "3"))); 67 | expected.push_back(2); 68 | } 69 | SECTION("exists") { 70 | rows = storage.select(&Object::id, where(not exists(select(&Object::id, where(is_equal(&Object::id, 2)))))); 71 | } 72 | REQUIRE(rows == expected); 73 | } 74 | -------------------------------------------------------------------------------- /examples/cross_join.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The example is implemented from here http://www.sqlitetutorial.net/sqlite-cross-join/ 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using std::cout; 10 | using std::endl; 11 | 12 | namespace DataModel { 13 | 14 | struct Rank { 15 | std::string rank; 16 | }; 17 | 18 | struct Suit { 19 | std::string suit; 20 | }; 21 | } 22 | 23 | static auto initStorage(const std::string& path) { 24 | using namespace DataModel; 25 | using namespace sqlite_orm; 26 | return make_storage(path, 27 | make_table("ranks", make_column("rank", &Rank::rank)), 28 | make_table("suits", make_column("suit", &Suit::suit))); 29 | } 30 | 31 | int main(int, char**) { 32 | using namespace DataModel; 33 | 34 | using Storage = decltype(initStorage("")); 35 | 36 | Storage storage = initStorage("cross_join.sqlite"); 37 | 38 | // sync schema in case db/tables do not exist 39 | storage.sync_schema(); 40 | 41 | // remove old data if something left from after the last time 42 | storage.remove_all(); 43 | storage.remove_all(); 44 | 45 | storage.insert(Rank{"2"}); 46 | storage.insert(Rank{"3"}); 47 | storage.insert(Rank{"4"}); 48 | storage.insert(Rank{"5"}); 49 | storage.insert(Rank{"6"}); 50 | storage.insert(Rank{"7"}); 51 | storage.insert(Rank{"8"}); 52 | storage.insert(Rank{"9"}); 53 | storage.insert(Rank{"10"}); 54 | storage.insert(Rank{"J"}); 55 | storage.insert(Rank{"Q"}); 56 | storage.insert(Rank{"K"}); 57 | storage.insert(Rank{"A"}); 58 | 59 | storage.insert(Suit{"Clubs"}); 60 | storage.insert(Suit{"Diamonds"}); 61 | storage.insert(Suit{"Hearts"}); 62 | storage.insert(Suit{"Spades"}); 63 | 64 | using namespace sqlite_orm; 65 | 66 | /** 67 | * When you pass columns(...) with members sqlite_orm gathers all types and creates 68 | * a std::set with respective table names. Once you passed `cross_join` as an argument 69 | * after columns T's table name is removed from table names set. 70 | */ 71 | // SELECT rank, suit 72 | // FROM ranks 73 | // CROSS JOIN suits 74 | // ORDER BY suit; 75 | auto cards = storage.select(columns(&Rank::rank, &Suit::suit), 76 | cross_join(), 77 | order_by(&Suit::suit)); // cards is vector> 78 | 79 | cout << "cards count = " << cards.size() << endl; 80 | for (auto card: cards) { 81 | cout << std::get<0>(card) << '\t' << std::get<1>(card) << endl; 82 | } 83 | cout << endl; 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /tests/statement_serializer_tests/ast/dynamic_order_by.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace sqlite_orm; 5 | 6 | TEST_CASE("dynamic_order_by") { 7 | using internal::serialize; 8 | 9 | struct User { 10 | uint32_t id; 11 | std::string name; 12 | std::string lastName; 13 | }; 14 | 15 | struct OtherUser { 16 | uint32_t id; 17 | std::string name; 18 | std::string lastName; 19 | }; 20 | 21 | auto table1 = make_table("users", 22 | make_column("id", &User::id), 23 | make_column("name", &User::name), 24 | make_column("last_name", &User::lastName)); 25 | auto table2 = make_table("other_users", 26 | make_column("id", &OtherUser::id), 27 | make_column("name", &OtherUser::name), 28 | make_column("last_name", &OtherUser::lastName)); 29 | 30 | using db_objects_t = internal::db_objects_tuple; 31 | auto dbObjects = db_objects_t{table1, table2}; 32 | using context_t = internal::serializer_context; 33 | context_t context{dbObjects}; 34 | auto storage = make_storage("", table1, table2); 35 | 36 | std::string value; 37 | decltype(value) expected; 38 | 39 | auto orderBySql = dynamic_order_by(storage); 40 | SECTION("users.name") { 41 | orderBySql.push_back(order_by(&User::name).asc()); 42 | expected = R"(ORDER BY "users"."name" ASC)"; 43 | } 44 | SECTION("users.lastName") { 45 | orderBySql.push_back(order_by(&User::lastName).asc()); 46 | expected = R"(ORDER BY "users"."last_name" ASC)"; 47 | } 48 | SECTION("users.both") { 49 | orderBySql.push_back(order_by(&User::name).asc()); 50 | orderBySql.push_back(order_by(&User::lastName).asc()); 51 | expected = R"(ORDER BY "users"."name" ASC, "users"."last_name" ASC)"; 52 | } 53 | SECTION("other_users.name") { 54 | orderBySql.push_back(order_by(&OtherUser::name).asc()); 55 | expected = R"(ORDER BY "other_users"."name" ASC)"; 56 | } 57 | SECTION("other_users.lastName") { 58 | orderBySql.push_back(order_by(&OtherUser::lastName).asc()); 59 | expected = R"(ORDER BY "other_users"."last_name" ASC)"; 60 | } 61 | SECTION("other_users.both") { 62 | orderBySql.push_back(order_by(&OtherUser::name).asc()); 63 | orderBySql.push_back(order_by(&OtherUser::lastName).asc()); 64 | expected = R"(ORDER BY "other_users"."name" ASC, "other_users"."last_name" ASC)"; 65 | } 66 | value = serialize(orderBySql, context); 67 | 68 | REQUIRE(value == expected); 69 | } 70 | -------------------------------------------------------------------------------- /dev/functional/cxx_core_features.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * This header detects core C++ language features on which sqlite_orm depends. 5 | * May be updated/overwritten by cxx_compiler_quirks.h 6 | */ 7 | 8 | #ifdef __has_cpp_attribute 9 | #define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr) 10 | #else 11 | #define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) 0L 12 | #endif 13 | 14 | #ifdef __has_include 15 | #define SQLITE_ORM_HAS_INCLUDE(file) __has_include(file) 16 | #else 17 | #define SQLITE_ORM_HAS_INCLUDE(file) 0L 18 | #endif 19 | 20 | #if __cpp_aggregate_nsdmi >= 201304L 21 | #define SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED 22 | #endif 23 | 24 | #if __cpp_constexpr >= 201304L 25 | #define SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED 26 | #endif 27 | 28 | #if __cpp_noexcept_function_type >= 201510L 29 | #define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED 30 | #endif 31 | 32 | #if __cpp_aggregate_bases >= 201603L 33 | #define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED 34 | #endif 35 | 36 | #if __cpp_fold_expressions >= 201603L 37 | #define SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED 38 | #endif 39 | 40 | #if __cpp_constexpr >= 201603L 41 | #define SQLITE_ORM_CONSTEXPR_LAMBDAS_SUPPORTED 42 | #endif 43 | 44 | #if __cpp_range_based_for >= 201603L 45 | #define SQLITE_ORM_SENTINEL_BASED_FOR_SUPPORTED 46 | #endif 47 | 48 | #if __cpp_if_constexpr >= 201606L 49 | #define SQLITE_ORM_IF_CONSTEXPR_SUPPORTED 50 | #endif 51 | 52 | #if __cpp_inline_variables >= 201606L 53 | #define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED 54 | #endif 55 | 56 | #if __cpp_structured_bindings >= 201606L 57 | #define SQLITE_ORM_STRUCTURED_BINDINGS_SUPPORTED 58 | #endif 59 | 60 | #if __cpp_generic_lambdas >= 201707L 61 | #define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED 62 | #else 63 | #endif 64 | 65 | #if __cpp_init_captures >= 201803L 66 | #define SQLITE_ORM_PACK_EXPANSION_IN_INIT_CAPTURE_SUPPORTED 67 | #endif 68 | 69 | #if __cpp_consteval >= 201811L 70 | #define SQLITE_ORM_CONSTEVAL_SUPPORTED 71 | #endif 72 | 73 | #if __cpp_char8_t >= 201811L 74 | #define SQLITE_ORM_CHAR8T_SUPPORTED 75 | #endif 76 | 77 | #if __cpp_aggregate_paren_init >= 201902L 78 | #define SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED 79 | #endif 80 | 81 | #if __cpp_concepts >= 201907L 82 | #define SQLITE_ORM_CONCEPTS_SUPPORTED 83 | #endif 84 | 85 | #if __cpp_nontype_template_args >= 201911L 86 | #define SQLITE_ORM_CLASSTYPE_TEMPLATE_ARGS_SUPPORTED 87 | #endif 88 | 89 | #if __cpp_nontype_template_args >= 201911L 90 | #define SQLITE_ORM_CLASSTYPE_TEMPLATE_ARGS_SUPPORTED 91 | #endif 92 | 93 | #if __cpp_pack_indexing >= 202311L 94 | #define SQLITE_ORM_PACK_INDEXING_SUPPORTED 95 | #endif 96 | 97 | #if __cplusplus >= 202002L 98 | #define SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED 99 | #endif 100 | -------------------------------------------------------------------------------- /dev/mapped_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include // std::forward, std::move 5 | 6 | #include "row_extractor.h" 7 | #include "mapped_iterator.h" 8 | #include "ast_iterator.h" 9 | #include "prepared_statement.h" 10 | #include "connection_holder.h" 11 | #include "util.h" 12 | 13 | namespace sqlite_orm { 14 | 15 | namespace internal { 16 | 17 | /** 18 | * A C++ view-like class which is returned 19 | * by `storage_t::iterate()` function. This class contains STL functions: 20 | * - size_t size() 21 | * - bool empty() 22 | * - iterator end() 23 | * - iterator begin() 24 | * All these functions are not right const cause all of them may open SQLite connections. 25 | * 26 | * `mapped_view` is also a 'borrowed range', 27 | * meaning that iterators obtained from it are not tied to the lifetime of the view instance. 28 | */ 29 | template 30 | struct mapped_view { 31 | using mapped_type = T; 32 | using storage_type = S; 33 | using db_objects_type = typename S::db_objects_type; 34 | 35 | storage_type& storage; 36 | connection_ref connection; 37 | get_all_t expression; 38 | 39 | mapped_view(storage_type& storage, connection_ref conn, Args&&... args) : 40 | storage(storage), connection(std::move(conn)), expression{{std::forward(args)...}} {} 41 | 42 | size_t size() const { 43 | return this->storage.template count(); 44 | } 45 | 46 | bool empty() const { 47 | return !this->size(); 48 | } 49 | 50 | mapped_iterator begin() { 51 | using context_t = serializer_context; 52 | auto& dbObjects = obtain_db_objects(this->storage); 53 | context_t context{dbObjects}; 54 | context.skip_table_name = false; 55 | context.replace_bindable_with_question = true; 56 | 57 | statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->expression, context))}; 58 | iterate_ast(this->expression.conditions, conditional_binder{stmt.get()}); 59 | return {dbObjects, std::move(stmt)}; 60 | } 61 | 62 | mapped_iterator end() { 63 | return {}; 64 | } 65 | }; 66 | } 67 | } 68 | 69 | #ifdef SQLITE_ORM_CPP20_RANGES_SUPPORTED 70 | template 71 | inline constexpr bool std::ranges::enable_borrowed_range> = true; 72 | #endif 73 | -------------------------------------------------------------------------------- /dev/backup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include // std::system_error 5 | #include // std::string 6 | #include 7 | #include // std::move, std::exchange 8 | 9 | #include "error_code.h" 10 | #include "connection_holder.h" 11 | 12 | namespace sqlite_orm { 13 | 14 | namespace internal { 15 | 16 | /** 17 | * A backup class. Don't construct it as is, call storage.make_backup_from or storage.make_backup_to instead. 18 | * An instance of this class represents a wrapper around sqlite3_backup pointer. Use this class 19 | * to have maximum control on a backup operation. In case you need a single backup in one line you 20 | * can skip creating a backup_t instance and just call storage.backup_from or storage.backup_to function. 21 | */ 22 | struct backup_t { 23 | backup_t(connection_ref to_, 24 | const std::string& zDestName, 25 | connection_ref from_, 26 | const std::string& zSourceName, 27 | std::unique_ptr holder_) : 28 | handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), 29 | holder(std::move(holder_)), to(to_), from(from_) { 30 | if (!this->handle) { 31 | throw std::system_error{orm_error_code::failed_to_init_a_backup}; 32 | } 33 | } 34 | 35 | backup_t(backup_t&& other) : 36 | handle(std::exchange(other.handle, nullptr)), holder(std::move(other.holder)), to(other.to), 37 | from(other.from) {} 38 | 39 | ~backup_t() { 40 | if (this->handle) { 41 | (void)sqlite3_backup_finish(this->handle); 42 | } 43 | } 44 | 45 | /** 46 | * Calls sqlite3_backup_step with pages argument 47 | */ 48 | int step(int pages) { 49 | return sqlite3_backup_step(this->handle, pages); 50 | } 51 | 52 | /** 53 | * Returns sqlite3_backup_remaining result 54 | */ 55 | int remaining() const { 56 | return sqlite3_backup_remaining(this->handle); 57 | } 58 | 59 | /** 60 | * Returns sqlite3_backup_pagecount result 61 | */ 62 | int pagecount() const { 63 | return sqlite3_backup_pagecount(this->handle); 64 | } 65 | 66 | protected: 67 | sqlite3_backup* handle = nullptr; 68 | std::unique_ptr holder; 69 | connection_ref to; 70 | connection_ref from; 71 | }; 72 | } 73 | } 74 | --------------------------------------------------------------------------------