// SPDX-License-Identifier: GPL-3.0-or-later #ifndef ML_DATABASE_H #define ML_DATABASE_H #include "Dimension.h" #include "ml-private.h" #include "json/single_include/nlohmann/json.hpp" namespace ml { class Statement { public: using RowCallback = std::function; public: Statement(const char *RawStmt) : RawStmt(RawStmt), ParsedStmt(nullptr) {} template bool exec(sqlite3 *Conn, RowCallback RowCb, ArgTypes ...Args) { if (!prepare(Conn)) return false; switch (bind(1, Args...)) { case 0: return false; case sizeof...(Args): break; default: return resetAndClear(false); } while (true) { switch (int RC = sqlite3_step(ParsedStmt)) { case SQLITE_BUSY: case SQLITE_LOCKED: usleep(SQLITE_INSERT_DELAY * USEC_PER_MS); continue; case SQLITE_ROW: RowCb(ParsedStmt); continue; case SQLITE_DONE: return resetAndClear(true); default: error("Stepping through '%s' returned rc=%d", RawStmt, RC); return resetAndClear(false); } } } ~Statement() { if (!ParsedStmt) return; int RC = sqlite3_finalize(ParsedStmt); if (RC != SQLITE_OK) error("Could not properly finalize statement (rc=%d)", RC); } private: bool prepare(sqlite3 *Conn); bool bindValue(size_t Pos, const int Value); bool bindValue(size_t Pos, const std::string &Value); template size_t bind(size_t Pos, ArgType T) { return bindValue(Pos, T); } template size_t bind(size_t Pos, ArgType T, ArgTypes ...Args) { return bindValue(Pos, T) + bind(Pos + 1, Args...); } bool resetAndClear(bool Ret); private: const char *RawStmt; sqlite3_stmt *ParsedStmt; }; class Database { private: static const char *SQL_CREATE_ANOMALIES_TABLE; static const char *SQL_INSERT_ANOMALY; static const char *SQL_SELECT_ANOMALY; static const char *SQL_SELECT_ANOMALY_EVENTS; public: Database(const std::string &Path); ~Database(); template bool insertAnomaly(ArgTypes... Args) { Statement::RowCallback RowCb = [](sqlite3_stmt *Stmt) { (void) Stmt; }; return InsertAnomalyStmt.exec(Conn, RowCb, Args...); } template bool getAnomalyInfo(nlohmann::json &Json, ArgTypes&&... Args) { Statement::RowCallback RowCb = [&](sqlite3_stmt *Stmt) { const char *Text = static_cast(sqlite3_column_blob(Stmt, 0)); Json = nlohmann::json::parse(Text); }; return GetAnomalyInfoStmt.exec(Conn, RowCb, Args...); } template bool getAnomaliesInRange(std::vector> &V, ArgTypes&&... Args) { Statement::RowCallback RowCb = [&](sqlite3_stmt *Stmt) { V.push_back({ sqlite3_column_int64(Stmt, 0), sqlite3_column_int64(Stmt, 1) }); }; return GetAnomaliesInRangeStmt.exec(Conn, RowCb, Args...); } private: sqlite3 *Conn; Statement InsertAnomalyStmt{SQL_INSERT_ANOMALY}; Statement GetAnomalyInfoStmt{SQL_SELECT_ANOMALY}; Statement GetAnomaliesInRangeStmt{SQL_SELECT_ANOMALY_EVENTS}; }; } #endif /* ML_DATABASE_H */