diff --git a/CMakeLists.txt b/CMakeLists.txt index faf923b1..e9aa2924 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,11 +23,6 @@ if(PSEMEK_USE_FREETYPE) list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_USE_FREETYPE=1") endif() -option(PSEMEK_USE_SQLITE "Include sqlite3 database support for journaling" OFF) -if(PSEMEK_USE_SQLITE) - list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_USE_SQLITE=1") -endif() - set(PSEMEK_CXX_FLAGS) if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")) list(APPEND PSEMEK_CXX_FLAGS -Wall -Werror -Wextra -pedantic -Wno-narrowing -Wno-sign-compare) diff --git a/libs/journal/CMakeLists.txt b/libs/journal/CMakeLists.txt index cb4e3fd9..33a6feea 100644 --- a/libs/journal/CMakeLists.txt +++ b/libs/journal/CMakeLists.txt @@ -1,14 +1,6 @@ file(GLOB_RECURSE PSEMEK_JOURNAL_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp") file(GLOB_RECURSE PSEMEK_JOURNAL_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp") -if(PSEMEK_USE_SQLITE) - find_package(SQLite3 REQUIRED) -endif() - psemek_add_library(psemek-journal ${PSEMEK_JOURNAL_HEADERS} ${PSEMEK_JOURNAL_SOURCES}) target_include_directories(psemek-journal PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ${SQLite3_INCLUDE_DIRS}) target_link_libraries(psemek-journal PUBLIC psemek-util psemek-log) - -if(PSEMEK_USE_SQLITE) - target_link_libraries(psemek-journal PUBLIC SQLite::SQLite3) -endif() diff --git a/libs/journal/include/psemek/journal/journal.hpp b/libs/journal/include/psemek/journal/journal.hpp index 01c3496b..b81a35da 100644 --- a/libs/journal/include/psemek/journal/journal.hpp +++ b/libs/journal/include/psemek/journal/journal.hpp @@ -3,14 +3,12 @@ #include #include -#include - namespace psemek::journal { struct journal { - journal(std::filesystem::path const & path); + journal(); ~journal(); bool enabled() const; @@ -18,7 +16,7 @@ namespace psemek::journal void log_event(event const & event); - std::vector> select(std::string const & query); + std::vector> select(); private: psemek_declare_pimpl diff --git a/libs/journal/include/psemek/journal/log_event.hpp b/libs/journal/include/psemek/journal/log_event.hpp index 7804f5ee..b1dc8f12 100644 --- a/libs/journal/include/psemek/journal/log_event.hpp +++ b/libs/journal/include/psemek/journal/log_event.hpp @@ -7,7 +7,7 @@ #include #define psemek_journal_log_event_extract_key(key, value) key -#define psemek_journal_log_event_extract_value(key, value) std::format("\"{}\"", value) +#define psemek_journal_log_event_extract_value(key, value) std::format("{}", value) #define psemek_journal_log_event_single_attribute_key(r, data, elem) psemek_journal_log_event_extract_key elem, #define psemek_journal_log_event_single_attribute_value(r, data, elem) psemek_journal_log_event_extract_value elem, diff --git a/libs/journal/source/journal.cpp b/libs/journal/source/journal.cpp index 82f1f4d7..4c252344 100644 --- a/libs/journal/source/journal.cpp +++ b/libs/journal/source/journal.cpp @@ -1,164 +1,78 @@ #include -#include -#include #include -#include -#include - -#ifdef PSEMEK_USE_SQLITE - #include -#endif +#include namespace psemek::journal { -#ifdef PSEMEK_USE_SQLITE - struct journal::impl { - impl(std::filesystem::path const & path) + struct table { - if (sqlite3_open(path.c_str(), &database) != SQLITE_OK) - { - auto error_message = std::format("Failed to open sqlite database at {}: {}", path.c_str(), sqlite3_errmsg(database)); - sqlite3_close(database); - throw util::exception(std::move(error_message)); - } - } + event_metadata metadata; + mutable std::vector events; + }; - ~impl() + struct table_hash { - sqlite3_close(database); - } + std::uint64_t operator()(std::string const & name) const noexcept + { + return std::hash{}(name); + } + + std::uint64_t operator()(table const & table) const noexcept + { + return std::hash{}(table.metadata.name); + } + }; + + struct table_equal + { + bool operator()(std::string const & name1, table const & table2) const noexcept + { + return name1 == table2.metadata.name; + } + + bool operator()(table const & table1, table const & table2) const noexcept + { + return table1.metadata.name == table2.metadata.name; + } + }; + + bool enabled = true; + util::hash_set tables; void log_event(event const & event) { + if (!enabled) + return; + auto table_it = tables.find(event.metadata.name); - if (table_it == tables.end()) - { - std::ostringstream command; - - command << "PRAGMA synchronous = OFF;"; - - command << "CREATE TABLE IF NOT EXISTS " << event.metadata.name << "(timestamp TEXT"; - - table_it = tables.insert({event.metadata.name, event.metadata}).first; - table_it->second.source_file = event.metadata.source_file; - table_it->second.source_line = event.metadata.source_line; - - for (auto const & attribute : event.metadata.columns) - command << ", " << attribute << " TEXT"; - - command << ");"; - - auto command_str = command.str(); - - char * error = nullptr; - if (sqlite3_exec(database, command_str.data(), nullptr, nullptr, &error) != SQLITE_OK) - { - auto error_message = std::string("Failed to create sqlite table: ") + error + std::string("\nSQL command: ") + command_str; - sqlite3_free(error); - throw util::exception(std::move(error_message)); - } - } - - { - std::ostringstream command; - - command << "PRAGMA synchronous = OFF; INSERT INTO " << event.metadata.name << "(timestamp"; - - for (auto const & attribute : event.metadata.columns) - command << ", " << attribute; - - command << ") VALUES (\"" << event.data.time << "\""; - - for (auto const & attribute : event.data.values) - command << ", " << attribute; - - command << ");"; - - auto command_str = command.str(); - - char * error = nullptr; - if (sqlite3_exec(database, command_str.data(), nullptr, nullptr, &error) != SQLITE_OK) - { - auto error_message = std::string("Failed to insert into sqlite table: ") + error + std::string("\nSQL command: ") + command_str; - sqlite3_free(error); - throw util::exception(std::move(error_message)); - } - } + table_it = tables.insert(table{event.metadata, {}}).first; + table_it->events.push_back(event.data); } - std::vector> select(std::string const & query) + std::vector> select() { - std::vector> result; + if (!enabled) + return {}; + + std::vector> result; for (auto const & table : tables) - { - std::string table_query = "SELECT * FROM " + table.first + (query.empty() ? "" : " WHERE ") + query + ";"; + for (auto const & event : table.events) + result.push_back({&table.metadata, &event}); - struct context - { - std::vector> * result; - event_metadata const * metadata; - }; - - context ctx{&result, &table.second}; - - auto callback = [](void * pcontext, int columns, char ** values, char **) -> int - { - auto & ctx = *(context *)pcontext; - - auto & event = ctx.result->emplace_back(std::pair{ctx.metadata, event_data{}}); - - event.second.time = values[0]; - - for (int i = 1; i < columns; ++i) - event.second.values.push_back(values[i]); - - return 0; - }; - - char * error = nullptr; - if (sqlite3_exec(database, table_query.c_str(), callback, &ctx, &error) != SQLITE_OK) - sqlite3_free(error); - } - - std::sort(result.begin(), result.end(), [](auto const & e1, auto const & e2){ return e1.second.time < e2.second.time; }); + std::sort(result.begin(), result.end(), [](auto const & e1, auto const & e2){ return e1.second->time < e2.second->time; }); return result; } - - sqlite3 * database = nullptr; - bool enabled = true; - - util::hash_map tables; }; -#else - - struct journal::impl - { - impl(std::filesystem::path const &) - {} - - void log_event(event const &) - {} - - std::vector> select(std::string const &) - { - return {}; - } - - bool enabled = true; - }; - -#endif - - journal::journal(std::filesystem::path const & path) - : pimpl_(make_impl(path)) + journal::journal() + : pimpl_(make_impl()) {} journal::~journal() = default; @@ -178,9 +92,9 @@ namespace psemek::journal impl().log_event(event); } - std::vector> journal::select(std::string const & query) + std::vector> journal::select() { - return impl().select(query); + return impl().select(); } }