#include "db.h" #include #include #include static sqlite3 *db = NULL; bool db_init(const char *db_path) { if (sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) { fprintf(stderr, "Error opening database: %s\n", sqlite3_errmsg(db)); return false; } // Enable WAL mode for better concurrency char *err_msg = NULL; if (sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, NULL, &err_msg) != SQLITE_OK) { fprintf(stderr, "Failed to set WAL mode: %s\n", err_msg); sqlite3_free(err_msg); // Not fatal } const char *schema_sql = "CREATE TABLE IF NOT EXISTS history (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " command TEXT NOT NULL," " cwd TEXT NOT NULL," " exit_code INTEGER NOT NULL," " duration INTEGER NOT NULL," " hostname TEXT NOT NULL," " username TEXT NOT NULL," " shell TEXT NOT NULL," " session TEXT NOT NULL," " timestamp INTEGER NOT NULL," " created_at DATETIME DEFAULT CURRENT_TIMESTAMP," " updated_at DATETIME DEFAULT CURRENT_TIMESTAMP" ");" "CREATE INDEX IF NOT EXISTS idx_timestamp ON history(timestamp DESC);" "CREATE INDEX IF NOT EXISTS idx_exit_code ON history(exit_code);" "CREATE INDEX IF NOT EXISTS idx_hostname ON history(hostname);" "CREATE INDEX IF NOT EXISTS idx_cwd ON history(cwd);"; if (sqlite3_exec(db, schema_sql, NULL, NULL, &err_msg) != SQLITE_OK) { fprintf(stderr, "Schema creation failed: %s\n", err_msg); sqlite3_free(err_msg); sqlite3_close(db); db = NULL; return false; } return true; } void db_close(void) { if (db) { sqlite3_close(db); db = NULL; } } sqlite3 *db_get_conn(void) { return db; } bool db_add_history(const HistoryEntry *entry) { if (!db) return false; const char *sql = "INSERT INTO history " "(command, cwd, exit_code, duration, hostname, username, shell, session, timestamp) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"; sqlite3_stmt *stmt; if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) { fprintf(stderr, "Prepare failed: %s\n", sqlite3_errmsg(db)); return false; } sqlite3_bind_text(stmt, 1, entry->command, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, entry->cwd, -1, SQLITE_STATIC); sqlite3_bind_int(stmt, 3, entry->exit_code); sqlite3_bind_int(stmt, 4, entry->duration); sqlite3_bind_text(stmt, 5, entry->hostname, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 6, entry->username, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 7, entry->shell, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 8, entry->session, -1, SQLITE_STATIC); sqlite3_bind_int64(stmt, 9, entry->timestamp); int rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { fprintf(stderr, "Insert failed: %s\n", sqlite3_errmsg(db)); return false; } return true; } void db_free_entry(HistoryEntry *entry) { if (!entry) return; free(entry->command); free(entry->cwd); free(entry->hostname); free(entry->username); free(entry->shell); free(entry->session); free(entry); }