Reimplement journal lib without sqlite & remove sqlite dependency

This commit is contained in:
Nikita Lisitsa 2025-12-02 13:43:30 +03:00
parent 7c398e28ff
commit 48d756bdd5
5 changed files with 52 additions and 153 deletions

View file

@ -23,11 +23,6 @@ if(PSEMEK_USE_FREETYPE)
list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_USE_FREETYPE=1") list(APPEND PSEMEK_DEFINITIONS "-DPSEMEK_USE_FREETYPE=1")
endif() 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) 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")) 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) list(APPEND PSEMEK_CXX_FLAGS -Wall -Werror -Wextra -pedantic -Wno-narrowing -Wno-sign-compare)

View file

@ -1,14 +1,6 @@
file(GLOB_RECURSE PSEMEK_JOURNAL_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp") 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") 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}) 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_include_directories(psemek-journal PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ${SQLite3_INCLUDE_DIRS})
target_link_libraries(psemek-journal PUBLIC psemek-util psemek-log) target_link_libraries(psemek-journal PUBLIC psemek-util psemek-log)
if(PSEMEK_USE_SQLITE)
target_link_libraries(psemek-journal PUBLIC SQLite::SQLite3)
endif()

View file

@ -3,14 +3,12 @@
#include <psemek/journal/event.hpp> #include <psemek/journal/event.hpp>
#include <psemek/util/pimpl.hpp> #include <psemek/util/pimpl.hpp>
#include <filesystem>
namespace psemek::journal namespace psemek::journal
{ {
struct journal struct journal
{ {
journal(std::filesystem::path const & path); journal();
~journal(); ~journal();
bool enabled() const; bool enabled() const;
@ -18,7 +16,7 @@ namespace psemek::journal
void log_event(event const & event); void log_event(event const & event);
std::vector<std::pair<event_metadata const *, event_data>> select(std::string const & query); std::vector<std::pair<event_metadata const *, event_data const *>> select();
private: private:
psemek_declare_pimpl psemek_declare_pimpl

View file

@ -7,7 +7,7 @@
#include <format> #include <format>
#define psemek_journal_log_event_extract_key(key, value) key #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_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, #define psemek_journal_log_event_single_attribute_value(r, data, elem) psemek_journal_log_event_extract_value elem,

View file

@ -1,164 +1,78 @@
#include <psemek/journal/journal.hpp> #include <psemek/journal/journal.hpp>
#include <psemek/log/log.hpp>
#include <psemek/util/exception.hpp>
#include <psemek/util/hash_table.hpp> #include <psemek/util/hash_table.hpp>
#include <format> #include <algorithm>
#include <sstream>
#ifdef PSEMEK_USE_SQLITE
#include <sqlite3.h>
#endif
namespace psemek::journal namespace psemek::journal
{ {
#ifdef PSEMEK_USE_SQLITE
struct journal::impl struct journal::impl
{ {
impl(std::filesystem::path const & path) struct table
{ {
if (sqlite3_open(path.c_str(), &database) != SQLITE_OK) event_metadata metadata;
{ mutable std::vector<event_data> events;
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));
}
}
~impl() struct table_hash
{ {
sqlite3_close(database); std::uint64_t operator()(std::string const & name) const noexcept
} {
return std::hash<std::string>{}(name);
}
std::uint64_t operator()(table const & table) const noexcept
{
return std::hash<std::string>{}(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<table, table_hash, table_equal> tables;
void log_event(event const & event) void log_event(event const & event)
{ {
if (!enabled)
return;
auto table_it = tables.find(event.metadata.name); auto table_it = tables.find(event.metadata.name);
if (table_it == tables.end()) if (table_it == tables.end())
{ table_it = tables.insert(table{event.metadata, {}}).first;
std::ostringstream command; table_it->events.push_back(event.data);
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));
}
}
} }
std::vector<std::pair<event_metadata const *, event_data>> select(std::string const & query) std::vector<std::pair<event_metadata const *, event_data const *>> select()
{ {
std::vector<std::pair<event_metadata const *, event_data>> result; if (!enabled)
return {};
std::vector<std::pair<event_metadata const *, event_data const *>> result;
for (auto const & table : tables) for (auto const & table : tables)
{ for (auto const & event : table.events)
std::string table_query = "SELECT * FROM " + table.first + (query.empty() ? "" : " WHERE ") + query + ";"; result.push_back({&table.metadata, &event});
struct context std::sort(result.begin(), result.end(), [](auto const & e1, auto const & e2){ return e1.second->time < e2.second->time; });
{
std::vector<std::pair<event_metadata const *, event_data>> * 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; });
return result; return result;
} }
sqlite3 * database = nullptr;
bool enabled = true;
util::hash_map<std::string, event_metadata> tables;
}; };
#else journal::journal()
: pimpl_(make_impl())
struct journal::impl
{
impl(std::filesystem::path const &)
{}
void log_event(event const &)
{}
std::vector<std::pair<event_metadata const *, event_data>> select(std::string const &)
{
return {};
}
bool enabled = true;
};
#endif
journal::journal(std::filesystem::path const & path)
: pimpl_(make_impl(path))
{} {}
journal::~journal() = default; journal::~journal() = default;
@ -178,9 +92,9 @@ namespace psemek::journal
impl().log_event(event); impl().log_event(event);
} }
std::vector<std::pair<event_metadata const *, event_data>> journal::select(std::string const & query) std::vector<std::pair<event_metadata const *, event_data const *>> journal::select()
{ {
return impl().select(query); return impl().select();
} }
} }