From 9d539a4fb273a579f9b3446081fbb0b2a722dc98 Mon Sep 17 00:00:00 2001 From: Yevgeniy Zakharov Date: Tue, 10 Dec 2024 22:41:59 +0500 Subject: [PATCH] added locking mode --- .vscode/launch.json | 29 +++++++ .vscode/settings.json | 78 +++++++++++++++++++ .vscode/tasks.json | 70 +++++++++++++++++ dev/error_code.h | 1 + dev/journal_mode.h | 24 +++--- dev/locking_mode.h | 40 ++++++++++ dev/pragma.h | 35 ++++++--- dev/row_extractor.h | 32 +++++++- include/sqlite_orm/sqlite_orm.h | 133 ++++++++++++++++++++++++++------ tests/pragma_tests.cpp | 17 +++- 10 files changed, 412 insertions(+), 47 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 dev/locking_mode.h diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..810d989b6 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run unit_tests", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/tests/unit_tests", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "environment": [], + "externalConsole": false, + "MIMode": "lldb" + }, + { + "name": "Run amalgamate script", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/third_party/amalgamate/amalgamate.py", + "args": [ + "-c", "${workspaceFolder}/third_party/amalgamate/config.json", + "-s", "${workspaceFolder}" + ], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..1e6e6100f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,78 @@ +{ + "files.associations": { + "*.c": "c", + "string": "cpp", + "__bit_reference": "cpp", + "__hash_table": "cpp", + "__split_buffer": "cpp", + "__tree": "cpp", + "array": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "initializer_list": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "span": "cpp", + "string_view": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "chrono": "cpp", + "format": "cpp", + "ranges": "cpp", + "text_encoding": "cpp", + "__locale": "cpp", + "__node_handle": "cpp", + "__verbose_abort": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "memory": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "locale": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "print": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "tuple": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "variant": "cpp", + "algorithm": "cpp", + "clocale": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "compare": "cpp", + "concepts": "cpp", + "exception": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory_resource": "cpp", + "random": "cpp", + "system_error": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "__config": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..42cee96ea --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,70 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Run unit_tests", + "type": "shell", + "command": "${workspaceFolder}/build/tests/unit_tests", + "options": { + "cwd": "${workspaceFolder}/build/tests" + }, + "group": { + "kind": "test", + "isDefault": true + }, + "problemMatcher": [], + "presentation": { + "reveal": "always", + "panel": "shared" + } + }, + { + "label": "Run amalgamate script", + "type": "shell", + "command": "python3", + "args": [ + "third_party/amalgamate/amalgamate.py", + "-c", + "third_party/amalgamate/config.json", + "-s", + "." + ], + "group": { + "kind": "build", + "isDefault": false + }, + "problemMatcher": [], + "presentation": { + "reveal": "always", + "panel": "shared" + } + }, + { + "label": "Run clang-format", + "type": "shell", + "command": "clang-format", + "args": [ + "-i", + "-style=file", + "include/sqlite_orm/*.h", + "tests/*.cpp", + "tests/*/*.cpp", + "tests/*/*/*.cpp", + "dev/*.h", + "dev/*/*.h", + "tests/*/*.h", + "examples/*.cpp", + "examples/*/src/*.cpp" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "problemMatcher": [], + "presentation": { + "reveal": "always", + "panel": "shared" + } + } + ] +} \ No newline at end of file diff --git a/dev/error_code.h b/dev/error_code.h index 662de285b..58568621d 100644 --- a/dev/error_code.h +++ b/dev/error_code.h @@ -28,6 +28,7 @@ namespace sqlite_orm { cannot_start_a_transaction_within_a_transaction, no_active_transaction, incorrect_journal_mode_string, + incorrect_locking_mode_string, invalid_collate_argument_enum, failed_to_init_a_backup, unknown_member_value, diff --git a/dev/journal_mode.h b/dev/journal_mode.h index b72defda1..5432225b7 100644 --- a/dev/journal_mode.h +++ b/dev/journal_mode.h @@ -7,6 +7,8 @@ #include // std::transform #include // std::toupper +#include "serialize_result_type.h" + #if defined(_WINNT_) // DELETE is a macro defined in the Windows SDK (winnt.h) #pragma push_macro("DELETE") @@ -33,8 +35,8 @@ namespace sqlite_orm { namespace internal { - inline const std::string& to_string(journal_mode j) { - static std::string res[] = { + inline const serialize_result_type& to_string(journal_mode value) { + static const std::array res = { "DELETE", "TRUNCATE", "PERSIST", @@ -42,15 +44,15 @@ namespace sqlite_orm { "WAL", "OFF", }; - return res[static_cast(j)]; + return res.at(static_cast(value)); } - inline std::unique_ptr journal_mode_from_string(const std::string& str) { - std::string upper_str; - std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) { + inline std::pair journal_mode_from_string(const std::string& string) { + std::string upperString; + std::transform(string.begin(), string.end(), std::back_inserter(upperString), [](char c) { return static_cast(std::toupper(static_cast(c))); }); - static std::array all = {{ + static const std::array allValues = {{ journal_mode::DELETE, journal_mode::TRUNCATE, journal_mode::PERSIST, @@ -58,12 +60,12 @@ namespace sqlite_orm { journal_mode::WAL, journal_mode::OFF, }}; - for(auto j: all) { - if(to_string(j) == upper_str) { - return std::make_unique(j); + for(auto journalMode: allValues) { + if(to_string(journalMode) == upperString) { + return {true, journalMode}; } } - return {}; + return {false, journal_mode::OFF}; } } } diff --git a/dev/locking_mode.h b/dev/locking_mode.h new file mode 100644 index 000000000..1f40265a3 --- /dev/null +++ b/dev/locking_mode.h @@ -0,0 +1,40 @@ +#include // std::array +#include // std::string +#include // std::pair +#include // std::back_inserter + +#include "serialize_result_type.h" + +namespace sqlite_orm { + enum class locking_mode : signed char { + NORMAL = 0, + EXCLUSIVE = 1, + }; + + namespace internal { + inline const serialize_result_type& to_string(locking_mode value) { + static const std::array res = { + "NORMAL", + "EXCLUSIVE", + }; + return res.at(static_cast(value)); + } + + inline std::pair locking_mode_from_string(const std::string& string) { + std::string upperString; + std::transform(string.begin(), string.end(), std::back_inserter(upperString), [](char c) { + return static_cast(std::toupper(static_cast(c))); + }); + static const std::array allValues = {{ + locking_mode::NORMAL, + locking_mode::EXCLUSIVE, + }}; + for(auto lockingMode: allValues) { + if(to_string(lockingMode) == upperString) { + return {true, lockingMode}; + } + } + return {false, locking_mode::NORMAL}; + } + } +} \ No newline at end of file diff --git a/dev/pragma.h b/dev/pragma.h index 394af4305..3d608dbb0 100644 --- a/dev/pragma.h +++ b/dev/pragma.h @@ -12,6 +12,7 @@ #include "error_code.h" #include "row_extractor.h" #include "journal_mode.h" +#include "locking_mode.h" #include "connection_holder.h" #include "util.h" #include "serializing_util.h" @@ -63,6 +64,14 @@ namespace sqlite_orm { return this->get_pragma("busy_timeout"); } + sqlite_orm::locking_mode locking_mode() { + return this->get_pragma("locking_mode"); + } + + void locking_mode(sqlite_orm::locking_mode value) { + this->set_pragma("locking_mode", value); + } + sqlite_orm::journal_mode journal_mode() { return this->get_pragma("journal_mode"); } @@ -231,23 +240,29 @@ namespace sqlite_orm { */ template void set_pragma(const std::string& name, const T& value, sqlite3* db = nullptr) { - auto con = this->get_connection(); - if(!db) { - db = con.get(); - } std::stringstream ss; - ss << "PRAGMA " << name << " = " << value << std::flush; - perform_void_exec(db, ss.str()); + ss << "PRAGMA " << name << " = " << value; + this->set_pragma_impl(ss.str(), db); } void set_pragma(const std::string& name, const sqlite_orm::journal_mode& value, sqlite3* db = nullptr) { + std::stringstream ss; + ss << "PRAGMA " << name << " = " << to_string(value); + this->set_pragma_impl(ss.str(), db); + } + + void set_pragma(const std::string& name, const sqlite_orm::locking_mode& value, sqlite3* db = nullptr) { + std::stringstream ss; + ss << "PRAGMA " << name << " = " << to_string(value); + this->set_pragma_impl(ss.str(), db); + } + + void set_pragma_impl(const std::string& query, sqlite3* db = nullptr) { auto con = this->get_connection(); - if(!db) { + if(db == nullptr) { db = con.get(); } - std::stringstream ss; - ss << "PRAGMA " << name << " = " << to_string(value) << std::flush; - perform_void_exec(db, ss.str()); + perform_void_exec(db, query); } }; } diff --git a/dev/row_extractor.h b/dev/row_extractor.h index e18598afc..21fcabf73 100644 --- a/dev/row_extractor.h +++ b/dev/row_extractor.h @@ -25,6 +25,7 @@ #include "arithmetic_tag.h" #include "pointer_value.h" #include "journal_mode.h" +#include "locking_mode.h" #include "error_code.h" #include "is_std_ptr.h" #include "type_traits.h" @@ -404,6 +405,32 @@ namespace sqlite_orm { } }; + /** + * Specialization for locking_mode. + */ + template<> + struct row_extractor { + locking_mode extract(const char* columnText) const { + if(columnText) { + auto resultPair = internal::locking_mode_from_string(columnText); + if(resultPair.first) { + return resultPair.second; + } else { + throw std::system_error{orm_error_code::incorrect_locking_mode_string}; + } + } else { + throw std::system_error{orm_error_code::incorrect_locking_mode_string}; + } + } + + locking_mode extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); + return this->extract(cStr); + } + + locking_mode extract(sqlite3_value* value) const = delete; + }; + /** * Specialization for journal_mode. */ @@ -411,8 +438,9 @@ namespace sqlite_orm { struct row_extractor { journal_mode extract(const char* columnText) const { if(columnText) { - if(auto res = internal::journal_mode_from_string(columnText)) { - return std::move(*res); + auto resultPair = internal::journal_mode_from_string(columnText); + if(resultPair.first) { + return resultPair.second; } else { throw std::system_error{orm_error_code::incorrect_journal_mode_string}; } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 8e9538414..d7d9085fd 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -2852,6 +2852,7 @@ namespace sqlite_orm { cannot_start_a_transaction_within_a_transaction, no_active_transaction, incorrect_journal_mode_string, + incorrect_locking_mode_string, invalid_collate_argument_enum, failed_to_init_a_backup, unknown_member_value, @@ -12618,6 +12619,8 @@ namespace sqlite_orm { #include // std::transform #include // std::toupper +// #include "serialize_result_type.h" + #if defined(_WINNT_) // DELETE is a macro defined in the Windows SDK (winnt.h) #pragma push_macro("DELETE") @@ -12644,8 +12647,8 @@ namespace sqlite_orm { namespace internal { - inline const std::string& to_string(journal_mode j) { - static std::string res[] = { + inline const serialize_result_type& to_string(journal_mode value) { + static const std::array res = { "DELETE", "TRUNCATE", "PERSIST", @@ -12653,15 +12656,15 @@ namespace sqlite_orm { "WAL", "OFF", }; - return res[static_cast(j)]; + return res.at(static_cast(value)); } - inline std::unique_ptr journal_mode_from_string(const std::string& str) { - std::string upper_str; - std::transform(str.begin(), str.end(), std::back_inserter(upper_str), [](char c) { + inline std::pair journal_mode_from_string(const std::string& string) { + std::string upperString; + std::transform(string.begin(), string.end(), std::back_inserter(upperString), [](char c) { return static_cast(std::toupper(static_cast(c))); }); - static std::array all = {{ + static const std::array allValues = {{ journal_mode::DELETE, journal_mode::TRUNCATE, journal_mode::PERSIST, @@ -12669,12 +12672,12 @@ namespace sqlite_orm { journal_mode::WAL, journal_mode::OFF, }}; - for(auto j: all) { - if(to_string(j) == upper_str) { - return std::make_unique(j); + for(auto journalMode: allValues) { + if(to_string(journalMode) == upperString) { + return {true, journalMode}; } } - return {}; + return {false, journal_mode::OFF}; } } } @@ -12722,6 +12725,47 @@ namespace sqlite_orm { // #include "journal_mode.h" +// #include "locking_mode.h" +#include // std::array +#include // std::string +#include // std::pair +#include // std::back_inserter + +// #include "serialize_result_type.h" + +namespace sqlite_orm { + enum class locking_mode : signed char { + NORMAL = 0, + EXCLUSIVE = 1, + }; + + namespace internal { + inline const serialize_result_type& to_string(locking_mode value) { + static const std::array res = { + "NORMAL", + "EXCLUSIVE", + }; + return res.at(static_cast(value)); + } + + inline std::pair locking_mode_from_string(const std::string& string) { + std::string upperString; + std::transform(string.begin(), string.end(), std::back_inserter(upperString), [](char c) { + return static_cast(std::toupper(static_cast(c))); + }); + static const std::array allValues = {{ + locking_mode::NORMAL, + locking_mode::EXCLUSIVE, + }}; + for(auto lockingMode: allValues) { + if(to_string(lockingMode) == upperString) { + return {true, lockingMode}; + } + } + return {false, locking_mode::NORMAL}; + } + } +} // #include "error_code.h" // #include "is_std_ptr.h" @@ -13103,6 +13147,32 @@ namespace sqlite_orm { } }; + /** + * Specialization for locking_mode. + */ + template<> + struct row_extractor { + locking_mode extract(const char* columnText) const { + if(columnText) { + auto resultPair = internal::locking_mode_from_string(columnText); + if(resultPair.first) { + return resultPair.second; + } else { + throw std::system_error{orm_error_code::incorrect_locking_mode_string}; + } + } else { + throw std::system_error{orm_error_code::incorrect_locking_mode_string}; + } + } + + locking_mode extract(sqlite3_stmt* stmt, int columnIndex) const { + auto cStr = (const char*)sqlite3_column_text(stmt, columnIndex); + return this->extract(cStr); + } + + locking_mode extract(sqlite3_value* value) const = delete; + }; + /** * Specialization for journal_mode. */ @@ -13110,8 +13180,9 @@ namespace sqlite_orm { struct row_extractor { journal_mode extract(const char* columnText) const { if(columnText) { - if(auto res = internal::journal_mode_from_string(columnText)) { - return std::move(*res); + auto resultPair = internal::journal_mode_from_string(columnText); + if(resultPair.first) { + return resultPair.second; } else { throw std::system_error{orm_error_code::incorrect_journal_mode_string}; } @@ -15978,6 +16049,8 @@ namespace sqlite_orm { // #include "journal_mode.h" +// #include "locking_mode.h" + // #include "connection_holder.h" // #include "util.h" @@ -16470,6 +16543,14 @@ namespace sqlite_orm { return this->get_pragma("busy_timeout"); } + sqlite_orm::locking_mode locking_mode() { + return this->get_pragma("locking_mode"); + } + + void locking_mode(sqlite_orm::locking_mode value) { + this->set_pragma("locking_mode", value); + } + sqlite_orm::journal_mode journal_mode() { return this->get_pragma("journal_mode"); } @@ -16638,23 +16719,29 @@ namespace sqlite_orm { */ template void set_pragma(const std::string& name, const T& value, sqlite3* db = nullptr) { - auto con = this->get_connection(); - if(!db) { - db = con.get(); - } std::stringstream ss; - ss << "PRAGMA " << name << " = " << value << std::flush; - perform_void_exec(db, ss.str()); + ss << "PRAGMA " << name << " = " << value; + this->set_pragma_impl(ss.str(), db); } void set_pragma(const std::string& name, const sqlite_orm::journal_mode& value, sqlite3* db = nullptr) { + std::stringstream ss; + ss << "PRAGMA " << name << " = " << to_string(value); + this->set_pragma_impl(ss.str(), db); + } + + void set_pragma(const std::string& name, const sqlite_orm::locking_mode& value, sqlite3* db = nullptr) { + std::stringstream ss; + ss << "PRAGMA " << name << " = " << to_string(value); + this->set_pragma_impl(ss.str(), db); + } + + void set_pragma_impl(const std::string& query, sqlite3* db = nullptr) { auto con = this->get_connection(); - if(!db) { + if(db == nullptr) { db = con.get(); } - std::stringstream ss; - ss << "PRAGMA " << name << " = " << to_string(value) << std::flush; - perform_void_exec(db, ss.str()); + perform_void_exec(db, query); } }; } diff --git a/tests/pragma_tests.cpp b/tests/pragma_tests.cpp index 21298643a..21bcd76c8 100644 --- a/tests/pragma_tests.cpp +++ b/tests/pragma_tests.cpp @@ -20,7 +20,22 @@ TEST_CASE("recursive_triggers") { REQUIRE(result); } -TEST_CASE("Journal mode") { +TEST_CASE("locking_mode") { + auto storage = make_storage(""); + + SECTION("EXCLUSIVE") { + storage.pragma.locking_mode(locking_mode::EXCLUSIVE); + const auto result = storage.pragma.locking_mode(); + REQUIRE(result == locking_mode::EXCLUSIVE); + } + SECTION("NORMAL") { + storage.pragma.locking_mode(locking_mode::NORMAL); + const auto result = storage.pragma.locking_mode(); + REQUIRE(result == locking_mode::NORMAL); + } +} + +TEST_CASE("journal_mode") { auto filename = "journal_mode.sqlite"; ::remove(filename); auto storage = make_storage(filename);