This commit is contained in:
Your Name
2025-05-03 09:39:08 +12:00
parent 66cfde013c
commit 16754a48d4
2 changed files with 238 additions and 0 deletions

View File

@@ -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

View File

@@ -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