Whee!
This commit is contained in:
231
src/database.cpp
231
src/database.cpp
@@ -0,0 +1,231 @@
|
||||
#include "database.hpp"
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
namespace simple_object_storage {
|
||||
|
||||
bool Database::createVersionTable() {
|
||||
const char* sql =
|
||||
"CREATE TABLE IF NOT EXISTS version_info ("
|
||||
"version INTEGER NOT NULL"
|
||||
");";
|
||||
|
||||
char* err_msg = nullptr;
|
||||
int rc = sqlite3_exec(db_, sql, nullptr, nullptr, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
std::string error = err_msg;
|
||||
sqlite3_free(err_msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we need to insert initial version
|
||||
sqlite3_stmt* stmt;
|
||||
sql = "SELECT COUNT(*) FROM version_info;";
|
||||
if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool needs_initial_version = false;
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
needs_initial_version = sqlite3_column_int(stmt, 0) == 0;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (needs_initial_version) {
|
||||
sql = "INSERT INTO version_info (version) VALUES (?);";
|
||||
if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
sqlite3_bind_int(stmt, 1, CURRENT_VERSION);
|
||||
bool success = sqlite3_step(stmt) == SQLITE_DONE;
|
||||
sqlite3_finalize(stmt);
|
||||
return success;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Database::getVersion(int& version) {
|
||||
const char* sql = "SELECT version FROM version_info;";
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sqlite3_step(stmt) != SQLITE_ROW) {
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
version = sqlite3_column_int(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Database::setVersion(int version) {
|
||||
const char* sql = "UPDATE version_info SET version = ?;";
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_bind_int(stmt, 1, version);
|
||||
bool success = sqlite3_step(stmt) == SQLITE_DONE;
|
||||
sqlite3_finalize(stmt);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Database::migrate(int from_version, int to_version) {
|
||||
// Currently only one version, so no migrations needed
|
||||
// This method will be expanded when we need to add new versions
|
||||
return true;
|
||||
}
|
||||
|
||||
Database::Database(const std::filesystem::path& path) : path_(path) {
|
||||
int rc = sqlite3_open(path.string().c_str(), &db_);
|
||||
if (rc != SQLITE_OK) {
|
||||
throw std::runtime_error("Cannot open database: " + std::string(sqlite3_errmsg(db_)));
|
||||
}
|
||||
|
||||
// Create version table and set initial version
|
||||
if (!createVersionTable()) {
|
||||
throw std::runtime_error("Failed to create version table");
|
||||
}
|
||||
|
||||
// Check current version and migrate if needed
|
||||
int current_version;
|
||||
if (!getVersion(current_version)) {
|
||||
throw std::runtime_error("Failed to get database version");
|
||||
}
|
||||
|
||||
if (current_version != CURRENT_VERSION) {
|
||||
if (!migrate(current_version, CURRENT_VERSION)) {
|
||||
throw std::runtime_error("Failed to migrate database");
|
||||
}
|
||||
if (!setVersion(CURRENT_VERSION)) {
|
||||
throw std::runtime_error("Failed to update database version");
|
||||
}
|
||||
}
|
||||
|
||||
// Create objects table if it doesn't exist
|
||||
const char* create_table_sql =
|
||||
"CREATE TABLE IF NOT EXISTS objects ("
|
||||
"label_tag TEXT PRIMARY KEY,"
|
||||
"hash TEXT NOT NULL,"
|
||||
"metadata TEXT NOT NULL"
|
||||
");";
|
||||
|
||||
char* err_msg = nullptr;
|
||||
rc = sqlite3_exec(db_, create_table_sql, nullptr, nullptr, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
std::string error = err_msg;
|
||||
sqlite3_free(err_msg);
|
||||
throw std::runtime_error("Failed to create table: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
Database::~Database() {
|
||||
if (db_) {
|
||||
sqlite3_close(db_);
|
||||
}
|
||||
}
|
||||
|
||||
bool Database::insert(const dbEntry& entry) {
|
||||
std::string sql = "INSERT INTO objects (label_tag, hash, metadata) VALUES (?, ?, ?);";
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
if (sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_bind_text(stmt, 1, entry.label_tag.c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, entry.hash.c_str(), -1, SQLITE_STATIC);
|
||||
std::string metadata_str = entry.metadata.dump();
|
||||
sqlite3_bind_text(stmt, 3, metadata_str.c_str(), -1, SQLITE_STATIC);
|
||||
|
||||
bool success = sqlite3_step(stmt) == SQLITE_DONE;
|
||||
sqlite3_finalize(stmt);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Database::remove(const std::string& label_tag) {
|
||||
std::string sql = "DELETE FROM objects WHERE label_tag = ?;";
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
if (sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_bind_text(stmt, 1, label_tag.c_str(), -1, SQLITE_STATIC);
|
||||
bool success = sqlite3_step(stmt) == SQLITE_DONE;
|
||||
sqlite3_finalize(stmt);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Database::get(const std::string& label_tag, dbEntry& entry) {
|
||||
std::string sql = "SELECT hash, metadata FROM objects WHERE label_tag = ?;";
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
if (sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_bind_text(stmt, 1, label_tag.c_str(), -1, SQLITE_STATIC);
|
||||
|
||||
if (sqlite3_step(stmt) != SQLITE_ROW) {
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
entry.label_tag = label_tag;
|
||||
entry.hash = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
|
||||
std::string metadata_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
|
||||
entry.metadata = nlohmann::json::parse(metadata_str);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Database::update(const std::string& label_tag, const dbEntry& entry) {
|
||||
std::string sql = "UPDATE objects SET hash = ?, metadata = ? WHERE label_tag = ?;";
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
if (sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_bind_text(stmt, 1, entry.hash.c_str(), -1, SQLITE_STATIC);
|
||||
std::string metadata_str = entry.metadata.dump();
|
||||
sqlite3_bind_text(stmt, 2, metadata_str.c_str(), -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 3, label_tag.c_str(), -1, SQLITE_STATIC);
|
||||
|
||||
bool success = sqlite3_step(stmt) == SQLITE_DONE;
|
||||
sqlite3_finalize(stmt);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Database::list(std::vector<dbEntry>& entries) {
|
||||
std::string sql = "SELECT label_tag, hash, metadata FROM objects;";
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
if (sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entries.clear();
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
dbEntry entry;
|
||||
entry.label_tag = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
|
||||
entry.hash = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
|
||||
std::string metadata_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
|
||||
entry.metadata = nlohmann::json::parse(metadata_str);
|
||||
entries.push_back(entry);
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace simple_object_storage
|
||||
|
@@ -17,6 +17,8 @@ class dbEntry {
|
||||
|
||||
class Database {
|
||||
public:
|
||||
static const int CURRENT_VERSION = 1;
|
||||
|
||||
Database(const std::filesystem::path& path);
|
||||
~Database();
|
||||
bool insert(const dbEntry& entry);
|
||||
@@ -27,6 +29,11 @@ class Database {
|
||||
private:
|
||||
std::filesystem::path path_;
|
||||
sqlite3* db_;
|
||||
|
||||
bool createVersionTable();
|
||||
bool getVersion(int& version);
|
||||
bool setVersion(int version);
|
||||
bool migrate(int from_version, int to_version);
|
||||
};
|
||||
|
||||
} // namespace simple_object_storage
|
||||
|
Reference in New Issue
Block a user