From cbfc0b563c66e6ba0bbf7e5cea722027b9ff49b0 Mon Sep 17 00:00:00 2001 From: rawalcher Date: Sun, 29 Dec 2024 17:05:55 +0100 Subject: [PATCH] sqlite database with methods added --- CMakeLists.txt | 36 ++++++++++++++---- includes/database_service.hpp | 31 +++++++-------- src/database_connection.cpp | 71 ++++++++++++++++++++++++++++++----- 3 files changed, 105 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 88b430b..c4a8093 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,4 @@ cmake_minimum_required(VERSION 3.16) -cmake_policy(SET CMP0167 OLD) project(short-link VERSION 1.0 LANGUAGES CXX) @@ -9,17 +8,20 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) set(BUILD_SHARED_LIBS OFF) include_directories(${PROJECT_SOURCE_DIR}/includes) -add_executable(Application src/main.cpp - src/http_connection.cpp - src/http_server.cpp - src/request_handler.cpp + +add_executable(Application + src/main.cpp + src/http_connection.cpp + src/http_server.cpp + src/request_handler.cpp + src/database_connection.cpp ) add_custom_target(copy_frontend ALL - COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/frontend ${CMAKE_BINARY_DIR}/frontend + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/frontend ${CMAKE_BINARY_DIR}/frontend ) -find_package(Boost REQUIRED filesystem system) +find_package(Boost REQUIRED COMPONENTS filesystem system) if(Boost_FOUND) include_directories(${Boost_INCLUDE_DIRS}) @@ -27,4 +29,22 @@ if(Boost_FOUND) target_link_libraries(Application PRIVATE Boost::filesystem Boost::system) else() message(FATAL_ERROR "Boost not found!") -endif() \ No newline at end of file +endif() + +find_package(SOCI REQUIRED sqlite3) + +if(SOCI_FOUND) + include_directories(${SOCI_INCLUDE_DIRS}) + target_link_libraries(Application PRIVATE soci_core soci_sqlite3) +else() + message(FATAL_ERROR "SOCI not found!") +endif() + +find_package(SQLite3 REQUIRED) + +if(SQLite3_FOUND) + include_directories(${SQLite3_INCLUDE_DIRS}) + target_link_libraries(Application PRIVATE SQLite::SQLite3) +else() + message(FATAL_ERROR "SQLite3 not found!") +endif() diff --git a/includes/database_service.hpp b/includes/database_service.hpp index a374260..77845a9 100644 --- a/includes/database_service.hpp +++ b/includes/database_service.hpp @@ -1,35 +1,36 @@ #ifndef DATABASE_SERVICE_HPP #define DATABASE_SERVICE_HPP -#include -#include -#include -#include +#include #include -#include #include +#include +#include +#include +#include class DatabaseService { public: - static DatabaseService& getInstance(boost::asio::io_context& io_context, std::size_t pool_size); - void asyncSetKey(const std::string& key, const std::string& value, std::function callback); - void asyncGetKey(const std::string& key, std::function callback); //we could use a varient here for the result + static DatabaseService& getInstance(const std::string& db_path, std::size_t pool_size); + void shortenURL(const std::string& longURL, std::function callback); + void getLongURL(const std::string& shortCode, std::function callback); -private: - DatabaseService(boost::asio::io_context& io_context, std::size_t pool_size = 4); +private: + DatabaseService(const std::string& db_path, std::size_t pool_size = 4); ~DatabaseService(); DatabaseService(DatabaseService const&); void operator=(DatabaseService const&); - std::shared_ptr getConnection(); + std::shared_ptr getConnection(); + std::string generateShortUUID(); - boost::asio::io_context& io_context_; + std::string db_path_; std::size_t pool_size_; - std::vector> connection_pool_; - std::size_t current_index_ = 0; //index used for round robin selection + std::vector> connection_pool_; + std::size_t current_index_ = 0; // Index used for round-robin selection static DatabaseService* INSTANCE; static std::mutex singleton_mutex; }; -#endif \ No newline at end of file +#endif diff --git a/src/database_connection.cpp b/src/database_connection.cpp index 6ff47b7..0cdd717 100644 --- a/src/database_connection.cpp +++ b/src/database_connection.cpp @@ -1,37 +1,46 @@ #include "../includes/database_service.hpp" -#include -#include -#include +#include +#include +#include +#include #include #include std::mutex DatabaseService::singleton_mutex; DatabaseService* DatabaseService::INSTANCE = nullptr; -DatabaseService& DatabaseService::getInstance(boost::asio::io_context &io_context, std::size_t pool_size) { +DatabaseService& DatabaseService::getInstance(const std::string& db_path, std::size_t pool_size) { std::lock_guard lock(singleton_mutex); if (!INSTANCE) { - INSTANCE = new DatabaseService(io_context, pool_size); + INSTANCE = new DatabaseService(db_path, pool_size); } return *INSTANCE; } -DatabaseService::DatabaseService(boost::asio::io_context& io_context, std::size_t pool_size) : io_context_(io_context), pool_size_(pool_size), current_index_(0) { +DatabaseService::DatabaseService(const std::string& db_path, std::size_t pool_size) + : db_path_(db_path), pool_size_(pool_size), current_index_(0) { if (pool_size_ <= 0) { throw std::invalid_argument("Connection pool size must be greater than zero."); } for (std::size_t i = 0; i < pool_size_; ++i) { - auto conn = std::make_shared(io_context_); - connection_pool_.emplace_back(conn); + auto session = std::make_shared(soci::sqlite3, db_path_); + connection_pool_.emplace_back(session); } + + // Ensure the table exists + auto conn = getConnection(); + *conn << "CREATE TABLE IF NOT EXISTS urls (" + "short_code CHAR(8) PRIMARY KEY, " + "original_url TEXT NOT NULL UNIQUE, " + "created_at INTEGER DEFAULT CURRENT_TIMESTAMP);"; } DatabaseService::~DatabaseService() { connection_pool_.clear(); } -std::shared_ptr DatabaseService::getConnection() { +std::shared_ptr DatabaseService::getConnection() { std::lock_guard lock(singleton_mutex); if (connection_pool_.empty()) { throw std::runtime_error("Connection pool is empty."); @@ -39,5 +48,47 @@ std::shared_ptr DatabaseService::getConnection() { auto conn = connection_pool_[current_index_]; current_index_ = (current_index_ + 1) % pool_size_; - return conn; + return conn; +} + +std::string DatabaseService::generateShortUUID() { + boost::uuids::uuid uuid = boost::uuids::random_generator()(); + std::string uuidStr = to_string(uuid); + return uuidStr.substr(0, 8); +} + +void DatabaseService::shortenURL(const std::string& longURL, std::function callback) { + try { + auto conn = getConnection(); + std::string shortCode; + *conn << "SELECT short_code FROM urls WHERE original_url = :long_url", soci::use(longURL), soci::into(shortCode); + + if (!shortCode.empty()) { + callback({}, shortCode); + return; + } + + shortCode = generateShortUUID(); + *conn << "INSERT INTO urls (short_code, original_url) VALUES (:short_code, :long_url)", + soci::use(shortCode), soci::use(longURL); + callback({}, shortCode); + } catch (const std::exception& e) { + callback(std::make_error_code(std::errc::io_error), ""); + } +} + +void DatabaseService::getLongURL(const std::string& shortCode, std::function callback) { + try { + auto conn = getConnection(); + std::string longURL; + *conn << "SELECT original_url FROM urls WHERE short_code = :short_code", soci::use(shortCode), soci::into(longURL); + + if (!longURL.empty()) { + callback({}, longURL); + } else { + callback(std::make_error_code(std::errc::no_such_file_or_directory), ""); + } + } catch (const std::exception& e) { + callback(std::make_error_code(std::errc::io_error), ""); + } } \ No newline at end of file