Bug fixing

This commit is contained in:
Your Name
2025-05-25 12:32:06 +12:00
parent 9308f4d719
commit 477d06d3bf
6 changed files with 261 additions and 207 deletions

View File

@@ -1,11 +1,31 @@
#include <stdexcept>
#include <sstream>
#include <set>
#include "database.hpp"
#include "sqlite3/sqlite3.h"
namespace simple_object_storage {
bool Database::createObjectsTable() {
const char* create_table_sql =
"CREATE TABLE IF NOT EXISTS objects ("
"hash TEXT PRIMARY KEY,"
"labels TEXT NOT NULL," // JSON array of labels
"tags TEXT NOT NULL," // JSON array of tags
"metadata TEXT NOT NULL"
");";
char* err_msg = nullptr;
int rc = sqlite3_exec(db_, create_table_sql, nullptr, nullptr, &err_msg);
if (rc != SQLITE_OK) {
std::string error = err_msg;
sqlite3_free(err_msg);
return false;
}
return true;
}
bool Database::createVersionTable() {
const char* sql =
"CREATE TABLE IF NOT EXISTS version_info ("
@@ -80,9 +100,21 @@ bool Database::setVersion(int version) {
}
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;
if (from_version == 1 && to_version == 2) {
// Drop old table
const char* drop_sql = "DROP TABLE IF EXISTS objects;";
char* err_msg = nullptr;
int rc = sqlite3_exec(db_, drop_sql, nullptr, nullptr, &err_msg);
if (rc != SQLITE_OK) {
std::string error = err_msg;
sqlite3_free(err_msg);
return false;
}
// Create new table with updated schema
return createObjectsTable();
}
return false;
}
Database::Database(const std::filesystem::path& path) : path_(path) {
@@ -112,19 +144,8 @@ Database::Database(const std::filesystem::path& path) : path_(path) {
}
// 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);
if (!createObjectsTable()) {
throw std::runtime_error("Failed to create objects table");
}
}
@@ -134,24 +155,6 @@ Database::~Database() {
}
}
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;
@@ -180,50 +183,36 @@ bool Database::remove_by_hash(const std::string& hash) {
return success;
}
bool Database::get(const std::string& label_tag, dbEntry& entry) {
std::string sql = "SELECT hash, metadata FROM objects WHERE label_tag = ?;";
bool Database::get(const std::string& hash, dbEntry& entry) {
std::string sql = "SELECT labels, tags, metadata FROM objects WHERE hash = ?;";
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);
sqlite3_bind_text(stmt, 1, hash.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.hash = hash;
std::string labels_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
std::string tags_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
std::string metadata_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
entry.labels = nlohmann::json::parse(labels_str).get<std::vector<std::string>>();
entry.tags = nlohmann::json::parse(tags_str).get<std::vector<std::string>>();
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;";
std::string sql = "SELECT hash, labels, tags, metadata FROM objects;";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
@@ -233,9 +222,13 @@ bool Database::list(std::vector<dbEntry>& entries) {
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.hash = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
std::string labels_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
std::string tags_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
std::string metadata_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
entry.labels = nlohmann::json::parse(labels_str).get<std::vector<std::string>>();
entry.tags = nlohmann::json::parse(tags_str).get<std::vector<std::string>>();
entry.metadata = nlohmann::json::parse(metadata_str);
entries.push_back(entry);
}
@@ -245,21 +238,69 @@ bool Database::list(std::vector<dbEntry>& entries) {
}
bool Database::update_or_insert(const dbEntry& entry) {
std::string sql = "INSERT OR REPLACE INTO objects (label_tag, hash, metadata) VALUES (?, ?, ?);";
sqlite3_stmt* stmt;
// First try to get existing entry
dbEntry existing;
bool exists = get(entry.hash, existing);
if (sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
return false;
if (exists) {
// Merge labels and tags
std::set<std::string> merged_labels(existing.labels.begin(), existing.labels.end());
merged_labels.insert(entry.labels.begin(), entry.labels.end());
std::set<std::string> merged_tags(existing.tags.begin(), existing.tags.end());
merged_tags.insert(entry.tags.begin(), entry.tags.end());
// Create new entry with merged data
dbEntry merged = entry;
merged.labels = std::vector<std::string>(merged_labels.begin(), merged_labels.end());
merged.tags = std::vector<std::string>(merged_tags.begin(), merged_tags.end());
// Merge metadata
for (const auto& [key, value] : entry.metadata.items()) {
merged.metadata[key] = value;
}
// Update database
std::string sql = "UPDATE objects SET labels = ?, tags = ?, metadata = ? WHERE hash = ?;";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
return false;
}
std::string labels_str = nlohmann::json(merged.labels).dump();
std::string tags_str = nlohmann::json(merged.tags).dump();
std::string metadata_str = merged.metadata.dump();
sqlite3_bind_text(stmt, 1, labels_str.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, tags_str.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, metadata_str.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, merged.hash.c_str(), -1, SQLITE_STATIC);
bool success = sqlite3_step(stmt) == SQLITE_DONE;
sqlite3_finalize(stmt);
return success;
} else {
// Insert new entry
std::string sql = "INSERT INTO objects (hash, labels, tags, metadata) VALUES (?, ?, ?, ?);";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
return false;
}
std::string labels_str = nlohmann::json(entry.labels).dump();
std::string tags_str = nlohmann::json(entry.tags).dump();
std::string metadata_str = entry.metadata.dump();
sqlite3_bind_text(stmt, 1, entry.hash.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, labels_str.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, tags_str.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, metadata_str.c_str(), -1, SQLITE_STATIC);
bool success = sqlite3_step(stmt) == SQLITE_DONE;
sqlite3_finalize(stmt);
return success;
}
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;
}
} // namespace simple_object_storage

