#include #ifdef SQLITE3_FOUND #include #include #include #include #include #include #include #include #include Sqlite3Engine::Sqlite3Engine(sqlite3* db): db(db) { } Sqlite3Engine::~Sqlite3Engine() { sqlite3_close(this->db); } std::set Sqlite3Engine::get_all_columns_from_table(const std::string& table_name) { std::set result; char* errmsg; std::string query{"PRAGMA table_info(" + table_name + ")"}; int res = sqlite3_exec(this->db, query.data(), [](void* param, int columns_nb, char** columns, char**) -> int { constexpr int name_column = 1; std::set* result = static_cast*>(param); if (name_column < columns_nb) result->insert(utils::tolower(columns[name_column])); return 0; }, &result, &errmsg); if (res != SQLITE_OK) { log_error("Error executing ", query, ": ", errmsg); sqlite3_free(errmsg); } return result; } std::unique_ptr Sqlite3Engine::open(const std::string& filename) { sqlite3* new_db; const std::string dirname = utils::dirname(filename); std::filesystem::create_directories(dirname); auto res = sqlite3_open_v2(filename.data(), &new_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); if (res != SQLITE_OK) { log_error("Failed to open database file ", filename, ": ", sqlite3_errmsg(new_db)); sqlite3_close(new_db); throw std::runtime_error(""); } std::unique_ptr db = std::make_unique(new_db); // Enable Write-Ahead Logging // https://sqlite.org/wal.html auto sqlite_journal_mode = Config::get("sqlite_journal_mode", "WAL"); if (sqlite_journal_mode != "none") { if (Sqlite3Engine::is_valid_sqlite_journal_mode(sqlite_journal_mode)) { auto [success, err_msg] = db->raw_exec("PRAGMA journal_mode = " + sqlite_journal_mode); if (!success) log_error("Failed enabling SQLite WAL with error: ", err_msg); } else { log_error("Invalid config option sqlite_journal_mode: ", sqlite_journal_mode); } } for (const auto &s : Sqlite3Engine::startup_commands) { auto [success, err_msg] = db->raw_exec(s); if (!success) { log_error("Failed running startup command: ", s, "; with error: ", err_msg); } } return db; } std::tuple Sqlite3Engine::raw_exec(const std::string& query) { #ifdef DEBUG_SQL_QUERIES log_debug("SQL QUERY: ", query); const auto timer = make_sql_timer(); #endif char* error; const auto result = sqlite3_exec(db, query.data(), nullptr, nullptr, &error); if (result != SQLITE_OK) { std::string err_msg(error); sqlite3_free(error); return std::make_tuple(false, err_msg); } return std::make_tuple(true, std::string{}); } std::unique_ptr Sqlite3Engine::prepare(const std::string& query) { sqlite3_stmt* stmt; auto res = sqlite3_prepare(db, query.data(), static_cast(query.size()) + 1, &stmt, nullptr); if (res != SQLITE_OK) { log_error("Error preparing statement: ", sqlite3_errmsg(db)); return nullptr; } return std::make_unique(stmt); } void Sqlite3Engine::extract_last_insert_rowid(Statement&) { this->last_inserted_rowid = sqlite3_last_insert_rowid(this->db); } std::string Sqlite3Engine::id_column_type() { return "INTEGER PRIMARY KEY AUTOINCREMENT"; } DatabaseType Sqlite3Engine::get_type() { return DatabaseType::sqlite3; }; bool Sqlite3Engine::is_valid_sqlite_journal_mode(std::string &sqlite_journal_mode) { // https://sqlite.org/pragma.html#pragma_journal_mode for (const char* s : Sqlite3Engine::valid_sqlite_journal_modes) { if (sqlite_journal_mode == s) return true; } return false; } #endif