View File

@@ -11,22 +11,21 @@ namespace simple_object_storage {
class dbEntry {
public:
std::string label_tag; // unique identifier for the object
std::string hash; // hash of the object - not unique
std::string hash; // unique primary key
std::vector<std::string> labels; // multiple labels
std::vector<std::string> tags; // multiple tags
nlohmann::json metadata;
};
class Database {
public:
static const int CURRENT_VERSION = 1;
static const int CURRENT_VERSION = 2;
Database(const std::filesystem::path& path);
~Database();
bool insert(const dbEntry& entry);
bool remove(const std::string& label_tag);
bool remove(const std::string& hash);
bool remove_by_hash(const std::string& hash);
bool get(const std::string& label_tag, dbEntry& entry);
bool update(const std::string& label_tag, const dbEntry& entry);
bool get(const std::string& hash, dbEntry& entry);
bool list(std::vector<dbEntry>& entries);
bool update_or_insert(const dbEntry& entry);
private:
@@ -37,6 +36,7 @@ class Database {
bool getVersion(int& version);
bool setVersion(int version);
bool migrate(int from_version, int to_version);
bool createObjectsTable();
};
} // namespace simple_object_storage

View File

@@ -67,9 +67,9 @@ void PutHandler::handle_put_object(const httplib::Request& req, httplib::Respons
}
// Validate required metadata fields
if (!metadata.contains("label")) {
if (!metadata.contains("labels") || !metadata["labels"].is_array() || metadata["labels"].empty()) {
res.status = 400;
nlohmann::json response = {{"result", "error"}, {"error", "Missing required metadata field: label"}};
nlohmann::json response = {{"result", "error"}, {"error", "Missing or invalid required metadata field: labels (must be non-empty array)"}};
res.set_content(response.dump(), "application/json");
return;
}
@@ -81,15 +81,6 @@ void PutHandler::handle_put_object(const httplib::Request& req, httplib::Respons
return;
}
// Extract label and tags
std::string label = metadata["label"];
if (label.empty()) {
res.status = 400;
nlohmann::json response = {{"result", "error"}, {"error", "Label cannot be empty"}};
res.set_content(response.dump(), "application/json");
return;
}
// Add filename to metadata if not provided
if (!metadata.contains("filename")) {
metadata["filename"] = file.filename;
@@ -147,6 +138,7 @@ void PutHandler::handle_put_object(const httplib::Request& req, httplib::Respons
// Move file to final location
std::filesystem::path final_path = server_.config_.object_store_path / std::to_string(hash);
if (!std::filesystem::exists(final_path)) {
try {
std::filesystem::rename(temp_path, final_path);
@@ -163,22 +155,11 @@ void PutHandler::handle_put_object(const httplib::Request& req, httplib::Respons
// Update database index
dbEntry entry;
entry.hash = std::to_string(hash);
entry.metadata = metadata; // Store the complete metadata
entry.labels = metadata["labels"].get<std::vector<std::string>>();
entry.tags = metadata["tags"].get<std::vector<std::string>>();
entry.metadata = metadata;
// For each tag, create a label:tag entry
bool success = true;
for (const auto& tag : metadata["tags"]) {
std::string tag_str = tag.get<std::string>();
if (tag_str.empty()) continue; // Skip empty tags
entry.label_tag = label + ":" + tag_str;
if (!server_.db_->update_or_insert(entry)) {
success = false;
break;
}
}
if (!success) {
if (!server_.db_->update_or_insert(entry)) {
res.status = 500;
nlohmann::json response = {{"result", "error"}, {"error", "Failed to update database index"}};
res.set_content(response.dump(), "application/json");